Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add json-schemas for Configuration json files #2532

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4d7e6de
added json-schema for config/dns.json
psyirius Aug 7, 2024
26256e1
[build-script]: strip $schema from config jsons before embedding
psyirius Aug 7, 2024
5a60625
added json-schema for config/themes.json
psyirius Aug 7, 2024
483baea
json-schema for config/themes.json required fields
psyirius Aug 7, 2024
6fedea1
added json-schema for config/tweaks.json
psyirius Aug 7, 2024
c61b1e8
added json-schema for config/preset.json
psyirius Aug 7, 2024
5e2acad
added json-schema for config/feature.json
psyirius Aug 7, 2024
8844b85
json-schema adjustments
psyirius Aug 7, 2024
e2a870f
added json-schema for config/applications.json
psyirius Aug 7, 2024
e7d0637
Merge branch 'main' into config-schema
psyirius Aug 7, 2024
632f295
Merge branch 'main' into config-schema
psyirius Aug 7, 2024
14efc4f
Merge branch 'main' into config-schema
psyirius Aug 7, 2024
b9b617e
Merge branch 'main' into config-schema
psyirius Aug 8, 2024
77e5c25
Merge branch 'main' into config-schema
psyirius Aug 9, 2024
50cb1be
Merge branch 'main' into config-schema
psyirius Aug 14, 2024
31ec62f
Merge branch 'main' into config-schema
ChrisTitusTech Aug 28, 2024
be9fae1
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
01f4198
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
97bf952
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
e6dbc81
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
26a2b3d
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
c79fa58
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
bf56639
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
4452177
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
6616437
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
5d53b96
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
0d9b6f3
Update configs.Tests.ps1
ChrisTitusTech Aug 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion Compile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,35 @@ Get-ChildItem "$workingdir\functions" -Recurse -File | ForEach-Object {
Update-Progress "Adding: Config *.json" 40
Get-ChildItem "$workingdir\config" | Where-Object {$psitem.extension -eq ".json"} | ForEach-Object {
$json = (Get-Content $psitem.FullName).replace("'","''")
$jsonAsObject = $json | convertfrom-json

# Replace every XML Special Character so it'll render correctly in final build
# Only do so if json files has content to be displayed (for example the applications, tweaks, features json files)
# Make an Array List containing every name at first level of Json File
[PSCustomObject]$jsonAsObject = $json | convertfrom-json

# Remove properties like $schema and such from the json object (we don't need it at this point)
@(
"`$schema"
) | ForEach-Object {
$jsonAsObject.PSObject.Properties.Remove($_) | Out-Null
}

$firstLevelJsonList = [System.Collections.ArrayList]::new()
$jsonAsObject.PSObject.Properties.Name | ForEach-Object {$null = $firstLevelJsonList.Add($_)}
# Note:
# Avoid using HTML Entity Codes, for example '”' (stands for "Right Double Quotation Mark"),
# Use **HTML decimal/hex codes instead**, as using HTML Entity Codes will result in XML parse Error when running the compiled script.
for ($i = 0; $i -lt $firstLevelJsonList.Count; $i += 1) {
$firstLevelName = $firstLevelJsonList[$i]
if ($jsonAsObject.$firstLevelName.content -ne $null) {
$jsonAsObject.$firstLevelName.content = $jsonAsObject.$firstLevelName.content.replace('&','&#38;').replace('“','&#8220;').replace('”','&#8221;').replace("'",'&#39;').replace('<','&#60;').replace('>','&#62;').replace('—','&#8212;')
$jsonAsObject.$firstLevelName.content = $jsonAsObject.$firstLevelName.content.replace('&#39;&#39;',"&#39;") # resolves the Double Apostrophe caused by the first replace function in the main loop
}
if ($jsonAsObject.$firstLevelName.description -ne $null) {
$jsonAsObject.$firstLevelName.description = $jsonAsObject.$firstLevelName.description.replace('&','&#38;').replace('“','&#8220;').replace('”','&#8221;').replace("'",'&#39;').replace('<','&#60;').replace('>','&#62;').replace('—','&#8212;')
$jsonAsObject.$firstLevelName.description = $jsonAsObject.$firstLevelName.description.replace('&#39;&#39;',"&#39;") # resolves the Double Apostrophe caused by the first replace function in the main loop
}
}
Comment on lines +81 to +96
Copy link
Contributor

@og-mrk og-mrk Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@psyirius @ChrisTitusTech This code for handling special characters is not needed anymore, as we're dynamically generating the UI, therefore skipping the XAML Reader in the process, so I recommend removing it plus any comments related to it.


# Add 'WPFInstall' as a prefix to every entry-name in 'applications.json' file
if ($psitem.Name -eq "applications.json") {
Expand Down
1 change: 1 addition & 0 deletions config/applications.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "../schemas/config/applications.json",
"1password": {
"category": "Utilities",
"choco": "1password",
Expand Down
1 change: 1 addition & 0 deletions config/dns.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "../schemas/config/dns.json",
"Google":{
"Primary": "8.8.8.8",
"Secondary": "8.8.4.4",
Expand Down
1 change: 1 addition & 0 deletions config/feature.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "../schemas/config/feature.json",
"WPFFeaturesdotnet": {
"Content": "All .Net Framework (2,3,4)",
"Description": ".NET and .NET Framework is a developer platform made up of tools, programming languages, and libraries for building many different types of applications.",
Expand Down
1 change: 1 addition & 0 deletions config/preset.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "../schemas/config/preset.json",
"Standard": [
"WPFTweaksAH",
"WPFTweaksConsumerFeatures",
Expand Down
1 change: 1 addition & 0 deletions config/themes.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "../schemas/config/themes.json",
"_default": {
"CustomDialogFontSize": "12",
"CustomDialogFontSizeHeader": "14",
Expand Down
1 change: 1 addition & 0 deletions config/tweaks.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "../schemas/config/tweaks.json",
"WPFTweaksAH": {
"Content": "Disable Activity History",
"Description": "This erases recent docs, clipboard, and run history.",
Expand Down
141 changes: 77 additions & 64 deletions pester/configs.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,81 +1,94 @@
# Enable verbose output
$VerbosePreference = "Continue"

# Import Config Files
$global:importedconfigs = @{}
Get-ChildItem .\config | Where-Object {$_.Extension -eq ".json"} | ForEach-Object {
$global:importedconfigs[$psitem.BaseName] = Get-Content $psitem.FullName | ConvertFrom-Json
$global:importedConfigs = @{}
Get-ChildItem .\config -Filter *.json | ForEach-Object {
try {
$global:importedConfigs[$_.BaseName] = Get-Content $_.FullName | ConvertFrom-Json
Write-Verbose "Successfully imported config file: $($_.FullName)"
} catch {
Write-Error "Failed to import config file: $($_.FullName). Error: $_"
}
}


#===========================================================================
# Tests - Application Installs
#===========================================================================

Describe "Config Files" -ForEach @(
@{
name = "applications"
config = $('{
"winget": "value",
"choco": "value",
"category": "value",
"content": "value",
"description": "value",
"link": "value"
}' | ConvertFrom-Json)
},
@{
name = "tweaks"
undo = $true
Describe "Config Files Validation" {
$configTemplates = @{
applications = @("winget", "choco", "category", "content", "description", "link")
tweaks = @("registry", "service", "ScheduledTask")
}
) {
Context "$name config file" {
It "Imports with no errors" {
$global:importedconfigs.$name | should -Not -BeNullOrEmpty

Context "Config File Structure" {
It "Should import all config files without errors" {
$global:importedConfigs | Should -Not -BeNullOrEmpty -Because "No config files were imported successfully"
Write-Verbose "Imported configs: $($global:importedConfigs.Keys -join ', ')"
}
if ($config) {
It "Imports should be the correct structure" {
$applications = $global:importedconfigs.$name | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
$template = $config | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
$result = New-Object System.Collections.Generic.List[System.Object]
Foreach ($application in $applications) {
$compare = $global:importedconfigs.$name.$application | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
if ($(Compare-Object $compare $template) -ne $null) {
$result.Add($application)

foreach ($configName in $configTemplates.Keys) {
It "Should have the correct structure for $configName" {
$global:importedConfigs.ContainsKey($configName) | Should -BeTrue -Because "Config file '$configName' is missing"
$config = $global:importedConfigs[$configName]
$config | Should -Not -BeNullOrEmpty -Because "Config file '$configName' is empty"

$properties = $config | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
foreach ($prop in $properties) {
$itemProperties = $config.$prop | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
$missingProperties = Compare-Object $configTemplates[$configName] $itemProperties | Where-Object { $_.SideIndicator -eq "<=" } | Select-Object -ExpandProperty InputObject
$missingProperties | Should -BeNullOrEmpty -Because "Item '$prop' in '$configName' config is missing properties: $($missingProperties -join ', ')"
if ($missingProperties) {
Write-Verbose "Missing properties in ${configName}['${prop}']: $($missingProperties -join ', ')"
}
}

$result | Select-String "WPF*" | should -BeNullOrEmpty
}
}
if($undo) {
It "Tweaks should contain original Value" {
$tweaks = $global:importedconfigs.$name | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
$result = New-Object System.Collections.Generic.List[System.Object]
}

Context "Tweaks Configuration" {
It "Should have original values for all tweaks" {
$tweaks = $global:importedConfigs.tweaks
$tweaks | Should -Not -BeNullOrEmpty -Because "Tweaks configuration is missing or empty"

foreach ($tweak in $tweaks) {
$Originals = @(
@{
name = "registry"
value = "OriginalValue"
},
@{
name = "service"
value = "OriginalType"
},
@{
name = "ScheduledTask"
value = "OriginalState"
}
)
Foreach ($original in $Originals) {
$TotalCount = ($global:importedconfigs.$name.$tweak.$($original.name)).count
$OriginalCount = ($global:importedconfigs.$name.$tweak.$($original.name).$($original.value) | Where-Object {$_}).count
if($TotalCount -ne $OriginalCount) {
$result.Add("$Tweak,$($original.name)")
}
$originals = @{
registry = "OriginalValue"
service = "OriginalType"
ScheduledTask = "OriginalState"
}

foreach ($tweak in ($tweaks | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name)) {
foreach ($type in $originals.Keys) {
$totalCount = ($tweaks.$tweak.$type).Count
$originalCount = ($tweaks.$tweak.$type.$($originals[$type]) | Where-Object { $_ }).Count
$originalCount | Should -Be $totalCount -Because "Tweak '$tweak' of type '$type' is missing some original values"
if ($originalCount -ne $totalCount) {
Write-Verbose "Tweak '$tweak' of type '$type' has $originalCount original values out of $totalCount total values"
}
}
$result | Select-String "WPF*" | should -BeNullOrEmpty
}
}
}

Context "Applications Configuration" {
It "Should have all required fields for each application" {
$apps = $global:importedConfigs.applications
$apps | Should -Not -BeNullOrEmpty -Because "Applications configuration is missing or empty"

foreach ($app in ($apps | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name)) {
foreach ($field in $configTemplates.applications) {
$apps.$app.$field | Should -Not -BeNullOrEmpty -Because "Application '$app' is missing the '$field' field"
if (-not $apps.$app.$field) {
Write-Verbose "Application '$app' is missing the '$field' field"
}
}
}
}
}
}

# Summarize test results
$testResults = Invoke-Pester -PassThru
if ($testResults.FailedCount -gt 0) {
Write-Error "Tests failed. $($testResults.FailedCount) out of $($testResults.TotalCount) tests failed."
exit 1
} else {
Write-Output "All tests passed successfully!"
}
55 changes: 55 additions & 0 deletions schemas/config/applications.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"definitions": {
"url": {
"type": "string",
"format": "uri"
}
},
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
"type": "object",
"properties": {
"content": {
"type": "string"
},
"description": {
"type": "string"
},
"category": {
"type": "string",
"enum": [
"Utilities",
"Document",
"Pro Tools",
"Multimedia Tools",
"Development",
"Games",
"Microsoft Tools",
"Browsers",
"Communications"
]
},
"choco": {
"type": "string"
},
"winget": {
"type": "string"
},
"link": {
"$ref": "#/definitions/url"
}
},
"required": [
"content",
"description",
"category",
"link",
"choco",
"winget"
],
"additionalProperties": false
}
}
}
29 changes: 29 additions & 0 deletions schemas/config/dns.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
"type": "object",
"properties": {
"Primary": {
"type": "string",
"format": "ipv4"
},
"Secondary": {
"type": "string",
"format": "ipv4"
},
"Primary6": {
"type": "string",
"format": "ipv6"
},
"Secondary6": {
"type": "string",
"format": "ipv6"
}
},
"required": ["Primary", "Secondary", "Primary6", "Secondary6"],
"additionalProperties": false
}
}
}
57 changes: 57 additions & 0 deletions schemas/config/feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
"type": "object",
"properties": {
"Content": {
"type": "string"
},
"Description": {
"type": "string"
},
"category": {
"type": "string",
"enum": ["Features", "Fixes", "Legacy Windows Panels"]
},
"panel": {
"type": "string",
"pattern": "^\\d+$"
},
"Order": {
"type": "string",
"pattern": "^[0-9a-f]+_$"
},
"feature": {
"type": "array",
"items": {
"type": "string"
}
},
"InvokeScript": {
"type": "array",
"items": {
"type": "string"
}
},
"UndoScript": {
"type": "array",
"items": {
"type": "string"
}
},
"Type": {
"type": "string",
"enum": ["Button"]
},
"ButtonWidth": {
"type": "string",
"pattern": "^\\d+$"
}
},
"required": ["Content", "category", "panel"],
"additionalProperties": false
}
}
}
12 changes: 12 additions & 0 deletions schemas/config/preset.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"patternProperties": {
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
Loading