PSLeankit-Card.psm1

<#
.SYNOPSIS
    Returns a new LeanKit card hashtable but does not add it to a board
#>


function New-LeanKitCard{
    [CmdletBinding(DefaultParameterSetName='Profile')]
    [OutputType([array])]
    param(
        
        # ID of the lane to add the card to
        [parameter(mandatory=$true)]
        [int]$LaneID,

        # Title of the card
        [parameter(mandatory=$true)]
        [string]$Title,
        
        # Description of the card
        [parameter(mandatory=$true)]
        [string]$Description,

        # Identity of the type of card to be created
        [parameter(mandatory=$true)]
        [alias("CardTypeID")]
        [int]$TypeID,

        # Numeric priority of card
        [parameter(mandatory=$false)]
        [ValidateRange(0,3)]
        [int]$Priority=0,

        # Whether the card action is 'blocked'
        [parameter(mandatory=$false)]
        [boolean]$IsBlocked=$false,

        # The reason the card action is blocked!
        [parameter(mandatory=$false)]
        [string]$BlockReason=$null,

        # Card's location in the lane?
        [parameter(mandatory=$false)]
        [int]$Index=0,

        # Time the card's action will start
        [parameter(mandatory=$false)]
        [datetime]$StartDate=0,

        # Time the card's action will be due
        [parameter(mandatory=$false)]
        [datetime]$DueDate=0,

        # The name of the external system which the ticket is referencing with "ExternalCardID
        [parameter(mandatory=$false)]
        [string]$ExternalSystemName=0,

        # The url of the external system which the ticket is referencing with "ExternalCardID
        [parameter(mandatory=$false)]
        [string]$ExternalSystemUrl=0,

        # Comma seperated string of tags
        [parameter(mandatory=$false)]
        [string]$Tags=0,

        # ID of the class of service to be assigned to this card
        [parameter(mandatory=$false)]
        [int]$ClassOfServiceID=$null,

        # The ID of an external reference (e.g. a ticket) for this card
        [parameter(mandatory=$false)]
        [string]$ExternalCardID,
        
        # Array of user IDs assigned to this card
        [parameter(mandatory=$false)]
        [int[]]$AssignedUserIDs=@()
    )
    $private:LeanKitCard = @{}

    $private:ValidLeanKitCardProperties = @('LaneID','Title','Description','TypeID','Priority','IsBlocked','BlockReason','Index',
        'StartDate','DueDate','ExternalSystemName','ExternalSystemUrl','Tags','ClassOfServiceID','ExternalCardID','AssignedUserIDs')

    foreach($private:LeanKitCardProperty in $PsBoundParameters.Keys | ?{$private:ValidLeanKitCardProperties -contains $_}){
        $private:LeanKitCard.$private:LeanKitCardProperty = $PsBoundParameters.$private:LeanKitCardProperty
    }

    return $private:LeanKitCard
}

<#
.SYNOPSIS
    Adds a leankit card to a board
#>

