PSArtifactory.psm1

using namespace Microsoft.PowerShell.SHiPS

New-Alias -Name ls -Value "Get-ChildItem" -Force

if(Test-Path -Path (Join-Path $HOME ".PSArtifactory")   ){

}else{
    mkdir -p (Join-Path $HOME ".PSArtifactory")
}
Function Clear-AFCredential {
    Remove-Item -Path (Get-AFCredentialPath) -Force
}
function Clear-AFTrashCan {
    Invoke-AFApi -Path '/trash/empty' -Method "POST"
}
function Get-AFChildItem {
    param (

        # Parameter help description
        [Parameter(Mandatory)]
        [String]
        $SourcePath,

        # Parameter help description
        [Parameter(Mandatory)]
        [String]
        $TargetPath,

        # Parameter help description
        [Parameter()]
        [Switch]
        $DryRun,

        # Parameter help description
        [Parameter()]
        [Int]
        $SuppressLayouts=1,

        # Parameter help description
        [Parameter()]
        [Switch]
        $FailFast
    )
    $Path = "/api/copy/$SourcePath?to=/$TargetPath"
    Invoke-AFApi -Path $Path -Method "POST"
}
function Get-AFChildItem {
    param (
        # Parameter help description
        [Parameter()][ValidateNotNullOrEmpty()]
        [String]
        $Repository = (Get-AFContext).Repository,

        # Parameter help description
        [Parameter()]
        [Int32]
        $Depth = 10,

        # Parameter help description
        [Parameter()]
        [switch]
        $IncludeFolders,

        # Parameter help description
        [Parameter()]
        [String]
        $Path
    )
    $listFolders = 0
    if($IncludeFolders){$listFolders = 1}
    if($Path){
        $Uri = "/storage/$($Repository)$($Path)" #?includeRootPath=0&deep=0&depth=$Depth&mdTimestamps=1&listFolders=$listFolders"
    }else{
        $Uri = "/storage/$($Repository)" #?includeRootPath=0&deep=0&depth=$Depth&mdTimestamps=1&listFolders=$listFolders"
    }
    Invoke-AFApi -Path $Uri
    # $Results
    # $Results
}
function Get-AFContext {
    Get-Content -Raw -Path (Get-AFContextPath) | ConvertFrom-Json
}
Function Get-AFFile{
    param(
        # Parameter help description
        [Parameter(Mandatory)]
        [String]
        $AFPath,

        # Parameter help description
        [Parameter(Mandatory)]
        [String]
        $LocalPath
    )
    Invoke-AFRequest -OutFile $LocalPath -Path $AFPath
}
function Get-AFRepository {
    [CmdletBinding(DefaultParameterSetName='Plural')]
    param (
        # Parameter help description
        [Parameter(ParameterSetName='Single')]   
        [String]
        $Key,

        # Parameter help description
        [Parameter(ParameterSetName='Plural')]
        [ValidateSet("local","remote","virtual","distribution")]
        [String]
        $Type
    )
    
    begin {

    }
    
    process {
        
        $Path = "/repositories"

        if($Key){
            $Path += "/$Key"
        }
        if($Type){
            $Path += "?type=$Type"
        }
        (Invoke-AFApi -Path $Path) | % { 
            [PSCustomObject]@{Url = $PSItem.url; Name = $PSItem.key}
            # [Repository]::new($PSItem.key, $PSItem.url)
        }
    }
    
    end {
    }
}


<#
.SYNOPSIS
    Short description
.DESCRIPTION
    Long description
.EXAMPLE
    Example of how to use this cmdlet
.EXAMPLE
    Another example of how to use this cmdlet
.INPUTS
    Inputs to this cmdlet (if any)
.OUTPUTS
    Output from this cmdlet (if any)
.NOTES
    General notes
.COMPONENT
    The component this cmdlet belongs to
.ROLE
    The role this cmdlet belongs to
.FUNCTIONALITY
    The functionality that best describes this cmdlet

