...

Text file src/github.com/Azure/azure-sdk-for-go/eng/common/scripts/Update-DocsMsToc.ps1

Documentation: github.com/Azure/azure-sdk-for-go/eng/common/scripts

     1<#
     2.SYNOPSIS
     3Update unified ToC file for publishing reference docs on docs.microsoft.com
     4
     5.DESCRIPTION
     6Given a doc repo location and a location to output the ToC generate a Unified
     7Table of Contents:
     8
     9* Get list of packages onboarded to docs.microsoft.com (domain specific)
    10* Get metadata for onboarded packages from metadata CSV
    11* Build a sorted list of services
    12* Add ToC nodes for the service
    13* Add "Core" packages to the bottom of the ToC under "Other"
    14
    15ToC node layout:
    16* Service (service level overview page)
    17  * Client Package 1 (package level overview page)
    18  * Client Package 2 (package level overview page)
    19  ...
    20  * Management
    21    * Management Package 1
    22    * Management Package 2
    23    ...
    24
    25.PARAMETER DocRepoLocation
    26Location of the documentation repo. This repo may be sparsely checked out
    27depending on the requirements for the domain
    28
    29.PARAMETER OutputLocation
    30Output location for unified reference yml file
    31
    32#>
    33
    34param(
    35  [Parameter(Mandatory = $true)]
    36  [string] $DocRepoLocation,
    37
    38  [Parameter(Mandatory = $true)]
    39  [string] $OutputLocation
    40)
    41. $PSScriptRoot/common.ps1
    42. $PSScriptRoot/Helpers/PSModule-Helpers.ps1
    43
    44Install-ModuleIfNotInstalled "powershell-yaml" "0.4.1" | Import-Module
    45
    46Set-StrictMode -Version 3
    47
    48function GetClientPackageNode($clientPackage) {
    49  $packageInfo = &$GetDocsMsTocDataFn `
    50    -packageMetadata $clientPackage `
    51    -docRepoLocation $DocRepoLocation
    52
    53  return [PSCustomObject]@{
    54    name     = $packageInfo.PackageTocHeader
    55    href     = $packageInfo.PackageLevelReadmeHref
    56    # This is always one package and it must be an array
    57    children = $packageInfo.TocChildren
    58  };
    59}
    60
    61function GetPackageKey($pkg) {
    62  $pkgKey = $pkg.Package
    63  $groupId = $null
    64
    65  if ($pkg.PSObject.Members.Name -contains "GroupId") {
    66    $groupId = $pkg.GroupId
    67  }
    68
    69  if ($groupId) {
    70    $pkgKey = "${groupId}:${pkgKey}"
    71  }
    72
    73  return $pkgKey
    74}
    75
    76function GetPackageLookup($packageList) {
    77  $packageLookup = @{}
    78
    79  foreach ($pkg in $packageList) {
    80    $pkgKey = GetPackageKey $pkg
    81
    82    # We want to prefer updating non-hidden packages but if there is only
    83    # a hidden entry then we will return that
    84    if (!$packageLookup.ContainsKey($pkgKey) -or $packageLookup[$pkgKey].Hide -eq "true") {
    85      $packageLookup[$pkgKey] = $pkg
    86    }
    87    else {
    88      # Warn if there are more then one non-hidden package
    89      if ($pkg.Hide -ne "true") {
    90        Write-Host "Found more than one package entry for $($pkg.Package) selecting the first non-hidden one."
    91      }
    92    }
    93  }
    94
    95  return $packageLookup
    96}
    97
    98$onboardedPackages = &$GetOnboardedDocsMsPackagesFn `
    99  -DocRepoLocation $DocRepoLocation
   100
   101# This criteria is different from criteria used in `Update-DocsMsPackages.ps1`
   102# because we need to generate ToCs for packages which are not necessarily "New"
   103# in the metadata AND onboard legacy packages (which `Update-DocsMsPackages.ps1`
   104# does not do)
   105$fullMetadata = Get-CSVMetadata
   106$metadata = @()
   107foreach($metadataEntry in $fullMetadata) {
   108  if ($metadataEntry.Package -and $metadataEntry.Hide -ne 'true') {
   109    $pkgKey = GetPackageKey $metadataEntry
   110    if($onboardedPackages.ContainsKey($pkgKey)) {
   111      $metadata += $metadataEntry
   112    }
   113  }
   114}
   115
   116$fileMetadata = @()
   117foreach ($metadataFile in Get-ChildItem "$DocRepoLocation/metadata/*/*.json" -Recurse) {
   118  $fileContent = Get-Content $metadataFile -Raw
   119  $metadataEntry = ConvertFrom-Json $fileContent
   120
   121  if ($metadataEntry) {
   122    $fileMetadata += $metadataEntry
   123  }
   124}
   125
   126# Add file metadata information to package metadata from metadata CSV. Because
   127# metadata can exist for packages in both preview and GA there may be more than
   128# one file metadata entry. If that is the case keep the first entry found. We
   129# only use the `DirectoryPath` property from the json file metadata at this time
   130for ($i = 0; $i -lt $metadata.Count; $i++) {
   131  foreach ($fileEntry in $fileMetadata) {
   132    if ($fileEntry.Name -eq $metadata[$i].Package) {
   133      if ($metadata[$i].PSObject.Members.Name -contains "FileMetadata") {
   134        Write-Host "File metadata already added for $($metadata[$i].Package). Keeping the first entry found."
   135        continue
   136      }
   137      if (!($metadata[$i].PSObject.Members.Name -contains "GroupId") -or ($fileEntry.Group -eq $metadata[$i].GroupId)) {
   138        Add-Member `
   139          -InputObject $metadata[$i] `
   140          -MemberType NoteProperty `
   141          -Name FileMetadata `
   142          -Value $fileEntry
   143      }
   144    }
   145  }
   146}
   147
   148$packagesForToc = @{}
   149$allPackages = GetPackageLookup $metadata
   150foreach ($metadataKey in $allPackages.Keys) {
   151  $metadataEntry = $allPackages[$metadataKey]
   152  if (!$metadataEntry.ServiceName) {
   153    LogWarning "Empty ServiceName for package `"$metadataKey`". Skipping."
   154    continue
   155  }
   156  $packagesForToc[$metadataKey] = $metadataEntry
   157}
   158
   159# Get unique service names and sort alphabetically to act as the service nodes
   160# in the ToC
   161$services = @{}
   162foreach ($package in $packagesForToc.Values) {
   163  if ($package.ServiceName -eq 'Other') {
   164    # Skip packages under the service category "Other". Those will be handled
   165    # later
   166    continue
   167  }
   168  if (!$services.ContainsKey($package.ServiceName)) {
   169    $services[$package.ServiceName] = $true
   170  }
   171}
   172$serviceNameList = $services.Keys | Sort-Object
   173
   174$toc = @()
   175foreach ($service in $serviceNameList) {
   176  Write-Host "Building service: $service"
   177
   178  $packageItems = @()
   179
   180  # Client packages get individual entries
   181  $clientPackages = $packagesForToc.Values.Where({ $_.ServiceName -eq $service -and ('client' -eq $_.Type) })
   182  $clientPackages = $clientPackages | Sort-Object -Property Package
   183  foreach ($clientPackage in $clientPackages) {
   184    $packageItems += GetClientPackageNode -clientPackage $clientPackage
   185  }
   186
   187  # All management packages go under a single `Management` header in the ToC
   188  $mgmtPackages = $packagesForToc.Values.Where({ $_.ServiceName -eq $service -and ('mgmt' -eq $_.Type) })
   189  $mgmtPackages = $mgmtPackages | Sort-Object -Property Package
   190  if ($mgmtPackages) {
   191    $children = &$GetDocsMsTocChildrenForManagementPackagesFn `
   192      -packageMetadata $mgmtPackages `
   193      -docRepoLocation $DocRepoLocation
   194
   195    $packageItems += [PSCustomObject]@{
   196      name     = 'Management'
   197      # There could be multiple packages, ensure this is treated as an array
   198      # even if it is a single package
   199      children = @($children)
   200    }
   201  }
   202
   203  $uncategorizedPackages = $packagesForToc.Values.Where({ $_.ServiceName -eq $service -and !(@('client', 'mgmt') -contains $_.Type) })
   204  if ($uncategorizedPackages) {
   205    foreach ($package in $uncategorizedPackages) {
   206      LogWarning "Uncategorized package for service: $service - $($package.Package). Package not onboarded."
   207    }
   208  }
   209
   210  $serviceReadmeBaseName = $service.ToLower().Replace(' ', '-').Replace('/', '-')
   211  $serviceTocEntry = [PSCustomObject]@{
   212    name            = $service;
   213    href            = "~/docs-ref-services/{moniker}/$serviceReadmeBaseName.md"
   214    landingPageType = 'Service'
   215    items           = @($packageItems)
   216  }
   217  $toc += $serviceTocEntry
   218}
   219
   220# Core packages belong under the "Other" node in the ToC
   221$otherPackageItems = New-Object -TypeName System.Collections.Generic.List[PSCustomObject]
   222$otherPackages = $packagesForToc.Values.Where({ $_.ServiceName -eq 'Other' })
   223$otherPackages = $otherPackages | Sort-Object -Property DisplayName
   224
   225if ($otherPackages) {
   226  foreach ($otherPackage in $otherPackages) {
   227    $segments = $otherPackage.DisplayName.Split('-').ForEach({ $_.Trim() })
   228
   229
   230    if ($segments.Count -gt 1) {
   231      $currentNode = $otherPackageItems
   232
   233      # Iterate up to the penultimate item in the array so that the final item
   234      # in the array can be added as a leaf node. Since the array always has at
   235      # least two elements this iteration will cover at least the first element.
   236      # e.g. @(0, 1)[0..0] => 0
   237      foreach ($segment in $segments[0..($segments.Count - 2)]) {
   238        $matchingNode = $currentNode.Where({ $_.name -eq $segment })
   239
   240        # ToC nodes can be "branches" which contain 0 or more branch
   241        # or leaf nodes in an "items" field OR they can be leaf nodes which have
   242        # a "children" field which can only contain package names or namespaces.
   243        # A node cannot contain both "items" and "children". If a node already
   244        # has a "children" field then it is a leaf node and cannot take
   245        # additional branch nodes.
   246        # Children are added using the `GetClientPackageNode` function
   247        if ($matchingNode -and $matchingNode.PSObject.Members.Name -contains "children") {
   248          LogWarning "Cannot create nested entry for package $($otherPackage.Package) because Segment `"$segment`" in the DisplayName $($otherPackage.DisplayName) is already a leaf node. Excluding package: $($otherPackage.Package)"
   249          $currentNode = $null
   250          break
   251        }
   252
   253        if ($matchingNode) {
   254          $currentNode = $matchingNode[0].items
   255        }
   256        else {
   257          $newNode = [PSCustomObject]@{
   258            name            = $segment
   259            landingPageType = 'Service'
   260            items           = New-Object -TypeName System.Collections.Generic.List[PSCustomObject]
   261          }
   262          $currentNode.Add($newNode)
   263          $currentNode = $newNode.items
   264        }
   265      }
   266
   267      if ($null -ne $currentNode) {
   268        $otherPackage.DisplayName = $segments[$segments.Count - 1]
   269        $currentNode.Add((GetClientPackageNode $otherPackage))
   270      }
   271
   272    }
   273    else {
   274      $otherPackageItems.Add((GetClientPackageNode $otherPackage))
   275    }
   276  }
   277}
   278$toc += [PSCustomObject]@{
   279  name            = 'Other';
   280  landingPageType = 'Service';
   281  items           = $otherPackageItems + @(
   282    [PSCustomObject]@{
   283      name            = "Uncategorized Packages";
   284      landingPageType = 'Service';
   285      # All onboarded packages which have not been placed in the ToC will be
   286      # handled by the docs system here. In this case the list would consist of
   287      # packages whose ServiceName field is empty in the metadata.
   288      children        = @('**');
   289    }
   290  )
   291}
   292
   293$output = @([PSCustomObject]@{
   294    name            = 'Reference';
   295    landingPageType = 'Root';
   296    expanded        = $false;
   297    items           = $toc
   298  })
   299
   300if (Test-Path "Function:$UpdateDocsMsTocFn") {
   301  $output = &$UpdateDocsMsTocFn -toc $output
   302}
   303
   304$outputYaml = ConvertTo-Yaml $output
   305Set-Content -Path $OutputLocation -Value $outputYaml

View as plain text