function Add-LeanKitCard{
    [CmdletBinding(DefaultParameterSetName='Profile')]
    [OutputType([array])]
    param(
        # URL of the leankit account
        [Parameter(ParameterSetName='Credential')]
        [string]$URL,
        
        # PSCredentialsObject with the username and password needed to auth against leankit
        [Parameter(ParameterSetName='Credential')]
        [alias('credentials')]
        [pscredential]$credential,
        
        # Name of the profile to load
        [Parameter(ParameterSetName='Profile')]
        [string]$ProfileName,

        # ID of the board in which to update the card
        [parameter(mandatory=$true)]
        [int]$BoardID,

        # ID of the lane to update the card to
        [parameter(mandatory=$false)]
        [int]$LaneID,

        # Title of the card
        [parameter(mandatory=$false)]
        [string]$Title,
        
        # Description of the card
        [parameter(mandatory=$false)]
        [string]$Description,

        # Identity to which to update the card
        [parameter(mandatory=$false)]
        [alias("CardTypeID")]
        [int]$TypeID,

        # Numeric priority to which to update the card
        [parameter(mandatory=$false)]
        [int]$Priority,

        # Whether the card action is 'blocked'
        [parameter(mandatory=$false)]
        [boolean]$IsBlocked,

        # The reason the card action is blocked!
        [parameter(mandatory=$false)]
        [string]$BlockReason,

        # Card's location in the lane?
        [parameter(mandatory=$false)]
        [int]$Index=0,

        # Time the card's action will start
        [parameter(mandatory=$false)]
        $StartDate,

        # Time the card's action will be due
        [parameter(mandatory=$false)]
        $DueDate,

        # The name of the external system which the ticket is referencing with "ExternalCardID"
        [parameter(mandatory=$false)]
        [string]$ExternalSystemName,

        # The url of the external system which the ticket is referencing with "ExternalCardID"
        [parameter(mandatory=$false)]
        [string]$ExternalSystemUrl,

        # Comma seperated string of tags
        [parameter(mandatory=$false)]
        [string]$Tags,

        # ID of the class of service to be assigned to this card
        [parameter(mandatory=$false)]
        [int]$ClassOfServiceID=$null,

        # The ID of an external reference (e.g. a ticket) for this card
        [parameter(mandatory=$false)]
        [string]$ExternalCardID,
        
        # Array of user IDs assigned to this card
        [parameter(mandatory=$false)]
        [int[]]$AssignedUserIDs,

        # A comment to be added in case we're overriding the lane's Work in Process limit
        [parameter(mandatory=$false)]
        [string]$WipOverrideComment="Created programatically by PSLeanKit"
    )

    $private:Params = @{}

    $private:ValidLeanKitCardProperties = @('LaneID','Title','Description','TypeID','Priority','IsBlocked','BlockReason','Index',
        'StartDate','DueDate','ExternalSystemName','ExternalSystemUrl','Tags','ClassOfServiceID','ExternalCardID','AssignedUserIDs')

    foreach($private:LeanKitCardProperty in $PsBoundParameters.Keys | ?{$private:ValidLeanKitCardProperties -contains $_}){
        $private:Params.$private:LeanKitCardProperty = $PsBoundParameters.$private:LeanKitCardProperty
    }

    $private:Card = New-LeanKitCard @private:params
        

    # Pass any common parameters on to the superordinate cmdlet
    $private:Params = Merge-LeanKitProfileDataWithExplicitParams -ExplicitParams $PsBoundParameters
    if($ProfileName){$private:Params.ProfileName = $ProfileName}
    $global:AddLeanKitCardParams = $private:params
    $private:Params.boardID = $private:BoardID 
    $private:Params.Cards = @($private:Card)
    $private:Params.WipOverrideComment = $private:WipOverrideComment
    return Add-LeanKitCards @private:params
}

function Add-LeanKitCards{
    [CmdletBinding(DefaultParameterSetName='Profile')]
    [OutputType([array])]
    param(
        # URL of the leankit account
        [Parameter(ParameterSetName='Credential')]
        [string]$URL,
        
        # PSCredentialsObject with the username and password needed to auth against leankit
        [Parameter(ParameterSetName='Credential')]
        [alias('credentials')]
        [pscredential]$credential,
        
        # Name of the profile to load
        [Parameter(ParameterSetName='Profile')]
        [string]$ProfileName,

        # ID of the board
        [parameter(mandatory=$true)]
        [int]$boardID,

        # A reason for overridding the WorkInProgress Limit
        [parameter(mandatory=$false)]
        [string]$WipOverrideComment='',

        # Array of card hashtables created from New-LeanKitCard
        [parameter(mandatory=$true)]
        [ValidateScript({
            if($_.length -gt 100){
                #"You cannot pass greater than 100 cards at a time to add-LeanKitCards"
                return $false;
            }
            return $true;
        })]
        [hashtable[]]$cards
    )

    # Try and get defaults and break out of the function with a null value if we can't
    $private:LeanKitProfile = Merge-LeanKitProfileDataWithExplicitParams -ProfileData $(Get-LeanKitProfile -ProfileName $ProfileName) -ExplicitParams $PsBoundParameters -ErrorOnIncompleteResultantData -ErrorAction Stop

    # Loop through and convert each card's date to the correct format
    $Private:LeanKitDateFormat = Get-LeanKitDateFormat @private:LeanKitProfile
    
    foreach($private:Card in $private:Cards){
        
        $private:Card.StartDate = [string]$(if($private:Card.StartDate){(Get-Date $private:Card.StartDate -format $Private:LeanKitDateFormat)}else{""})
        $private:Card.DueDate = [string]$(if($private:Card.DueDate ){Get-Date $private:Card.DueDate  -format $Private:LeanKitDateFormat}else{""})
    }
    
    [string]$uri = $private:LeanKitProfile.URL + "/Kanban/Api/Board/$boardID/AddCards?wipOverrideComment=$WipOverrideComment"
    $private:result = Invoke-RestMethod -Uri $uri -Credential $private:LeanKitProfile.Credential -Method Post -Body $(ConvertTo-Json $cards ) -ContentType "application/json"
    if($private:result.ReplyCode -ne 201){
        Write-Warning "Failed to add cards `r`n`t$($private:result.ReplyCode) - $($private:result.ReplyText)";
        return $private:result
    }else{
        return $private:result.ReplyData
    }

    

}