function Verb-Noun {
    [CmdletBinding(DefaultParameterSetName='Parameter Set 1',
                   SupportsShouldProcess=$true,
                   PositionalBinding=$false,
                   HelpUri = 'http://www.microsoft.com/',
                   ConfirmImpact='Medium')]
    [Alias()]
    [OutputType([String])]
    Param (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   Position=0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true,
                   ValueFromRemainingArguments=$false,
                   ParameterSetName='Parameter Set 1')]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [ValidateCount(0,5)]
        [ValidateSet("sun", "moon", "earth")]
        [Alias("p1")]
        $Param1,
        
        # Param2 help description
        [Parameter(ParameterSetName='Parameter Set 1')]
        [AllowNull()]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        [ValidateScript({$true})]
        [ValidateRange(0,5)]
        [int]
        $Param2,
        
        # Param3 help description
        [Parameter(ParameterSetName='Another Parameter Set')]
        [ValidatePattern("[a-z]*")]
        [ValidateLength(0,15)]
        [String]
        $Param3
    )
    
    begin {
    }
    
    process {
        if ($pscmdlet.ShouldProcess("Target", "Operation")) {
            
        }
    }
    
    end {
    }
}

#>

Function Get-AFSystemInfo{
    param()
    Invoke-AFApi -Path '/system'
}
function Get-AFToken {
    (Invoke-AFApi -Path '/security/token').tokens
}
Function New-AFToken{
    param(
        # Parameter help description
        [Parameter()]
        [String][Alias("AccessKey","UserName")]
        $Subject = ([Guid]::newGuid().Guid),

        # Parameter help description
        [Parameter()]
        [String]
        $ExpirySec = 3600,

        # Parameter help description
        [Parameter()]
        [switch]
        $Refreshable,

        # Parameter help description
        [Parameter()]
        [string[]]
        $GroupName = @("readers")
    )

    $Body = @{
        username = $Subject
        scope = "member-of-groups:$($GroupName -join ',')"
        expires_in = $ExpirySec
    }

    if($Refreshable) { $Body += @{ refreshable = $true} }
    
    Invoke-AFApi -Path '/security/token' -Method Post -Body $Body -ContentType 'application/x-www-form-urlencoded'
}
Function Ping-AFServer{
    [cmdletbinding()]
    param()
    $TargetUri = (Get-AFContext).APIUri + '/system/ping'
    Write-Verbose "Pinging $TargetUri"

    if((Invoke-WebRequest -Method Get -Uri $TargetUri) -like "ok"){
        Write-Output $true
    }else {
        Write-Output $false
    }
}
Function Register-AFDrive{
    New-PSDrive -name PSArtifactory -psprovider SHiPS -root "PSArtifactory#Root"
}
Function Remove-AFItem {
    param(
        # Parameter help description
        [Parameter()]
        [String]
        $Repository = (Get-AFContext).Repository,

        # Parameter help description
        [Parameter(Mandatory)]
        [String]
        $Uri
    )
    if($Repository -eq $null){
        throw "Reposiotry null. Configure using Set-AFContext -Repository 'RepositoryName' -APIUri ..."
    }
    $Uri = "/$Repository$Uri"
    Invoke-AFRequest -Path $Uri -Method Delete
}
function Revoke-AFToken{
    param(
        # Parameter help description
        [Parameter(Mandatory)]
        [String]
        $Id
    )
    $Body = @{
        token_id = $Id
    }
    Invoke-AFApi -Path '/security/token/revoke' -Method Post -Body $Body
}
function Set-AFContext {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
    param(
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Repository,

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

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]
        $BaseUri
    )

    BEGIN {
        if (-not $PSBoundParameters.ContainsKey('Confirm')) {
            $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference')
        }
        if (-not $PSBoundParameters.ContainsKey('WhatIf')) {
            $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference')
        }
    }

    PROCESS {
        $Prop = @{ 
            APIUri = $APIUri
            Repository = $Repository
            BaseUri = $BaseUri
        }
        
        $Prop | ConvertTo-Json | Out-File -FilePath (Get-AFContextPath)
    }
    END {

    }
}

