Skip to content

Commit ade5ac4

Browse files
Merge pull request #1 from StartAutomating/InitialBuild
Initial commit
2 parents def34c9 + dcd89ca commit ade5ac4

40 files changed

+3190
-1
lines changed

.github/workflows/TestAndPublish.yml

Lines changed: 484 additions & 0 deletions
Large diffs are not rendered by default.

@Delay.ps1

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<#
2+
.Synopsis
3+
Send an event after a delay.
4+
.Description
5+
Send an event after waiting an arbitrary [Timespan]
6+
.Example
7+
On Delay "00:00:01" -Then { "In a second!" | Out-Host }
8+
#>
9+
10+
param(
11+
# The amount of time to wait
12+
[Parameter(Mandatory,Position=0,ParameterSetName='Delay',ValueFromPipelineByPropertyName=$true)]
13+
[Alias('Delay', 'In')]
14+
[Timespan]
15+
$Wait
16+
)
17+
18+
process {
19+
$timer = New-Object Timers.Timer -Property @{Interval=$Wait.TotalMilliseconds;AutoReset=$false}
20+
$timer.Start()
21+
$timer | Add-Member NoteProperty EventName Elapsed -PassThru
22+
}

@FileChange.ps1

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<#
2+
.Synopsis
3+
Watches for File Changes.
4+
.Description
5+
Uses the [IO.FileSystemWatcher] to watch for changes to files.
6+
7+
Because some applications and frameworks write to files differently,
8+
you may see more than one event for a given change.
9+
#>
10+
param(
11+
# The path to the file or directory
12+
[Parameter(ValueFromPipelineByPropertyName)]
13+
[Alias('Fullname')]
14+
[string]
15+
$FilePath = "$pwd",
16+
17+
# A wildcard filter describing the names of files to watch
18+
[Parameter(ValueFromPipelineByPropertyName)]
19+
[string]
20+
$FileFilter,
21+
22+
# A notify filter describing the file changes that should raise events.
23+
[Parameter(ValueFromPipelineByPropertyName)]
24+
[IO.NotifyFilters[]]
25+
$NotifyFilter = @("FileName", "DirectoryName", "LastWrite"),
26+
27+
# If set, will include subdirectories in the watcher.
28+
[Alias('InludeSubsdirectory','InludeSubsdirectories')]
29+
[switch]
30+
$Recurse,
31+
32+
# The names of the file change events to watch.
33+
# By default, watches for Changed, Created, Deleted, or Renamed
34+
[ValidateSet('Changed','Created','Deleted','Renamed')]
35+
[string[]]
36+
$EventName = @('Changed','Created','Deleted','Renamed')
37+
)
38+
39+
process {
40+
$resolvedFilePath = try {
41+
$ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($FilePath)
42+
} catch {
43+
Write-Error "Could not resolve path '$FilePath'"
44+
return
45+
}
46+
47+
if ([IO.File]::Exists("$resolvedFilePath")) { # If we're passed a path to a specific file
48+
$fileInfo = ([IO.FileInfo]"$resolvedFilePath")
49+
$filePath = $fileInfo.Directory.FullName # we need to watch the directory
50+
$FileFilter = $fileInfo.Name # and then filter based off of the file name.
51+
} elseif ([IO.Directory]::Exists("$resolvedFilePath")) {
52+
$filePath = "$resolvedFilePath"
53+
}
54+
55+
$fileSystemWatcher = [IO.FileSystemWatcher]::new($FilePath) # Create the watcher
56+
$fileSystemWatcher.IncludeSubdirectories = $Recurse # include subdirectories if -Recurse was passed
57+
$fileSystemWatcher.EnableRaisingEvents = $true # Enable raising events
58+
if ($FileFilter) {
59+
$fileSystemWatcher.Filter = $FileFilter
60+
} else {
61+
$fileSystemWatcher.Filter = "*"
62+
}
63+
$combinedNotifyFilter = 0
64+
foreach ($n in $NotifyFilter) {
65+
$combinedNotifyFilter = $combinedNotifyFilter -bor $n
66+
}
67+
$fileSystemWatcher.NotifyFilter = $combinedNotifyFilter
68+
$fileSystemWatcher |
69+
Add-Member NoteProperty EventName $EventName -Force -PassThru
70+
}

@Repeat.ps1

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<#
2+
.Synopsis
3+
Send events on repeat.
4+
.Description
5+
Sends events on repeat, at a given [Timespan] -Interval.
6+
.Example
7+
On Interval "00:01:00" { "Every minute" | Out-Host }
8+
#>
9+
[Diagnostics.Tracing.EventSource(Name='Elapsed')]
10+
param(
11+
# The amount of time to wait between sending events.
12+
[Parameter(Mandatory,Position=0,ValueFromPipelineByPropertyName=$true)]
13+
[Alias('Every')]
14+
[Timespan]
15+
$Interval
16+
)
17+
18+
process {
19+
$timer = New-Object Timers.Timer -Property @{Interval=$Interval.TotalMilliseconds;AutoReset=$true}
20+
$timer.Start()
21+
$timer
22+
}