function Get-LeanKitCard {
    [CmdletBinding(DefaultParameterSetName='Profile')]
    [OutputType([array])]
    param(
        # URL of the leankit account
        [Parameter(ParameterSetName='Credential')]
        [string]$URL,
        
        # PSCredentialsObject with the username and password needed to auth against leankit
        [Parameter(ParameterSetName='Credential')]
        [alias('credentials')]
        [pscredential]$credential,
        
        # Name of the profile to load
        [Parameter(ParameterSetName='Profile')]
        [string]$ProfileName,

        # ID of the board in which the card we're getting resides
        [parameter(mandatory=$true)]
        [int]$boardID,

        # ID of the card we're getting
        [parameter(mandatory=$true)]
        [int]$CardID
    )

    # Check for a default profile and merge the explicit creds/url with it
    $private:LeanKitProfile = Merge-LeanKitProfileDataWithExplicitParams -ProfileData $(Get-LeanKitProfile -ProfileName $ProfileName) -ExplicitParams $PsBoundParameters -ErrorOnIncompleteResultantData -ErrorAction Stop

    [string]$private:uri = $private:LeanKitProfile.URL + "/Kanban/Api/Board/$private:boardID/GetCard/$private:CardID"
    return $(Invoke-RestMethod -Uri $private:uri  -Credential $private:LeanKitProfile.Credential).ReplyData
}