function Set-AFCredential {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')]
    param(
        [Parameter(
            ParameterSetName='Basic'
        )]
        [ValidateNotNullOrEmpty()]
        [String]
        $UserId,

        [Parameter(
            ParameterSetName='Basic'
        )]
        [ValidateNotNullOrEmpty()]
        [String]
        $Password,

        [Parameter(
            ParameterSetName='AccessToken'
        )]
        [ValidateNotNullOrEmpty()]
        [string]
        $Token,

        [Parameter(
            ParameterSetName='ImportFromDisk'
        )]
        [Switch]
        $ImportFromDisk
    )

    BEGIN {
        if (-not $PSBoundParameters.ContainsKey('Confirm')) {
            $ConfirmPreference = $PSCmdlet.SessionState.PSVariable.GetValue('ConfirmPreference')
        }
        if (-not $PSBoundParameters.ContainsKey('WhatIf')) {
            $WhatIfPreference = $PSCmdlet.SessionState.PSVariable.GetValue('WhatIfPreference')
        }
    }

    PROCESS {
        
        
        switch($PSCmdlet.ParameterSetName){
            "Basic" {
                # $SecPassword = ConvertTo-SecureString $Password -AsPlainText -Force
                $SecPassword = $Password
            }
            "AccessToken" { 
                $UserId = "AccessToken"
                # $SecPassword = ConvertTo-SecureString $Token -AsPlainText -Force
                $SecPassword = $Token
            }
        }
        
        # $PSCred = New-Object System.Management.Automation.PsCredential($UserId,$SecPassword)
        $PSCred = @{
            Username = $UserId
            Password = $SecPassword
        }

        
        New-Object -TypeName PSObject -Property (@{ 
            Credential = $PSCred
            CredentialType = $PSCmdlet.ParameterSetName
        }) | ConvertTo-Json | Out-File -FilePath (Get-AFCredentialPath)
    }
    END {
        
    }
}

Function Get-AFContextPath {
    (Join-Path (Join-Path $HOME ".PSArtifactory") "context")
}
Function Get-AFCredential {
    [cmdletbinding()]
    param()

    # Write-Verbose "The full key is not returned for security!"
    # Write-Host $script:AFCredential.Credential.GetNetworkCredential().Password
    # if(-not $script:AFCredential){
    # throw "No credentials found! Try running Set-AFCredential"
    # }else{
    # Write-Verbose "Found Credentials of type $($script:AFCredential.CredentialType)"
    # }
    if(-not (Test-Path -Path (Get-AFCredentialPath))){
        throw "No credentials found! Set them with Set-AFCredential"
    }
    $Credential = Get-Content -Raw -Path (Get-AFCredentialPath) | ConvertFrom-Json
    
    $SecPassword = ConvertTo-SecureString $Credential.Credential.Password -AsPlainText -Force
    $PSCred = New-Object System.Management.Automation.PsCredential($Credential.Credential.Username, $SecPassword)
    $Credential.Credential = $PSCred
    $Credential
}
Function Get-AFCredentialPath {
    
    (Join-Path (Join-Path $HOME ".PSArtifactory") "credential")

}
function Invoke-AFApi {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Path,

        [Parameter()]
        [ValidateSet('Delete', 'Get', 'Post', 'Put')]
        [string]
        $Method = 'Get',

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [hashtable]
        $Headers = @{},

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Accept,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [HashTable]
        $Body,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $ContentType = 'application/x-www-form-urlencoded',


        [Parameter()]
        [ValidateSet("RepoUri","APIUri")]
        [string]
        $Uri = "APIUri"
    )

    $FullUri = (Get-AFContext).APIUri
    
    $IwrParams = @{
        Uri     = $FullUri + $Path
        Method  = $Method
        ContentType = $ContentType
    }

    
    $Cred = Get-AFCredential
    
    switch ($Cred.CredentialType) {
        "Basic" { 
            Write-Verbose "BASIC AUTH USED"
            $IwrParams += @{ Credential = $Cred.Credential } 
        }
        "AccessToken" {
            Write-Verbose "BEARER AUTH USED"
            $IwrParams += @{ 
                Headers = @{ Bearer = $Cred.Credential.GetNetworkCredential().Password}
            }
        }
    }
    if($Body){
        $IwrParams += @{ Body = $Body}
    }

    # Write-Host ($IwrParams | ConvertTo-Json -Depth 4)
    try{
        Invoke-RestMethod @IwrParams -AllowUnencryptedAuthentication
    }
    catch {
        $_.Exception.Message
    }
    
    
    
}

