diff --git a/.github/workflows/JSON-LD-Build.yml b/.github/workflows/JSON-LD-Build.yml index e9d134e..aa451c8 100644 --- a/.github/workflows/JSON-LD-Build.yml +++ b/.github/workflows/JSON-LD-Build.yml @@ -35,7 +35,7 @@ jobs: Install-Module -Name Pester -Repository PSGallery -Force -Scope CurrentUser -MaximumVersion $PesterMaxVersion -SkipPublisherCheck -AllowClobber Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion} @Parameters - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@v2 - name: RunPester id: RunPester shell: pwsh @@ -92,6 +92,9 @@ jobs: $result = Invoke-Pester -PassThru -Verbose -OutputFile ".\$moduleName.TestResults.xml" -OutputFormat NUnitXml @codeCoverageParameters + "::set-output name=TotalCount::$($result.TotalCount)", + "::set-output name=PassedCount::$($result.PassedCount)", + "::set-output name=FailedCount::$($result.FailedCount)" | Out-Host if ($result.FailedCount -gt 0) { "::debug:: $($result.FailedCount) tests failed" foreach ($r in $result.TestResult) { @@ -491,20 +494,6 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v2 - - name: GitLogger - uses: GitLogging/GitLoggerAction@main - id: GitLogger - - name: Use PSSVG Action - uses: StartAutomating/PSSVG@main - id: PSSVG - - name: Use PipeScript Action - uses: StartAutomating/PipeScript@main - id: PipeScript - name: UseEZOut uses: StartAutomating/EZOut@master - - name: UseHelpOut - uses: StartAutomating/HelpOut@master - - name: Use PSJekyll Action - uses: PowerShellWeb/PSJekyll@main - id: PSJekyll diff --git a/Build/GitHub/Jobs/JsonLD.psd1 b/Build/GitHub/Jobs/JsonLD.psd1 index 8c2f390..288a11f 100644 --- a/Build/GitHub/Jobs/JsonLD.psd1 +++ b/Build/GitHub/Jobs/JsonLD.psd1 @@ -5,28 +5,7 @@ @{ name = 'Check out repository' uses = 'actions/checkout@v2' - }, - @{ - name = 'GitLogger' - uses = 'GitLogging/GitLoggerAction@main' - id = 'GitLogger' - }, - @{ - name = 'Use PSSVG Action' - uses = 'StartAutomating/PSSVG@main' - id = 'PSSVG' - }, - @{ - name = 'Use PipeScript Action' - uses = 'StartAutomating/PipeScript@main' - id = 'PipeScript' - }, - 'RunEZOut', - 'RunHelpOut', - @{ - name = 'Use PSJekyll Action' - uses = 'PowerShellWeb/PSJekyll@main' - id = 'PSJekyll' } + 'RunEZOut' ) } \ No newline at end of file diff --git a/Build/JSON-LD.ezout.ps1 b/Build/JSON-LD.ezout.ps1 new file mode 100644 index 0000000..6b9d2fa --- /dev/null +++ b/Build/JSON-LD.ezout.ps1 @@ -0,0 +1,39 @@ +#requires -Module EZOut +# Install-Module EZOut or https://github.com/StartAutomating/EZOut +$myFile = $MyInvocation.MyCommand.ScriptBlock.File +$myModuleName = $MyInvocation.MyCommand.Name -replace '\.ezout.ps1$' +$myRoot = $myFile | Split-Path | Split-Path +Push-Location $myRoot +$formatting = @( + # Add your own Write-FormatView here, + # or put them in a Formatting or Views directory + foreach ($potentialDirectory in 'Formatting','Views','Types') { + Join-Path $myRoot $potentialDirectory | + Get-ChildItem -ea ignore | + Import-FormatView -FilePath {$_.Fullname} + } +) + +$destinationRoot = $myRoot + +if ($formatting) { + $myFormatFilePath = Join-Path $destinationRoot "$myModuleName.format.ps1xml" + # You can also output to multiple paths by passing a hashtable to -OutputPath. + $formatting | Out-FormatData -Module $MyModuleName -OutputPath $myFormatFilePath +} + +$types = @( + # Add your own Write-TypeView statements here + # or declare them in the 'Types' directory + Join-Path $myRoot Types | + Get-Item -ea ignore | + Import-TypeView + +) + +if ($types) { + $myTypesFilePath = Join-Path $destinationRoot "$myModuleName.types.ps1xml" + # You can also output to multiple paths by passing a hashtable to -OutputPath. + $types | Out-TypeData -OutputPath $myTypesFilePath +} +Pop-Location diff --git a/CHANGELOG.md b/CHANGELOG.md index f3830db..049b0d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,32 @@ -> Like It? [Star It](https://github.com/PowerShellWeb/JSON-LD) -> Love It? [Support It](https://github.com/sponsors/StartAutomating) +Please: + +* [Like, Share, and Subscribe](https://github.com/PowerShellWeb/JSON-LD) +* [Support Us](https://github.com/sponsors/StartAutomating) + +--- + +## JSON-LD 0.1.1 + +* Updating Examples (#13) +* Simplfiying module scaffolding (#15) +* Building types with EZOut (#5) +* Supporting file input (#23) +* `Get-JSONLD -as` + * `Get-JSONLD -as json` (#16) + * `Get-JSONLD -as html` (#17) + * `Get-JSONLD -as script` (#18) + * `Get-JSONLD -as xml` (#19) +* Adding conversion to JsonSchema (#21) +* Adding conversion to At Protocol Lexicons (#22) + +--- ## JSON-LD 0.1 Caching JSON-LD requests +--- + ## JSON-LD 0.0.1 Get Linked Data from any page diff --git a/Commands/Get-JsonLD.ps1 b/Commands/Get-JsonLD.ps1 index c69504e..a7a5b71 100644 --- a/Commands/Get-JsonLD.ps1 +++ b/Commands/Get-JsonLD.ps1 @@ -8,7 +8,7 @@ function Get-JsonLD { This is a format used by many websites to provide structured data about their content. .EXAMPLE # Want to get information about a movie? Linked Data to the rescue! - Get-JsonLD -Url https://www.imdb.com/title/tt0211915/ + Get-JsonLD -Url https://letterboxd.com/film/amelie/ .EXAMPLE # Want information about an article? Lots of news sites use this format. Get-JsonLD https://www.thebulwark.com/p/mahmoud-khalil-immigration-detention-first-amendment-free-speech-rights @@ -21,9 +21,30 @@ function Get-JsonLD { param( # The URL that may contain JSON-LD data [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [Alias('href')] [Uri] $Url, + <# + + If set, will the output as: + + |as|is| + |-|-| + |html|the response as text| + |json|the match as json| + |*jsonld`|ld`|linkedData*|the match as linked data|' + |script|the script tag| + |xml|the script tag, as xml| + + #> + [ValidateSet('html', 'json', 'jsonld', 'ld', 'linkedData', 'script', 'xml')] + [string] + $as = 'jsonld', + + [switch] + $RawHtml, + # If set, will force the request to be made even if the URL has already been cached. [switch] $Force @@ -46,39 +67,146 @@ application/ld\+json # The type that indicates linked d '@, 'IgnoreCase,IgnorePatternWhitespace','00:00:00.1') # Initialize the cache for JSON-LD requests - if (-not $script:JsonLDRequestCache) { - $script:JsonLDRequestCache = [Ordered]@{} + if (-not $script:Cache) { + $script:Cache = [Ordered]@{} + } + + filter output { + $in = $_ + $mySelf = $MyInvocation.MyCommand + if ($in.'@context' -is [string]) { + $context = $in.'@context' + } + if ($in.'@graph') { + if ($in.pstypenames -ne 'application/ld+json') { + $in.pstypenames.insert(0,'application/ld+json') + } + foreach ($graphObject in $in.'@graph') { + $null = $graphObject | + & $mySelf + } + } + elseif ($in.'@type') { + + $typeName = if ($context) { + $context, $in.'@type' -join '/' + } else { + $in.'@type' + } + + if ($in.pstypenames -ne 'application/ld+json') { + $in.pstypenames.insert(0,'application/ld+json') + } + if ($in.pstypenames -ne $typeName) { + $in.pstypenames.insert(0,$typeName) + } + + foreach ($property in $in.psobject.properties) { + if ($property.value.'@type') { + $null = $property.value | + & $mySelf + } + } + } + $in + } + + $foreachFile = { + $inFile = $_.FullName + try { + + Get-Content -LiteralPath $_.FullName -Raw | + ConvertFrom-Json | + output + } catch { + Write-Verbose "$($inFile.FullName) : $_" + } } } process { + if ($url.IsFile -or + -not $url.AbsoluteUri + ) { + if (Test-Path $url.OriginalString) { + Get-ChildItem $url.OriginalString -File | + Foreach-Object $foreachFile + } elseif ($MyInvocation.MyCommand.Module -and + (Test-Path ( + Join-Path ( + $MyInvocation.MyCommand.Module | Split-Path + ) $url.OriginalString + )) + ) { + Get-ChildItem -Path ( + Join-Path ( + $MyInvocation.MyCommand.Module | Split-Path + ) $url.OriginalString + ) -File | + Foreach-Object $foreachFile + } + + return + } + $restResponse = - if ($Force -or -not $script:JsonLDRequestCache[$url]) { - $script:JsonLDRequestCache[$url] = Invoke-RestMethod -Uri $Url - $script:JsonLDRequestCache[$url] + if ($Force -or -not $script:Cache[$url]) { + $script:Cache[$url] = Invoke-RestMethod -Uri $Url + $script:Cache[$url] } else { - $script:JsonLDRequestCache[$url] + $script:Cache[$url] } + + if ($as -eq 'html') { + return $restResponse + } + + # Find all linked data tags within the response foreach ($match in $linkedDataRegex.Matches("$restResponse")) { + # If we want the result as xml + if ($As -eq 'xml') { + # try to cast it + $matchXml ="$match" -as [xml] + if ($matchXml) { + # and output it if found. + $matchXml + continue + } else { + # otherwise, fall back to the ` + + + GetLexicon + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 77845e0..02d222d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This information can be useful in any number of fun and useful PowerShell scenar For example, let's get information about a movie. ~~~PowerShell -Get-JsonLD https://www.imdb.com/title/tt0211915/ +Get-JsonLD -Url https://letterboxd.com/film/amelie/ ~~~ Let's take things a step further, and get the information we can know about any movie: diff --git a/Types/JsonLD/GetJsonSchema.ps1 b/Types/JsonLD/GetJsonSchema.ps1 new file mode 100644 index 0000000..782898d --- /dev/null +++ b/Types/JsonLD/GetJsonSchema.ps1 @@ -0,0 +1,95 @@ + +param($graph = $this) + +if (-not $graph.'@graph') { + if ($graph.'@context' -is [string] -and + $graph.'@type' -is [string]) { + $gotGraph = Get-JsonLD -Url ( + $graph.'@context', $graph.'@type' -join '/' -replace '^http:', 'https:' + ) + if ($gotGraph.'@graph') { + $graph = $gotGraph + } + } +} + +if (-not $graph.'@graph') { return } + +$graphTypes = $graph.'@graph' | Group-Object '@type' -AsHashTable + +$classes = $graphTypes['rdfs:class'] +if (-not $classes) { + return +} + +$baseType = $classes | + Where-Object 'rdfs:label' -eq 'thing' + +if (-not $baseType) { + return +} + +$ClassHierarchy = @( + $baseType + do { + $parentType = $classes | + Where-Object { + $_.'rdfs:subClassOf'.'@id' -eq $baseType.'@id' + } + if ($parentType) { + $parentType + $baseType = $parentType + } + } while ($parentType) +) + +if (-not $ClassHierarchy) { return } + +$classInfo = $ClassHierarchy[-1] + +$jsonSchema = [Ordered]@{ + '$id' = "`$$($classInfo.'@id' -replace 'schema:', 'schema.org/')" + title = $($classInfo.'@id' -replace 'schema:', 'https://schema.org/') + description = $classInfo.'rdfs:comment' + properties = [Ordered]@{} +} + +foreach ($rdfProperty in $graphTypes['rdf:property']) { + $propertyInfo = [Ordered]@{} + switch -regex ($rdfProperty.'@id') { + '(?>date|time)$' { + $propertyInfo['type'] = 'string' + $propertyInfo['format'] = 'date-time' + } + 'url$' { + $propertyInfo['type'] = 'string' + $propertyInfo['format'] = 'url' + } + '(?>name|description)$' { + $propertyInfo['type'] = 'string' + } + default { + $propertyInfo['type'] = 'object' + } + } + if (@($rdfProperty.'schema:rangeIncludes').Count -eq 1) { + switch ($rdfProperty.'schema:rangeIncludes') { + schema:Boolean { + $propertyInfo['type'] = 'boolean' + } + schema:Integer { + $propertyInfo['type'] = 'integer' + } + schema:Number { + $propertyInfo['type'] = 'number' + } + } + } + if ($rdfProperty.'rdfs:comment') { + $propertyInfo['description'] = $rdfProperty.'rdfs:comment' + } + $propertyName = $rdfProperty.'@id' -replace '^schema:' + $jsonSchema.properties[$propertyName] = $propertyInfo +} +$jsonSchema + diff --git a/Types/JsonLD/GetLexicon.ps1 b/Types/JsonLD/GetLexicon.ps1 new file mode 100644 index 0000000..d875812 --- /dev/null +++ b/Types/JsonLD/GetLexicon.ps1 @@ -0,0 +1,32 @@ + +param($graph = $this) + +$jsonSchema = $this.GetJsonSchema($graph) +if (-not $jsonSchema.'$id') { + throw "Missing $jsonSchema.$id" + return +} + +$domain, $relativePath = $jsonSchema.'$id' -replace '^$' -split '/' +if (-not $domain) { return} +if (-not $relativePath ) { return } +$domain = @($domain -split '\.') +[Array]::Reverse($domain) +$nsid = $domain, $relativePath -join '.' + + +$jsonSchema.psobject.properties.Remove('$id') + +[Ordered]@{ + lexicon = 1 + id = $nsid + defs = [Ordered]@{ + main = [Ordered]@{ + type = 'record' + description = $jsonSchema.description + record = $jsonSchema + } + } +} + + diff --git a/Types/JsonLD/PSTypeName.txt b/Types/JsonLD/PSTypeName.txt new file mode 100644 index 0000000..a24735f --- /dev/null +++ b/Types/JsonLD/PSTypeName.txt @@ -0,0 +1 @@ +application/ld+json \ No newline at end of file