SpiceWorld2016_PowerShellUnplugged.ps1


#region Step 1 The Challenge
# someone said it was easy to use BASH to get info from Github
# curl https://api.github.com/repos/PowerShell/PowerShell/releases| grep download_count releases | awk '{ print $2 }' | sed 's/,//'

# I first tried this using Invoke-WebRequest. I hacked it up to find info
Gal Curl
$uri = "https://api.github.com/repos/PowerShell/PowerShell/releases"
Invoke-WebRequest $uri
$x = Invoke-WebRequest $uri
$x.ToString() |sls download_count
.\show-object1 $x
$j = ((iwr $uri).AllElements[3].Innertext|convertfrom-Json)
.\show-object1 $j
$x=((iwr $uri).AllElements[3].Innertext|convertfrom-Json)[0].Assets; $x |sort download_count -d|Ft name,down*;"Total Downloads: $(($x |measure download_count -sum).Sum)"
###############################
######## LESSON: Hack around and have FUN
###############################


#The problem was that this was too long so I emailed the team asking for a better approach.
#Lee Holmes pointed out that Invoke-RestMethod was a better cmdlet for REST APIs
.\show-object1 (irm api.github.com/repos/PowerShell/PowerShell/releases)
(irm api.github.com/repos/PowerShell/PowerShell/releases)[0].Assets | sort -d d* | ft n*,d*
###############################
######## LESSON: With a large enough community - there is always someone
######## that knows more about what you want to do than you do
###############################


return
#endregion



#region Step 2 Automation
function t 
{
(irm api.github.com/repos/PowerShell/PowerShell/releases)[0].Assets | sort -d d* | ft n*,d*
}
t
###############################
######## LESSON: Most Scripts are informal
###############################


#endregion




#region Step 3 - EVERYONE IS SAYING X
# At some point there was a comment thread about aliases and an email proported to say that
# "everyone was saying x"
# That didn't sound right so I decided to explore