function Update-LeanKitCard{
    [CmdletBinding(DefaultParameterSetName='Profile')]
    [OutputType([array])]
    param(
        # URL of the leankit account
        [Parameter(ParameterSetName='Credential')]
        [string]$URL,
        
        # PSCredentialsObject with the username and password needed to auth against leankit
        [Parameter(ParameterSetName='Credential')]
        [alias('credentials')]
        [pscredential]$credential,
        
        # Name of the profile to load
        [Parameter(ParameterSetName='Profile')]
        [string]$ProfileName,

        # ID of the board in which to update the card
        [parameter(mandatory=$true)]
        [int]$BoardID,

         # ID of the card to update
        [parameter(mandatory=$true)]
        [alias('CardID')]
        [int]$ID,

        # ID of the lane to update the card to
        [parameter(mandatory=$false)]
        [int]$LaneID,

        # Title of the card
        [parameter(mandatory=$false)]
        [string]$Title,
        
        # Description of the card
        [parameter(mandatory=$false)]
        [string]$Description,

        # Identity to which to update the card
        [parameter(mandatory=$false)]
        [alias("CardTypeID")]
        [int]$TypeID,

        # Numeric priority to which to update the card
        [parameter(mandatory=$false)]
        [ValidateRange(0,3)]
        [int]$Priority,

        # Whether the card action is 'blocked'
        [parameter(mandatory=$false)]
        [boolean]$IsBlocked,

        # The reason the card action is blocked!
        [parameter(mandatory=$false)]
        [string]$BlockReason,

        # Card's location in the lane?
        [parameter(mandatory=$false)]
        [int]$Index=0,

        # Time the card's action will start
        [parameter(mandatory=$false)]
        $StartDate,

        # Time the card's action will be due
        [parameter(mandatory=$false)]
        $DueDate,

        # The name of the external system which the ticket is referencing with "ExternalCardID"
        [parameter(mandatory=$false)]
        [string]$ExternalSystemName,

        # The url of the external system which the ticket is referencing with "ExternalCardID"
        [parameter(mandatory=$false)]
        [string]$ExternalSystemUrl,

        # Comma seperated string of tags
        [parameter(mandatory=$false)]
        [string]$Tags,

        # ID of the class of service to be assigned to this card
        [parameter(mandatory=$false)]
        [int]$ClassOfServiceID=$null,

        # The ID of an external reference (e.g. a ticket) for this card
        [parameter(mandatory=$false)]
        [string]$ExternalCardID,
        
        # Array of user IDs assigned to this card
        [parameter(mandatory=$false)]
        [int[]]$AssignedUserIDs,

        # A comment to be added in case we're overriding the lane's Work in Process limit
        [parameter(mandatory=$false)]
        [string]$WipOverrideComment="Created programatically by PSLeanKit"
    )
   
    # Pass any common parameters on to the superordinate cmdlet
    $private:LeanKitProfile = Merge-LeanKitProfileDataWithExplicitParams -ExplicitParams $PsBoundParameters
    if($ProfileName){$private:LeanKitProfile.ProfileName = $ProfileName}
    
    # Fetch the card and pipe it's existing values into a hashtable for manipulation
    $private:CardHashTable = @{};
    $private:Params = $private:LeanKitProfile.clone()
    $private:Params.boardID = $private:BoardID 
    $private:Params.CardID = $private:Id
    $private:Card = Get-LeanKitCard @private:Params
    $private:Card | Get-Member | ?{$_.MemberType -eq "NoteProperty"} | %{$private:CardHashTable.add($_.name, $private:Card.$($_.name))}

    # Loop through our params (those that are set) and ensure the hashtable reflects the values we've updated
    foreach($private:key in $private:CardHashTable.Keys.Clone()){

        if(([array]$PSBoundParameters.keys) -Contains($private:key)){
            $private:CardHashTable.$private:key = (Get-Variable  -Scope Private -Name $private:key).Value
        }
    }

    $private:Params = $private:LeanKitProfile.clone();
    $private:Params.BoardID = $private:BoardID 
    $private:Params.Cards = @($private:CardHashTable) 
    $private:Params.WipOverrideComment = $private:WipOverrideComment
    return Update-LeanKitCards @private:Params
}

