Standardizes driver model name handling across vendors

Improves consistency in how driver model names and identifiers are processed for different manufacturers (Microsoft, Dell, HP, Lenovo) throughout the codebase.

Introduces helper functions to extract base names from display names and construct standardized display names with identifiers in parentheses format.

Centralizes the logic for converting driver items to JSON model objects, eliminating code duplication across save and download operations.

Enhances validation during driver import and processing to skip invalid entries with missing required fields like MachineType for Lenovo or empty model names.

Ensures proper parsing of model names containing parenthetical identifiers (e.g., "Product Name (Type)") and extracts components correctly for each vendor's requirements.

Removes obsolete CabRelativePath property from Dell driver handling.
This commit is contained in:
rbalsleyMSFT
2025-11-14 15:23:52 -08:00
parent 11b3e120e2
commit 235065322c
2 changed files with 225 additions and 102 deletions
+64 -5
View File
@@ -4907,19 +4907,78 @@ if ($driversJsonPath -and (Test-Path $driversJsonPath) -and ($InstallDrivers -or
$makeName = $makeEntry.Name $makeName = $makeEntry.Name
if ($makeEntry.Value.PSObject.Properties['Models']) { if ($makeEntry.Value.PSObject.Properties['Models']) {
foreach ($modelEntry in $makeEntry.Value.Models) { foreach ($modelEntry in $makeEntry.Value.Models) {
# Construct the PSCustomObject exactly as the Save-*DriversTask functions expect $DriverItemData if ($null -eq $modelEntry -or -not $modelEntry.PSObject.Properties['Name']) {
WriteLog "Skipping model entry for Make '$makeName' due to missing Name."
continue
}
$modelName = $modelEntry.Name
if ([string]::IsNullOrWhiteSpace($modelName)) {
WriteLog "Skipping model entry for Make '$makeName' because Name is empty."
continue
}
$modelName = $modelName.Trim()
$driverItem = $null
switch ($makeName) {
'Microsoft' {
$driverItem = [PSCustomObject]@{ $driverItem = [PSCustomObject]@{
Make = $makeName Make = $makeName
Model = $modelEntry.Name # This is the display name, e.g., "Surface Book 3" or "Lenovo 500w (83LH)" Model = $modelName
Link = if ($modelEntry.PSObject.Properties['Link']) { $modelEntry.Link } else { $null } Link = if ($modelEntry.PSObject.Properties['Link']) { $modelEntry.Link } else { $null }
ProductName = if ($modelEntry.PSObject.Properties['ProductName']) { $modelEntry.ProductName } else { $null } # Specifically for Lenovo
MachineType = if ($modelEntry.PSObject.Properties['MachineType']) { $modelEntry.MachineType } else { $null } # Specifically for Lenovo
# Ensure all properties potentially accessed by any Save-*DriversTask via $DriverItemData are present
} }
}
'HP' {
$driverItem = [PSCustomObject]@{
Make = $makeName
Model = $modelName
}
}
'Lenovo' {
$machineType = if ($modelEntry.PSObject.Properties['MachineType']) { $modelEntry.MachineType } else { $null }
$productName = $modelName
if ([string]::IsNullOrWhiteSpace($machineType) -and $modelName -match '(.+?)\s*\((.+?)\)$') {
$productName = $matches[1].Trim()
$machineType = $matches[2].Trim()
}
if ([string]::IsNullOrWhiteSpace($machineType)) {
WriteLog "Skipping Lenovo model '$modelName' because MachineType is missing."
continue
}
$displayModel = "$productName ($machineType)"
$driverItem = [PSCustomObject]@{
Make = $makeName
Model = $displayModel
ProductName = $productName
MachineType = $machineType
}
}
'Dell' {
$systemId = if ($modelEntry.PSObject.Properties['SystemId']) { $modelEntry.SystemId } else { $null }
$baseName = $modelName
if ([string]::IsNullOrWhiteSpace($systemId) -and $modelName -match '(.+?)\s*\((.+?)\)$') {
$baseName = $matches[1].Trim()
$systemId = $matches[2].Trim()
}
$displayModel = if ([string]::IsNullOrWhiteSpace($systemId)) { $baseName } else { "$baseName ($systemId)" }
$driverItem = [PSCustomObject]@{
Make = $makeName
Model = $displayModel
SystemId = $systemId
CabUrl = if ($modelEntry.PSObject.Properties['CabUrl']) { $modelEntry.CabUrl } else { $null }
}
}
default {
WriteLog "Skipping unsupported Make '$makeName' in Drivers.json."
}
}
if ($null -ne $driverItem) {
$driversToProcess += $driverItem $driversToProcess += $driverItem
} }
} }
} }
}
if ($driversToProcess.Count -eq 0) { if ($driversToProcess.Count -eq 0) {
WriteLog "No drivers found to process in $driversJsonPath." WriteLog "No drivers found to process in $driversJsonPath."
+153 -89
View File
@@ -5,6 +5,91 @@
This module contains all the business logic for the 'Drivers' tab in the FFU Builder UI. It handles fetching driver model lists from various manufacturers (Microsoft, Dell, HP, Lenovo), displaying and filtering them in the UI, and managing the selection state. It also includes functions to import and export driver selections to a JSON file (Drivers.json) and to orchestrate the parallel download of selected driver packages using the common parallel processing module. This module contains all the business logic for the 'Drivers' tab in the FFU Builder UI. It handles fetching driver model lists from various manufacturers (Microsoft, Dell, HP, Lenovo), displaying and filtering them in the UI, and managing the selection state. It also includes functions to import and export driver selections to a JSON file (Drivers.json) and to orchestrate the parallel download of selected driver packages using the common parallel processing module.
#> #>
function ConvertTo-DriverBaseName {
param(
[string]$ModelString
)
if ([string]::IsNullOrWhiteSpace($ModelString)) {
return $ModelString
}
if ($ModelString -match '^(.*?)\s*\((.+)\)\s*$') {
return $matches[1].Trim()
}
return $ModelString.Trim()
}
function Get-DriverDisplayName {
param(
[string]$BaseName,
[string]$Identifier
)
if ([string]::IsNullOrWhiteSpace($BaseName)) {
return $Identifier
}
if ([string]::IsNullOrWhiteSpace($Identifier)) {
return $BaseName.Trim()
}
return "$($BaseName.Trim()) ($($Identifier.Trim()))"
}
function Convert-DriverItemToJsonModel {
param(
[Parameter(Mandatory = $true)]
[pscustomobject]$DriverItem
)
$makeName = $DriverItem.Make
switch ($makeName) {
'Microsoft' {
$modelObject = @{ Name = $DriverItem.Model }
if ($DriverItem.PSObject.Properties['Link'] -and -not [string]::IsNullOrWhiteSpace($DriverItem.Link)) {
$modelObject.Link = $DriverItem.Link
}
return $modelObject
}
'Dell' {
$systemId = if ($DriverItem.PSObject.Properties['SystemId']) { $DriverItem.SystemId } else { $null }
$baseName = ConvertTo-DriverBaseName -ModelString $DriverItem.Model
if ([string]::IsNullOrWhiteSpace($baseName)) {
$baseName = $DriverItem.Model
}
$modelObject = @{ Name = $baseName }
if (-not [string]::IsNullOrWhiteSpace($systemId)) {
$modelObject.SystemId = $systemId
}
if ($DriverItem.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($DriverItem.CabUrl)) {
$modelObject.CabUrl = $DriverItem.CabUrl
}
return $modelObject
}
'HP' {
return @{ Name = $DriverItem.Model }
}
'Lenovo' {
$machineType = $DriverItem.MachineType
$baseName = if ($DriverItem.ProductName) { $DriverItem.ProductName } else { ConvertTo-DriverBaseName -ModelString $DriverItem.Model }
if ([string]::IsNullOrWhiteSpace($baseName) -or [string]::IsNullOrWhiteSpace($machineType)) {
WriteLog "Skipping Lenovo driver '$($DriverItem.Model)' because Name or MachineType is missing."
return $null
}
return @{
Name = $baseName
MachineType = $machineType
}
}
default {
WriteLog "Convert-DriverItemToJsonModel: Unsupported Make '$makeName'."
return $null
}
}
}
# Helper function to get models for a selected Make and standardize them # Helper function to get models for a selected Make and standardize them
function Get-ModelsForMake { function Get-ModelsForMake {
param( param(
@@ -107,13 +192,11 @@ function ConvertTo-StandardizedDriverModel {
$dellModelNumber = $null $dellModelNumber = $null
$dellSystemId = $null $dellSystemId = $null
$dellCabUrl = $null $dellCabUrl = $null
$dellCabRelative = $null
if ($Make -eq 'Dell') { if ($Make -eq 'Dell') {
if ($RawDriverObject.PSObject.Properties['Brand']) { $dellBrand = $RawDriverObject.Brand } if ($RawDriverObject.PSObject.Properties['Brand']) { $dellBrand = $RawDriverObject.Brand }
if ($RawDriverObject.PSObject.Properties['ModelNumber']) { $dellModelNumber = $RawDriverObject.ModelNumber } if ($RawDriverObject.PSObject.Properties['ModelNumber']) { $dellModelNumber = $RawDriverObject.ModelNumber }
if ($RawDriverObject.PSObject.Properties['SystemId']) { $dellSystemId = $RawDriverObject.SystemId } if ($RawDriverObject.PSObject.Properties['SystemId']) { $dellSystemId = $RawDriverObject.SystemId }
if ($RawDriverObject.PSObject.Properties['CabUrl']) { $dellCabUrl = $RawDriverObject.CabUrl } if ($RawDriverObject.PSObject.Properties['CabUrl']) { $dellCabUrl = $RawDriverObject.CabUrl }
if ($RawDriverObject.PSObject.Properties['CabRelativePath']) { $dellCabRelative = $RawDriverObject.CabRelativePath }
} }
$output = [PSCustomObject]@{ $output = [PSCustomObject]@{
@@ -137,7 +220,6 @@ function ConvertTo-StandardizedDriverModel {
$output | Add-Member -NotePropertyName ModelNumber -NotePropertyValue $dellModelNumber $output | Add-Member -NotePropertyName ModelNumber -NotePropertyValue $dellModelNumber
$output | Add-Member -NotePropertyName SystemId -NotePropertyValue $dellSystemId $output | Add-Member -NotePropertyName SystemId -NotePropertyValue $dellSystemId
$output | Add-Member -NotePropertyName CabUrl -NotePropertyValue $dellCabUrl $output | Add-Member -NotePropertyName CabUrl -NotePropertyValue $dellCabUrl
$output | Add-Member -NotePropertyName CabRelativePath -NotePropertyValue $dellCabRelative
} }
return $output return $output
@@ -213,44 +295,7 @@ function Save-DriversJson {
$modelsForThisMake = @() # Initialize an array to hold model objects $modelsForThisMake = @() # Initialize an array to hold model objects
foreach ($driverItem in $_.Group) { foreach ($driverItem in $_.Group) {
$modelObject = $null $modelObject = Convert-DriverItemToJsonModel -DriverItem $driverItem
switch ($makeName) {
'Microsoft' {
$modelObject = @{
Name = $driverItem.Model # Model is the display name
Link = $driverItem.Link
}
}
'Dell' {
$modelObject = @{
Name = $driverItem.Model
}
if ($driverItem.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($driverItem.SystemId)) {
$modelObject.SystemId = $driverItem.SystemId
}
if ($driverItem.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabUrl)) {
$modelObject.CabUrl = $driverItem.CabUrl
}
if ($driverItem.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabRelativePath)) {
$modelObject.CabRelativePath = $driverItem.CabRelativePath
}
}
'HP' {
$modelObject = @{
Name = $driverItem.Model
}
}
'Lenovo' {
$modelObject = @{
Name = $driverItem.Model # This is "ProductName (MachineType)"
ProductName = $driverItem.ProductName # This is "ProductName"
MachineType = $driverItem.MachineType # This is "MachineType"
}
}
default {
WriteLog "Save-DriversJson: Unknown Make '$makeName' encountered for model '$($driverItem.Model)'. Skipping."
}
}
if ($null -ne $modelObject) { if ($null -ne $modelObject) {
$modelsForThisMake += $modelObject $modelsForThisMake += $modelObject
} }
@@ -341,6 +386,69 @@ function Import-DriversJson {
continue continue
} }
$normalizedName = $importedModelNameFromObject
$skipModel = $false
switch ($makeName) {
'Lenovo' {
$productName = if ($importedModelObject.PSObject.Properties['ProductName'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.ProductName)) { $importedModelObject.ProductName } else { ConvertTo-DriverBaseName -ModelString $normalizedName }
$machineType = if ($importedModelObject.PSObject.Properties['MachineType'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.MachineType)) { $importedModelObject.MachineType } else { $null }
if ([string]::IsNullOrWhiteSpace($machineType) -and $normalizedName -match '(.+?)\s*\((.+?)\)$') {
if ([string]::IsNullOrWhiteSpace($productName)) { $productName = $matches[1].Trim() }
$machineType = $matches[2].Trim()
}
if ([string]::IsNullOrWhiteSpace($productName) -or [string]::IsNullOrWhiteSpace($machineType)) {
WriteLog "Import-DriversJson: Skipping Lenovo model '$normalizedName' due to missing ProductName or MachineType."
$skipModel = $true
}
else {
$normalizedName = Get-DriverDisplayName -BaseName $productName -Identifier $machineType
if ($importedModelObject.PSObject.Properties['ProductName']) {
$importedModelObject.ProductName = $productName
}
else {
$importedModelObject | Add-Member -NotePropertyName ProductName -NotePropertyValue $productName
}
if ($importedModelObject.PSObject.Properties['MachineType']) {
$importedModelObject.MachineType = $machineType
}
else {
$importedModelObject | Add-Member -NotePropertyName MachineType -NotePropertyValue $machineType
}
}
}
'Dell' {
$baseName = ConvertTo-DriverBaseName -ModelString $normalizedName
if ([string]::IsNullOrWhiteSpace($baseName)) { $baseName = $normalizedName }
$systemId = if ($importedModelObject.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.SystemId)) { $importedModelObject.SystemId } else { $null }
if ([string]::IsNullOrWhiteSpace($systemId) -and $normalizedName -match '(.+?)\s*\((.+?)\)$') {
if ([string]::IsNullOrWhiteSpace($baseName)) { $baseName = $matches[1].Trim() }
$systemId = $matches[2].Trim()
}
$normalizedName = if ([string]::IsNullOrWhiteSpace($systemId)) { $baseName.Trim() } else { Get-DriverDisplayName -BaseName $baseName -Identifier $systemId }
if ($importedModelObject.PSObject.Properties['SystemId']) {
$importedModelObject.SystemId = $systemId
}
else {
$importedModelObject | Add-Member -NotePropertyName SystemId -NotePropertyValue $systemId
}
}
default {
$normalizedName = $normalizedName.Trim()
}
}
if ($skipModel) {
continue
}
if ([string]::IsNullOrWhiteSpace($normalizedName)) {
WriteLog "Import-DriversJson: Skipping normalized model name for Make '$makeName'."
continue
}
$importedModelObject.Name = $normalizedName
$importedModelNameFromObject = $normalizedName
$existingModel = $State.Data.allDriverModels | Where-Object { $_.Make -eq $makeName -and $_.Model -eq $importedModelNameFromObject } | Select-Object -First 1 $existingModel = $State.Data.allDriverModels | Where-Object { $_.Make -eq $makeName -and $_.Model -eq $importedModelNameFromObject } | Select-Object -First 1
if ($null -ne $existingModel) { if ($null -ne $existingModel) {
@@ -382,12 +490,6 @@ function Import-DriversJson {
WriteLog "Import-DriversJson: Updated CabUrl for existing Dell model '$($existingModel.Model)'." WriteLog "Import-DriversJson: Updated CabUrl for existing Dell model '$($existingModel.Model)'."
} }
} }
if ($importedModelObject.PSObject.Properties['CabRelativePath'] -and $existingModel.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabRelativePath)) {
if ($existingModel.CabRelativePath -ne $importedModelObject.CabRelativePath) {
$existingModel.CabRelativePath = $importedModelObject.CabRelativePath
WriteLog "Import-DriversJson: Updated CabRelativePath for existing Dell model '$($existingModel.Model)'."
}
}
} }
$existingModelsUpdated++ $existingModelsUpdated++
@@ -445,9 +547,6 @@ function Import-DriversJson {
if ($importedModelObject.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabUrl)) { if ($importedModelObject.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabUrl)) {
$newDriverModel | Add-Member -NotePropertyName CabUrl -NotePropertyValue $importedModelObject.CabUrl $newDriverModel | Add-Member -NotePropertyName CabUrl -NotePropertyValue $importedModelObject.CabUrl
} }
if ($importedModelObject.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($importedModelObject.CabRelativePath)) {
$newDriverModel | Add-Member -NotePropertyName CabRelativePath -NotePropertyValue $importedModelObject.CabRelativePath
}
} }
$State.Data.allDriverModels.Add($newDriverModel) $State.Data.allDriverModels.Add($newDriverModel)
$newModelsAdded++ $newModelsAdded++
@@ -807,51 +906,16 @@ function Invoke-DownloadSelectedDrivers {
$modelsForThisMake = @() # Initialize an array to hold model objects $modelsForThisMake = @() # Initialize an array to hold model objects
foreach ($driverItem in $_.Group) { foreach ($driverItem in $_.Group) {
$modelObject = $null $modelObject = Convert-DriverItemToJsonModel -DriverItem $driverItem
switch ($makeName) {
'Microsoft' {
$modelObject = @{
Name = $driverItem.Model # Model is the display name
Link = $driverItem.Link
}
}
'Dell' {
$modelObject = @{
Name = $driverItem.Model
}
if ($driverItem.PSObject.Properties['SystemId'] -and -not [string]::IsNullOrWhiteSpace($driverItem.SystemId)) {
$modelObject.SystemId = $driverItem.SystemId
}
if ($driverItem.PSObject.Properties['CabUrl'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabUrl)) {
$modelObject.CabUrl = $driverItem.CabUrl
}
if ($driverItem.PSObject.Properties['CabRelativePath'] -and -not [string]::IsNullOrWhiteSpace($driverItem.CabRelativePath)) {
$modelObject.CabRelativePath = $driverItem.CabRelativePath
}
}
'HP' {
$modelObject = @{
Name = $driverItem.Model
}
}
'Lenovo' {
$modelObject = @{
Name = $driverItem.Model
ProductName = $driverItem.ProductName
MachineType = $driverItem.MachineType
}
}
default {
WriteLog "Auto-Save Drivers.json: Unrecognized Make '$makeName' for driver '$($driverItem.Model)'. Skipping."
}
}
if ($null -ne $modelObject) { if ($null -ne $modelObject) {
$modelsForThisMake += $modelObject $modelsForThisMake += $modelObject
} }
} }
# Add the models array to the make-specific object
if ($modelsForThisMake.Count -gt 0) {
$outputJson[$makeName] = @{ Models = $modelsForThisMake } $outputJson[$makeName] = @{ Models = $modelsForThisMake }
} }
}
# Ensure directory exists # Ensure directory exists
$parentDir = Split-Path -Path $driversJsonPath -Parent $parentDir = Split-Path -Path $driversJsonPath -Parent