(irm https://api.github.com/repos/PowerShell/PowerShell-RFC/issues/16/comments?per_page=300 -ov a).user.login |group -ov b|sort -Descending Count -ov c
.\Show-Object1.ps1 $a[0].user
$a[0] | % {$_.user} |sort -Unique login | 
    %{ add-member -in $_ -MemberType NoteProperty -name followCount -Value $((irm $_.followers_url).count) -pass -force } |
    %{ $user = (irm $_.url);
        add-member -in $_ -MemberType NoteProperty -name Name -Value $user.Name -force;
        add-member -in $_ -MemberType NoteProperty -name Bio -Value $User.bio -pass -force } |
    sort followcount,login |ft login,name,followcount,bio

(irm https://api.github.com/repos/PowerShell/PowerShell-RFC/issues/16/comments?per_page=300 -ov a).user.login |group -ov b|sort -Descending Count -ov c
# Notice that the conversation is dominated by 2 people.
# That made me think that I would like to have a tool to explore GIT COMMENTS

function Get-GitIssueComment
{
    Param
    (
        # Param1 help description
        [Parameter()]
        $Owner = "PowerShell",

        [Parameter()]
        $Repo = "PowerShell-RFC",

        [Parameter(Mandatory=1)]
        [int]
        $Issue
    )

    $uri = "https://api.github.com/repos/$Owner/$Repo/issues/$issue/comments?per_page=100"
    Write-Verbose "URI = $uri"
    Invoke-RestMethod -Uri $uri | Write-Output
}
Get-GitIssueComment -issue 16 -ov c
$c.user.login
(Get-GitIssueComment -issue 16 ).user.login |group -NoElement|sort count -Descending
cls
Get-GitIssueComment -Issue 16 |ft @{Name="User";expression={$_.user.login}},Body -Wrap 
cls
# Someone in the hallway mentioned something about a controversy about a "vulgar" comment
$c.where{$_.body -match "vulgar"} |ft @{Name="User";expression={$_.user.login}},Body -Wrap
# Looks like these two are going at it so let's explore that more
$c.where{$_.user.login -in "dlwyatt","kuldeepdhaka"} |ft @{Name="User";expression={$_.user.login}},Body -Wrap |out-tab
$c|gm

# Now we use this command on other repos:
Get-GitIssueComment -Owner PowerShell -Repo PowerShell -issue 2233 |
    ft -group @{Label="Day"; exp={([Datetime]$_.Created_at).DayOfWeek}} `
         @{Name="User";expression={$_.user.login}},Body
###############################
######## LESSON: A little formality makes it a lot more readable.
######## LESSON: Objects ROCK!
###############################

#endregion



#region Step 4 - Types make things better
# Here I have the same function as before but I add a single step - I add
# a give the generic PSCustomObject a type (just by adding it to PSTYPENAMES)

function Get-GitIssueComment
{
    
    Param
    (
        # Param1 help description
        [Parameter()]
        $Owner = "PowerShell",

        [Parameter()]
        $Repo = "PowerShell-RFC",

        [Parameter(Mandatory=1)]
        [int]
        $Issue
    )

    $uri = "https://api.github.com/repos/$Owner/$Repo/issues/$issue/comments?per_page=100"
    Write-Verbose "URI = $uri"
    $items = Invoke-RestMethod -Uri $uri 
    foreach ($c in $items)
    {
        $c.pstypenames.Insert(0,"JPS.GitIssueComment")
        Write-Output $c
    }
}
# Because it now has a TYPE - I can start partying on that TYPE
Update-TypeData -TypeName JPS.GitIssueComment -MemberType ScriptProperty -MemberName Login -Value {$this.user.login} -force
Update-TypeData -TypeName JPS.GitIssueComment -DefaultDisplayPropertySet id,body -DefaultDisplayProperty id  -DefaultKeyPropertySet id -Force


$c = Get-GitIssueComment -issue 16 
# Notice the new property: Login
$c |gm
cls
$c |sort Login |ft -GroupBy Login id, body |out-tab
$C[0] |fl *
# Notice that this gives you just the id and the body & you don't have to use Format-Table
$C[0]
#Tab complete this to understand why
gm -i $c[0].PSStandardMembers
$c[0].PSStandardMembers.DefaultDisplayPropertySet
$c[0].PSStandardMembers.DefaultDisplayProperty
# The defaultDisplayProperty is ID
$c |fw -AutoSize
# And when you have this stuff, things begin to get fun
$c |group login -NoElement |sort count -Descending

Get-GitIssueComment -Issue 16 | where{$_.login -in "dlwyatt","kuldeepdhaka"} |ft Login,Body -Wrap
# BUT NO INTELLISENSE - because there is no type defintion for JPS.GITISSUECOMMENT
Get-GitIssueComment -Issue 16 |

# Notice that even adding: [outputtype("JPS.GitIssueComment")]
# doesn't help


###############################
######## LESSON: PowerShell has an adaptive type system which you can party on by just adding a string
######## LESSON: TYPES ROCK
###############################
#endregion




#region Step 5 Lets make a class!

# We couldn't get any intellisense because the system doesn't
# know what the type "NAME" means in terms of properties, methods, etc
# So we want to convert the JSON document we get back into a proper class.

# Notice that the property Definitions have some type information
$c | Get-Member -MemberType Properties

#Here is a function to convert an Object into a Class
function Convert-ObjecttoClass
{
    [CmdletBinding()]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=1)]
        $InputObject,

        [Parameter(Mandatory=1)]
        [String]$ClassName
    )

#This is a HERE STRING. Because it uses " vs ' variables will be expanded
@"
class $ClassName
{
$(
foreach ($p in Get-Member -InputObject $InputObject -MemberType Properties |sort name)
{
    switch ($p.MemberType)
    {
        "NoteProperty" {
            $type = ($p.Definition -split " ")[0]
            if ($type -in "System.Management.Automation.PSCustomObject")
            {
                "`t[PSCustomObject]`$$($p.Name);`n"
            }else
            {
                "`t[$type]`$$($p.Name);`n"
            }
        }
    }
}
)
}
"@

}

Convert-ObjecttoClass -InputObject (Get-GitIssueComment -Issue 16)[0] -ClassName GitIssueComment

###############################
######## LESSON: Here documents are great to write scripts which write scripts
###############################

#endregion




#region Step 6 Now lets use that class!
cls

# 1) Pasted the class
# 2) Added an [OutputType([GitIssueComment6])]
# 3) Cast output to the class

class GitIssueComment6
{
    [string]$body;
     [string]$created_at;
     [string]$html_url;
     [int]$id;
     [string]$issue_url;
     [string]$updated_at;
     [string]$url;
     [PSObject]$user;
}
Update-TypeData -TypeName GitIssueComment6 -MemberType ScriptProperty -MemberName Login -Value {$this.user.login} -force
Update-TypeData -TypeName GitIssueComment6 -DefaultDisplayPropertySet id,body -DefaultDisplayProperty id  -DefaultKeyPropertySet id -Force

