server/pkg/gendocs/gendocs.go

Summary

Maintainability
A
40 mins
Test Coverage
package gendocs

import (
    "context"
    "fmt"

    "github.com/hashicorp/vault/sdk/framework"
    "github.com/hashicorp/vault/sdk/logical"
)

func GeneratePagesForBackend(ctx context.Context, pagesGenerator PagesGenerator, backend BackendHandle) error {
    var formatPathLink func(string) string
    if pagesGenerator.HasFormatPathLink() {
        formatPathLink = pagesGenerator.FormatPathLink
    }

    pages, err := GetBackendReferencePages(ctx, backend, formatPathLink)
    if err != nil {
        return fmt.Errorf("unable to generate backend pages: %w", err)
    }

    return GeneratePages(pagesGenerator, pages)
}

func GeneratePages(pagesGenerator PagesGenerator, pages []*BackendReferencePage) error {
    for _, page := range pages {
        if err := pagesGenerator.HandlePath(page.Path, page.Doc); err != nil {
            return fmt.Errorf("unable to handle path pattern %q: %w", page.Path, err)
        }
    }

    if err := pagesGenerator.Close(); err != nil {
        return fmt.Errorf("unable to close pages generator: %w", err)
    }

    return nil
}

type BackendReferencePage struct {
    Path string
    Doc  []byte
}

type BackendHandle struct {
    LogicalBackendRef   logical.Backend
    FrameworkBackendRef *framework.Backend
    Storage             logical.Storage
}

func NewBackendHandle(logicalBackendRef logical.Backend, frameworkBackendRef *framework.Backend, storage logical.Storage) BackendHandle {
    return BackendHandle{LogicalBackendRef: logicalBackendRef, FrameworkBackendRef: frameworkBackendRef, Storage: storage}
}

func GetBackendReferencePages(ctx context.Context, backend BackendHandle, formatPathLink func(markdownPagePath string) string) ([]*BackendReferencePage, error) {
    req := &logical.Request{
        Operation:  logical.HelpOperation,
        Storage:    backend.Storage,
        Connection: &logical.Connection{},
    }

    resp, err := backend.LogicalBackendRef.HandleRequest(ctx, req)
    if err != nil {
        return nil, err
    }

    var res []*BackendReferencePage

    var backendDoc *framework.OASDocument
    switch v := resp.Data["openapi"].(type) {
    case *framework.OASDocument:
        backendDoc = v
    case map[string]interface{}:
        backendDoc, err = framework.NewOASDocumentFromMap(v)
        if err != nil {
            return nil, fmt.Errorf("unable to parse openapi docs from backend help response: %w", err)
        }
    default:
        return nil, fmt.Errorf("no openapi backend docs found")
    }

    backendTemplateData, err := NewBackendTemplateData(backendDoc, backend.FrameworkBackendRef, formatPathLink)
    if err != nil {
        return nil, fmt.Errorf("unable to prepare backend template data: %w", err)
    }

    overview, err := ExecuteTemplate(BackendOverviewTemplate, backendTemplateData)
    if err != nil {
        return nil, fmt.Errorf("error executing backend overview template: %w", err)
    }

    res = append(res, &BackendReferencePage{
        Path: "/",
        Doc:  []byte(overview),
    })

    for _, pathTemplateData := range backendTemplateData.Paths {
        pathPage, err := ExecuteTemplate(PathTemplate, pathTemplateData)
        if err != nil {
            return nil, fmt.Errorf("error executing path %q template: %w", pathTemplateData.Name, err)
        }

        res = append(res, &BackendReferencePage{
            Path: pathTemplateData.Name,
            Doc:  []byte(pathPage),
        })
    }

    return res, nil
}