function Invoke-AFRequest {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Path,

        [Parameter()]
        [ValidateSet('Delete', 'Get', 'Post', 'Put')]
        [string]
        $Method = 'Get',

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [hashtable]
        $Headers = @{},

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Accept,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [HashTable]
        $Body,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $ContentType = 'application/x-www-form-urlencoded',


        [Parameter()]
        [string]
        $Uri,


        [Parameter()]
        [string]
        $OutFile
    )

    $FullUri = (Get-AFContext).BaseUri
    
    $IwrParams = @{
        Uri     = $FullUri + $Path
        Method  = $Method
        ContentType = $ContentType
        AllowUnencryptedAuthentication = $true
    }
    if($Uri){
        $IwrParams.Uri = $Uri
    }

    
    $Cred = Get-AFCredential
    
    switch ($Cred.CredentialType) {
        "Basic" { 
            Write-Verbose "BASIC AUTH USED"
            $IwrParams += @{ Credential = $Cred.Credential } 
        }
        "AccessToken" {
            Write-Verbose "BEARER AUTH USED"
            $IwrParams += @{ 
                Headers = @{ Bearer = $Cred.Credential.GetNetworkCredential().Password}
            }
        }
    }
    if($Body){
        $IwrParams += @{ Body = $Body}
    }
    if($OutFile){
        $IwrParams += @{ OutFile = $OutFile}
    }

    $IwrParams | ConvertTo-Json -Depth 4
    try{
        Invoke-RestMethod @IwrParams
    }
    catch {
        $_.Exception | ConvertTo-Json -Depth 1
    }
    
    
    
}

class RepositoryRoot: SHiPSDirectory
{
    
    RepositoryRoot([String]$Name): base($Name) #Implies Root Folder
    {

    }
    
    [object[]] GetChildItem()
    {
        return (Get-AFRepository) | ForEach-Object { 
            [RepositoryFolder]::new($PSItem.Name,$PSItem.Name)
        }
    }
}



# class Repository : SHiPSDirectory
# {
# [string]$Name
# [string]$Uri

# Repository([string]$Name, [String]$Uri): base($Name)
# {
# $this.Name = $Name
# $this.Uri = $Uri
# }

# [object[]] GetChildItem()
# {
# return (Get-RepoItems)
# }
# }

Function Get-RepoItems {
    foreach($Item in Get-AFChildItem -Path $this.Path){
        foreach($Child in $Item.children){
            # Write-Host "Processing $($PSItem | ConvertTo-Json -Compress)"
            $Name = $Child.uri.split("/")
            $Name = $Name[$Name.Count-1]
            # Write-Host "NAME: $Name"
            if($Child.folder -eq $true){
                [RepositoryFolder]::new($Name, $this.Path + $Child.uri, $this.Repository ,$Item.uri + $Child.uri)
            }else{
                # [RepositoryFolder]::new($Name, $this.Path + $PSItem.uri)
                [RepositoryItem]::new($Name, $this.Path + $Child.uri, $this.Repository, $Item.uri + $Child.uri)
            }
        }
    }
    
    
}


Class RepositoryFolder: SHiPSDirectory
{
    [String]$Path = ""
    [String]$Uri = ""
    [String]$Repository = ""

    RepositoryFolder([String]$Name, [String]$Repository): base($Name) #Implies Root Folder
    {
        $this.Repository = $Repository
    }
    RepositoryFolder([String]$Name, [String]$Path, [String]$Repository,[String]$Uri): base($Name) #Implies Root Folder
    {
        $this.Path = $Path
        $this.Uri = $Uri
        $this.Repository = $Repository
    }
    [object[]] GetChildItem()
    {
        return (Get-RepoItems)
    }
    
}

class RepositoryItem : SHiPSLeaf {
    
    [String]$Key
    [String]$Path
    [String]$Uri
    [String]$Repository = ""

    RepositoryItem([String]$Key,[String]$Path, [String]$Repository, [String]$Uri):base($Key)
    {
        $this.Key = $Key
        $this.Path = $Path
        $this.Uri = $Uri
        $this.Repository = $Repository
    }
    [bool] RemoveItem(){
        Write-Host "Removing Item: $($this.Uri)"
        return true
    }
}

class Root : SHiPSDirectory
{
    Root([string]$Name): base($Name)
    {
        $Credential = Get-AFCredential
        $Context = Get-AFContext
    }

    [object[]] GetChildItem()
    {
        return @(
            [RepositoryRoot]::new("Repositories"),
            [RepositoryRoot]::new("Users")
        )
    }
}


# class Root : SHiPSDirectory
# {
# Root([string]$Name): base($Name){
        
# }

# [object[]] GetChildItem()
# {
# return @(Get-RootFolders)
# }
# }
# Function Get-RootFolders(){
# $RootFolders.Values
# }
# $Script:RootFolders = @(
# Repositories = [Repositories]::new(),
# Users = [Repositories]::new()
# )