function Get-GitIssueComment6
{
    [CmdletBinding()]
    [OutputType([GitIssueComment6])]
    Param
    (
        # Param1 help description
        [Parameter()]
        $Owner = "PowerShell",

        [Parameter()]
        $Repo = "PowerShell-RFC",

        [Parameter(Mandatory=1)]
        [int]
        $Issue
    )

    $uri = "https://api.github.com/repos/$Owner/$Repo/issues/$issue/comments?per_page=100"
    Write-Verbose "URI = $uri"
    $items = Invoke-RestMethod -Uri $uri 
    foreach ($c in $items)
    {
        Write-Output ([GitIssueComment6]$c)
    }
}


Get-GitIssueComment6 -Issue 16 | where{$_.login -in "dlwyatt","kuldeepdhaka"} |ft Login,Body -Wrap
#Get-GitIssueComment6 -Issue 16 | where

# Did you notice this: [GitIssueComment6]$c
# PowerShell is very accomdating

[GitIssueComment6](@{body="Body"; Created_at="Today";id=3;user="Jeffrey";url="https://microsoft.com"}) |fl *
[GitIssueComment6](@"
body,Created_at,id,user,url
body,Today,13,Jeffrey,https://microsoft.com
"@
 |convertFrom-csv
) | fl *

###############################
######## LESSON: Having a class and an OUTPUT type gives you intellisense
######## LESSON: PowerShell will convert lots of things into classes instances
###############################

#endregion




#region Step 7 Now let's improve that Class
# Changes:
# - Capitalized Property names to make it cleaner
# - Added types to properties
# - Added a couple of helper methods

class GitIssueComment7
{
    [string]$Body;
     [DateTime]$Created_at;
     [URI]$Html_url;
     [int]$Id;
     [URI]$Issue_url;
     [DateTime]$Updated_at;
     [URI]$Url;
     [PSObject]$User;
    [Timespan]Get_Freshness() {return [DateTIme]::Now - $this.updated_at}
    [void]Show_Comment(){&"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" $this.Html_url}
}
function Get-GitIssueComment7
{
    [OutputType([GitIssueComment7])]
    Param
    (
        # Param1 help description
        [Parameter()]
        $Owner = "PowerShell",

        [Parameter()]
        $Repo = "PowerShell-RFC",

        [Parameter(Mandatory=1)]
        [int]
        $Issue
    )

    $uri = "https://api.github.com/repos/$Owner/$Repo/issues/$issue/comments?per_page=100"
    Write-Verbose "URI = $uri"
    $items = Invoke-RestMethod -Uri $uri 
    foreach ($c in $items)
    {
        Write-Output ([GitIssueComment7]$c)
    }
}
Update-TypeData -TypeName GitIssueComment7 -MemberType ScriptProperty -MemberName Login -Value {$this.user.login} -force
Update-TypeData -TypeName GitIssueComment7 -DefaultDisplayPropertySet id,body -DefaultDisplayProperty Title  -DefaultKeyPropertySet id -Force
Update-TypeData -TypeName GitIssueComment7 -MemberType ScriptProperty -MemberName Freshness -Value {$this.Get_freshness()} -Force
Update-TypeData -TypeName GitIssueComment7 -MemberType ScriptProperty -MemberName DayCreated -Value {$this.Created_at.Dayofweek} -Force

Get-GitIssueComment7 -issue 16 -ov c |sort created_at |ft -GroupBy DayCreated Id,Freshness,Body
$c[0] |fl *
$c |gm
$c[0].Issue_url
$c[0].Created_at |fl *
$c[0].Created_at |gm
$c[0].Show_Comment()

###############################
######## LESSON: You can incrementally add more formalism with types and unlock a lot of extra features
###############################



#endregion




#Regin Just in case
$Mycred = Get-Credential jsnover15@gmail.com

function Invoke-MyRestMethod
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Position=0)]
        $uri
    )

    $MoreParams = @{}
        $pair = "$($mycred.UserName):$($mycred.GetNetworkCredential().password)"
        $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))

        $basicAuthValue = "Basic $encodedCreds"

        $Headers = @{
            Authorization = $basicAuthValue
        }

        $MoreParams = @{Headers=$Headers}

        Invoke-RestMethod @PSBoundParameters @MoreParams 
}
Remove-Item alias:irm -Force
sal irm Invoke-MyRestMethod

function out-tab
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        $InputObject
    )
    begin
    {
        $t = $psise.CurrentPowerShellTab.Files.Add()
        $items = @()
    }
    process
    {
        $items += $InputObject;
    }
    end
    {
        $t.Editor.Text = "<#`n$($items|out-string)`n#>"
    }
}



$data = Import-Clixml C:\demo\Comments.xml
Invoke-FakeRM 
{
    return $data
}

#region