@Time.ps1

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<#
2+
.Synopsis
3+
Sends an event at a specific time.
4+
.Description
5+
Sends an event at a specific date and time.
6+
.Example
7+
On Time "5:00 PM" { "EOD!" | Out-Host }
8+
#>
9+
[Diagnostics.Tracing.EventSource(Name='Elapsed')]
10+
param(
11+
[Parameter(Mandatory,Position=0,ParameterSetName='SpecificTime')]
12+
[DateTime]
13+
$DateTime
14+
)
15+
16+
process {
17+
if ($DateTime -lt [DateTime]::Now) {
18+
Write-Error "-DateTime '$DateTime' must be in the future"
19+
return
20+
}
21+
22+
$timer =
23+
New-Object Timers.Timer -Property @{Interval=($DateTime - [DateTime]::Now).TotalMilliseconds;AutoReset=$false}
24+
25+
if (-not $timer) { return }
26+
$timer.Start()
27+
return $timer
28+
}

Clear-EventSource.ps1

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
function Clear-EventSource
2+
{
3+
<#
4+
.Synopsis
5+
Clears event source subscriptions
6+
.Description
7+
Clears any active subscriptions for any event source.
8+
.Example
9+
Clear-EventSource
10+
.Link
11+
Get-EventSource
12+
#>
13+
[CmdletBinding(SupportsShouldProcess=$true)]
14+
[OutputType([nullable])]
15+
param(
16+
# The name of the event source.
17+
[Parameter(ValueFromPipelineByPropertyName)]
18+
[string[]]
19+
$Name)
20+
21+
process {
22+
#region Determine Event Sources
23+
$parameterCopy = @{} + $PSBoundParameters
24+
$null = $parameterCopy.Remove('WhatIf')
25+
$eventSources = Get-EventSource @parameterCopy -Subscription
26+
if ($WhatIfPreference) {
27+
$eventSources
28+
return
29+
}
30+
#endregion Determine Event Sources
31+
#region Unregister
32+
if ($PSCmdlet.ShouldProcess("Clear event sources $($Name -join ',')")) {
33+
$eventSources | Unregister-Event
34+
}
35+
#endregion Unregister
36+
}
37+
}