function Update-LeanKitCards{
    [CmdletBinding(DefaultParameterSetName='Profile')]
    [OutputType([array])]
    param(
        # URL of the leankit account
        [Parameter(ParameterSetName='Credential')]
        [string]$URL,
        
        # PSCredentialsObject with the username and password needed to auth against leankit
        [Parameter(ParameterSetName='Credential')]
        [alias('credentials')]
        [pscredential]$credential,
        
        # Name of the profile to load
        [Parameter(ParameterSetName='Profile')]
        [string]$ProfileName,

        # ID of the board the card resides in
        [parameter(mandatory=$true)]
        [int]$BoardID,

        [parameter(mandatory=$true)]
        [ValidateScript({
            if($_.length -gt 100){
                # You cannot pass greater than 100 cards at a time to Update-LeanKitCards
                return $false;
            }
            if(
                ($_ |?{$_.ID}).length -lt $_.length
               ){
                # All cards must have an ID when passing to Update-LeanKitCards;
                return $false;
            }
            return $true;
        })]
        [hashtable[]]$Cards,

        # A message to provide in case we override a lane's Work in Process limit
        [parameter(mandatory=$false)]
        [string]$WipOverrideComment="Updated by PSLeankit automatically"
    )

    # Check for a default profile and merge the explicit creds/url with it
    $private:LeanKitProfile = Merge-LeanKitProfileDataWithExplicitParams -ProfileData $(Get-LeanKitProfile -ProfileName $ProfileName) -ExplicitParams $PsBoundParameters -ErrorOnIncompleteResultantData -ErrorAction Stop

    # Loop through and convert each card's date to the correct format
    $Private:LeanKitDateFormat = Get-LeanKitDateFormat @private:LeanKitProfile
    
    foreach($private:Card in $private:Cards){
        
        $private:Card.StartDate = [string]$(if($private:Card.StartDate){(Get-Date $private:Card.StartDate -format $Private:LeanKitDateFormat)}else{""})
        $private:Card.DueDate = [string]$(if($private:Card.DueDate ){Get-Date $private:Card.DueDate  -format $Private:LeanKitDateFormat}else{""})
    }

    # Format the URL and submit the request
    [string]$private:uri = $private:LeanKitProfile.URL + "/Kanban/Api/Board/$private:boardID/UpdateCards?wipOverrideComment=$private:WipOverrideComment"
    $private:result = Invoke-RestMethod -Uri $private:uri -Credential $private:LeanKitProfile.Credential -Method Post -Body $(ConvertTo-Json $private:cards) -ContentType "application/json" 
    
    # Check the request succeeded
    if($private:result.ReplyCode -ne 201){
        Write-Warning "Failed to update cards `r`n`t$($private:result.ReplyCode) - $($private:result.ReplyText)";
        return $private:result
    }else{
        return @($private:result).ReplyData
    }

}

function Remove-LeanKitCard {
    [CmdletBinding(DefaultParameterSetName='Profile')]
    [OutputType([array])]
    param(
        # URL of the leankit account
        [Parameter(ParameterSetName='Credential')]
        [string]$URL,
        
        # PSCredentialsObject with the username and password needed to auth against leankit
        [Parameter(ParameterSetName='Credential')]
        [alias('credentials')]
        [pscredential]$credential,
        
        # Name of the profile to load
        [Parameter(ParameterSetName='Profile')]
        [string]$ProfileName,

        # ID of the board in which the card we're deleting resides
        [parameter(mandatory=$true)]
        [int]$BoardID,

        # ID of the card we're deleting
        [parameter(mandatory=$true)]
        [int]$CardID
    )

    # Pass any common parameters on to the superordinate cmdlet
    $private:Params = Merge-LeanKitProfileDataWithExplicitParams -ExplicitParams $PsBoundParameters
    if($ProfileName){$private:Params.ProfileName = $ProfileName}

    $private:Params.BoardID = $private:BoardID 
    $private:Params.CardIDs = @($private:CardID)

    return Remove-LeanKitCards @private:Params
}

function Remove-LeanKitCards {
    [CmdletBinding(DefaultParameterSetName='Profile')]
    [OutputType([array])]
    param(
         # URL of the leankit account
        [Parameter(ParameterSetName='Credential')]
        [string]$URL,
        
        # PSCredentialsObject with the username and password needed to auth against leankit
        [Parameter(ParameterSetName='Credential')]
        [alias('credentials')]
        [pscredential]$credential,
        
        # Name of the profile to load
        [Parameter(ParameterSetName='Profile')]
        [string]$ProfileName,

        # ID of the board in which the cards we're deleting reside
        [parameter(mandatory=$true)]
        [int]$BoardID,

        # Array of card IDs to delete
        [parameter(mandatory=$true)]
        [int[]]$CardIDs
    )

    # Check for a default profile and merge the explicit creds/url with it
    $private:LeanKitProfile = Merge-LeanKitProfileDataWithExplicitParams -ProfileData $(Get-LeanKitProfile -ProfileName $ProfileName) -ExplicitParams $PsBoundParameters -ErrorOnIncompleteResultantData -ErrorAction Stop

    [string]$uri = $private:LeanKitProfile.URL + "/Kanban/Api/Board/$private:boardID/DeleteCards/"
    $result = Invoke-RestMethod -Uri $uri  -Credential $private:LeanKitProfile.Credential -Method Post -Body $(ConvertTo-Json $private:CardIDs) -ContentType "application/json" 
    return $result.ReplyData
}