EventSources/@HttpResponse.ps1

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<#
2+
.Synopsis
3+
Sends events on HTTP Responses.
4+
.Description
5+
Sends HTTP requests and signals on Responses
6+
7+
8+
Event MessageData will contain the response, with two additional properties:
9+
* .ResponseBytes
10+
* .ResponseContent
11+
#>
12+
[ComponentModel.InitializationEvent({
13+
# Because of the potential for a "narrow window" timing issue,
14+
# this event source needs to send the request the moment after the event has been subscribed to.
15+
$httpAsyncInfo = [PSCustomObject]@{
16+
InputObject = $this
17+
IAsyncResult = $this.BeginGetResponse($null, $null)
18+
InvokeID = $this.RequestID
19+
}
20+
if ($null -eq $Global:HttpResponsesAsync) {
21+
$Global:HttpResponsesAsync = [Collections.ArrayList]::new()
22+
}
23+
$null = $Global:HttpResponsesAsync.Add($httpAsyncInfo)
24+
})]
25+
param(
26+
# The Uniform Resource Identifier.
27+
[Parameter(Mandatory,Position=0,ValueFromPipelineByPropertyName)]
28+
[Alias('Url')]
29+
[Uri]
30+
$Uri,
31+
32+
# The HTTP Method
33+
[Parameter(Position=1,ValueFromPipelineByPropertyName)]
34+
[ValidateSet('Get','Head','Post','Put','Delete','Trace','Options','Merge','Patch')]
35+
[string]
36+
$Method = 'GET',
37+
38+
# A collection of headers to send with the request.
39+
[Parameter(Position=2,ValueFromPipelineByPropertyName)]
40+
[Alias('Headers')]
41+
[Collections.IDictionary]
42+
$Header,
43+
44+
# The request body.
45+
[Parameter(Position=3,ValueFromPipelineByPropertyName)]
46+
[PSObject]
47+
$Body,
48+
49+
# The polling interval.
50+
# This is the minimum amount of time until you will be notified of the success or failure of a http request
51+
[Parameter(Position=3,ValueFromPipelineByPropertyName)]
52+
[Timespan]
53+
$PollingInterval = "00:00:00.331",
54+
55+
[Text.Encoding]
56+
$TransferEncoding = $([Text.Encoding]::UTF8)
57+
)
58+
59+
process {
60+
# Clear the event subscriber if one exists.
61+
$eventSubscriber = Get-EventSubscriber -SourceIdentifier "@HttpResponse_Check" -ErrorAction SilentlyContinue
62+
if ($eventSubscriber) {$eventSubscriber | Unregister-Event}
63+
64+
# Create a new subscriber for the request.
65+
$httpResponseCheckTimer = New-Object Timers.Timer -Property @{
66+
Interval = $PollingInterval.TotalMilliseconds # Every pollinginterval,
67+
AutoReset = $true
68+
}
69+
$HttpResponseChecker =
70+
Register-ObjectEvent -InputObject $httpResponseCheckTimer -EventName Elapsed -Action {
71+
$toCallEnd = # check to see if any requests have completed.
72+
@(foreach ($httpAsyncInfo in $Global:HttpResponsesAsync) {
73+
if ($httpAsyncInfo.IAsyncResult.IsCompleted) {
74+
$httpAsyncInfo
75+
}
76+
})
77+
78+
$null = # Foreach completed request
79+
foreach ($httpAsyncInfo in $toCallEnd) {
80+
$webResponse =
81+
try { # try to get the response
82+
$httpAsyncInfo.InputObject.EndGetResponse($httpAsyncInfo.IAsyncResult)
83+
} catch {
84+
$_ # and catch any error.
85+
}
86+
87+
88+
if ($webResponse -is [Management.Automation.ErrorRecord] -or
89+
$webResponse -is [Exception]) # If we got an error
90+
{
91+
# Signal the error
92+
New-Event -SourceIdentifier "HttpRequest.Failed.$($httpAsyncInfo.InvokeID)" -MessageData $webResponse
93+
$Global:HttpResponsesAsync.Remove($httpAsyncInfo)
94+
continue
95+
}
96+
97+
98+
# Otherwise, get the response stream
99+
$ms = [IO.MemoryStream]::new()
100+
$null = $webResponse.GetResponseStream().CopyTo($ms)
101+
$responseBytes = $ms.ToArray() # as a [byte[]].
102+
$ms.Close()
103+
$ms.Dispose()
104+
105+
106+
$encoding = # See if the response had an encoding
107+
if ($webResponse.ContentEncoding) { $webResponse.ContentEncoding }
108+
elseif ($webResponse.CharacterSet) {
109+
# or a character set.
110+
[Text.Encoding]::GetEncodings() | Where-Object Name -EQ $webResponse.CharacterSet
111+
}
112+
113+
$webResponseContent =
114+
if ($encoding) { # If it did, decode the response content.
115+
[IO.StreamReader]::new([IO.MemoryStream]::new($responseBytes), $encoding).ReadToEnd()
116+
} else {
117+
$null
118+
}
119+
120+
# Add the properties to the web response.
121+
$webResponse |
122+
Add-Member NoteProperty ResponseBytes $webResponseContent -Force -PassThru |
123+
Add-Member NoteProperty ResponseContent $webResponseContent -Force
124+
$webResponse.Close()
125+
126+
# And send the response with the additional information.
127+
New-Event -SourceIdentifier "HttpRequest.Completed.$($httpAsyncInfo.InvokeID)" -MessageData $webResponse
128+
$Global:HttpResponsesAsync.Remove($httpAsyncInfo)
129+
}
130+
131+
if ($Global:HttpResponsesAsync.Count -eq 0) {
132+
Get-EventSubscriber -SourceIdentifier "@HttpResponse_Check" | Unregister-Event
133+
}
134+
}
135+
136+
$httpResponseCheckTimer.Start() # Start it's timer.
137+
138+
139+
$httpRequest = [Net.HttpWebRequest]::CreateHttp($Uri)
140+
$httpRequest.Method = $Method
141+
if ($Header -and $Header.Count) {
142+
foreach ($kv in $Header.GetEnumerator()) {
143+
$httpRequest.Headers[$kv.Key] = $kv.Value
144+
}
145+
}
146+
if ($Body) {
147+
$requestStream = $httpRequest.GetRequestStream()
148+
149+
if (-not $requestStream) { return }
150+
if ($body -is [byte[]] -or $Body -as [byte[]]) {
151+
[IO.MemoryStream]::new([byte[]]$Body).CopyTo($requestStream)
152+
}
153+
elseif ($Body -is [string]) {
154+
[IO.StreamWriter]::new($requestStream, $TransferEncoding).Write($Body)
155+
}
156+
else {
157+
[IO.StreamWriter]::new($requestStream, $TransferEncoding).Write((ConvertTo-Json -InputObject $body -Depth 100))
158+
}
159+
}
160+
$requestId = [Guid]::NewGuid().ToString()
161+
$httpRequest |
162+
Add-Member NoteProperty SourceIdentifier "HttpRequest.Completed.$requestId","HttpRequest.Failed.$requestId" -Force -PassThru |
163+
Add-Member NoteProperty RequestID $requestId -Force -PassThru
164+
}

EventSources/@Job.ps1

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<#
2+
.Synopsis
3+
Watches a PowerShell Job's State.
4+
.Description
5+
Watches a PowerShell Job's StateChange event.
6+
7+
This will send an event when a job finishes.
8+
#>
9+
param(
10+
[Parameter(Mandatory,ValueFromPipelineByPropertyName)]
11+
[Alias('ID')]
12+
[int]
13+
$JobID)
14+
15+
if ($_ -is [Management.Automation.Job]) {
16+
$_
17+
} else {
18+
Get-Job -Id $JobID -ErrorAction SilentlyContinue
19+
}
20+

0 commit comments

Comments
 (0)