Skip to content

Commit 2fc5728

Browse files
committed
breakout into pkg
1 parent d286326 commit 2fc5728

File tree

2 files changed

+268
-238
lines changed

2 files changed

+268
-238
lines changed

tools/resourcedocsgen/cmd/docs/registry.go

Lines changed: 12 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,9 @@ import (
2121
"io"
2222
"net/http"
2323
"net/url"
24-
"os"
2524
"path/filepath"
2625
"runtime"
27-
"slices"
2826
"strings"
29-
"time"
3027

3128
"github.com/spf13/cobra"
3229

@@ -39,78 +36,10 @@ import (
3936
pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema"
4037
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
4138
"github.com/pulumi/registry/tools/resourcedocsgen/pkg"
39+
"github.com/pulumi/registry/tools/resourcedocsgen/pkg/registry/svc"
4240
concpool "github.com/sourcegraph/conc/pool"
4341
)
4442

45-
// PackageMetadataProvider is an interface for providers that retrieve package metadata
46-
// from either the filesystem directory or the Pulumi Registry API.
47-
type PackageMetadataProvider interface {
48-
// GetPackageMetadata returns metadata for a specific package
49-
GetPackageMetadata(ctx context.Context, pkgName string) (pkg.PackageMeta, error)
50-
// ListPackageMetadata returns metadata for all packages
51-
ListPackageMetadata(ctx context.Context) ([]pkg.PackageMeta, error)
52-
}
53-
54-
// FileSystemProvider implements PackageMetadataProvider using the local yaml data files
55-
// in the pulumi/registry repository.
56-
type fileSystemProvider struct {
57-
registryDir string
58-
}
59-
60-
// RegistryAPIProvider implements PackageMetadataProvider using the Pulumi API
61-
// to retrieve package metadata.
62-
type registryAPIProvider struct {
63-
apiURL string
64-
}
65-
66-
// PackageMetadata represents the API response structure for package metadata
67-
// from the Pulumi Registry API.
68-
// TODO: will eventually be available in the "github.com/pulumi/pulumi/sdk/v3/go/common/apitype" package.
69-
type PackageMetadata struct {
70-
Name string `json:"name"`
71-
Publisher string `json:"publisher"`
72-
Source string `json:"source"`
73-
Version string `json:"version"`
74-
Title string `json:"title,omitempty"`
75-
Description string `json:"description,omitempty"`
76-
LogoURL string `json:"logoUrl,omitempty"`
77-
RepoURL string `json:"repoUrl,omitempty"`
78-
Category string `json:"category,omitempty"`
79-
IsFeatured bool `json:"isFeatured"`
80-
PackageTypes []string `json:"packageTypes,omitempty"`
81-
PackageStatus string `json:"packageStatus"`
82-
SchemaURL string `json:"schemaURL"`
83-
CreatedAt time.Time `json:"createdAt"`
84-
}
85-
86-
// PackageListResponse represents the API response structure for package lists
87-
type PackageListResponse struct {
88-
Packages []PackageMetadata `json:"packages"`
89-
}
90-
91-
// NewFileSystemProvider creates a new PackageMetadataProvider that reads from the filesystem
92-
func NewFileSystemProvider(registryDir string) PackageMetadataProvider {
93-
return &fileSystemProvider{
94-
registryDir: registryDir,
95-
}
96-
}
97-
98-
// NewAPIProvider creates a new PackageMetadataProvider that reads from the Pulumi API
99-
func NewAPIProvider(apiURL string) PackageMetadataProvider {
100-
return &registryAPIProvider{
101-
apiURL: apiURL,
102-
}
103-
}
104-
105-
func getRepoSlug(repoURL string) (string, error) {
106-
u, err := url.Parse(repoURL)
107-
if err != nil {
108-
return "", errors.Wrapf(err, "parsing repo url %s", repoURL)
109-
}
110-
111-
return u.Path, nil
112-
}
113-
11443
func genResourceDocsForPackageFromRegistryMetadata(
11544
metadata pkg.PackageMeta, docsOutDir, packageTreeJSONOutDir string,
11645
) error {
@@ -195,12 +124,17 @@ func getSchemaFileURL(metadata pkg.PackageMeta) (string, error) {
195124
return fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s", repoSlug, metadata.Version, schemaFilePath), nil
196125
}
197126

198-
func getRegistryPackagesPath(repoPath string) string {
199-
return filepath.Join(repoPath, "themes", "default", "data", "registry", "packages")
127+
func getRepoSlug(repoURL string) (string, error) {
128+
u, err := url.Parse(repoURL)
129+
if err != nil {
130+
return "", errors.Wrapf(err, "parsing repo url %s", repoURL)
131+
}
132+
133+
return u.Path, nil
200134
}
201135

202136
func genResourceDocsForAllRegistryPackages(
203-
provider PackageMetadataProvider,
137+
provider svc.PackageMetadataProvider,
204138
baseDocsOutDir, basePackageTreeJSONOutDir string,
205139
) error {
206140
ctx := context.Background()
@@ -226,25 +160,6 @@ func genResourceDocsForAllRegistryPackages(
226160
return pool.Wait()
227161
}
228162

229-
func convertAPIPackageToPackageMeta(apiPkg PackageMetadata) (*pkg.PackageMeta, error) {
230-
return &pkg.PackageMeta{
231-
Name: apiPkg.Name,
232-
Publisher: apiPkg.Publisher,
233-
Description: apiPkg.Description,
234-
LogoURL: apiPkg.LogoURL,
235-
RepoURL: apiPkg.RepoURL,
236-
Category: pkg.PackageCategory(apiPkg.Category),
237-
Featured: apiPkg.IsFeatured,
238-
Native: slices.Contains(apiPkg.PackageTypes, "native"),
239-
Component: slices.Contains(apiPkg.PackageTypes, "component"),
240-
PackageStatus: pkg.PackageStatus(apiPkg.PackageStatus),
241-
SchemaFileURL: apiPkg.SchemaURL,
242-
Version: apiPkg.Version,
243-
Title: apiPkg.Title,
244-
UpdatedOn: apiPkg.CreatedAt.Unix(),
245-
}, nil
246-
}
247-
248163
func resourceDocsFromRegistryCmd() *cobra.Command {
249164
var baseDocsOutDir string
250165
var basePackageTreeJSONOutDir string
@@ -259,11 +174,11 @@ func resourceDocsFromRegistryCmd() *cobra.Command {
259174
"Pass a package name in the registry as an optional arg to generate docs only for that package.",
260175
RunE: func(cmd *cobra.Command, args []string) error {
261176
ctx := cmd.Context()
262-
var provider PackageMetadataProvider
177+
var provider svc.PackageMetadataProvider
263178
if useAPI {
264-
provider = NewAPIProvider(apiURL)
179+
provider = svc.NewAPIProvider(apiURL)
265180
} else {
266-
provider = NewFileSystemProvider(registryDir)
181+
provider = svc.NewFileSystemProvider(registryDir)
267182
}
268183

269184
if len(args) > 0 {
@@ -307,144 +222,3 @@ func resourceDocsFromRegistryCmd() *cobra.Command {
307222

308223
return cmd
309224
}
310-
311-
// GetPackageMetadata implements PackageMetadataProvider for fileSystemProvider
312-
func (p *fileSystemProvider) GetPackageMetadata(ctx context.Context, pkgName string) (pkg.PackageMeta, error) {
313-
metadataFilePath := filepath.Join(getRegistryPackagesPath(p.registryDir), pkgName+".yaml")
314-
b, err := os.ReadFile(metadataFilePath)
315-
if err != nil {
316-
return pkg.PackageMeta{}, errors.Wrapf(err, "reading the metadata file %s", metadataFilePath)
317-
}
318-
319-
var metadata pkg.PackageMeta
320-
if err := yaml.Unmarshal(b, &metadata); err != nil {
321-
return pkg.PackageMeta{}, errors.Wrapf(err, "unmarshalling the metadata file %s", metadataFilePath)
322-
}
323-
324-
return metadata, nil
325-
}
326-
327-
// ListPackageMetadata implements PackageMetadataProvider for fileSystemProvider
328-
func (p *fileSystemProvider) ListPackageMetadata(ctx context.Context) ([]pkg.PackageMeta, error) {
329-
registryPackagesPath := getRegistryPackagesPath(p.registryDir)
330-
files, err := os.ReadDir(registryPackagesPath)
331-
if err != nil {
332-
return nil, errors.Wrapf(err, "reading directory %s", registryPackagesPath)
333-
}
334-
335-
// Count YAML files to pre-allocate the slice mostly to appease the linter.
336-
var metadataCount int
337-
for _, file := range files {
338-
if strings.HasSuffix(file.Name(), ".yaml") {
339-
metadataCount++
340-
}
341-
}
342-
343-
metadataList := make([]pkg.PackageMeta, 0, metadataCount)
344-
for _, file := range files {
345-
if !strings.HasSuffix(file.Name(), ".yaml") {
346-
continue
347-
}
348-
349-
metadata, err := p.GetPackageMetadata(ctx, strings.TrimSuffix(file.Name(), ".yaml"))
350-
if err != nil {
351-
return nil, err
352-
}
353-
metadataList = append(metadataList, metadata)
354-
}
355-
356-
return metadataList, nil
357-
}
358-
359-
// GetPackageMetadata implements PackageMetadataProvider for registryAPIProvider
360-
func (p *registryAPIProvider) GetPackageMetadata(ctx context.Context, pkgName string) (pkg.PackageMeta, error) {
361-
req, err := http.NewRequestWithContext(ctx, http.MethodGet,
362-
fmt.Sprintf("%s/packages?name=%s", p.apiURL, pkgName), nil)
363-
if err != nil {
364-
return pkg.PackageMeta{}, errors.Wrapf(err, "creating request for package %s", pkgName)
365-
}
366-
367-
resp, err := http.DefaultClient.Do(req)
368-
if err != nil {
369-
return pkg.PackageMeta{}, errors.Wrapf(err, "fetching package metadata from API for %s", pkgName)
370-
}
371-
defer resp.Body.Close()
372-
373-
if resp.StatusCode != http.StatusOK {
374-
return pkg.PackageMeta{}, errors.Errorf("unexpected status code %d when fetching package metadata", resp.StatusCode)
375-
}
376-
377-
var response PackageListResponse
378-
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
379-
return pkg.PackageMeta{}, errors.Wrap(err, "decoding API response")
380-
}
381-
382-
switch len(response.Packages) {
383-
case 0:
384-
return pkg.PackageMeta{}, errors.Errorf("no package found with name %s", pkgName)
385-
case 1:
386-
metadata, err := convertAPIPackageToPackageMeta(response.Packages[0])
387-
if err != nil {
388-
return pkg.PackageMeta{}, err
389-
}
390-
return *metadata, nil
391-
default:
392-
return pkg.PackageMeta{}, errors.Errorf("multiple packages found with name %s", pkgName)
393-
}
394-
}
395-
396-
// ListPackageMetadata implements PackageMetadataProvider for registryAPIProvider
397-
func (p *registryAPIProvider) ListPackageMetadata(ctx context.Context) ([]pkg.PackageMeta, error) {
398-
var allPackages []pkg.PackageMeta
399-
// Maximum allowed by the API (must be less than 500). Request up to 499 to account for pagination
400-
// with minimum number of requests.
401-
const limit = 499
402-
continuationToken := ""
403-
404-
for {
405-
url := fmt.Sprintf("%s/packages?limit=%d", p.apiURL, limit)
406-
if continuationToken != "" {
407-
url = fmt.Sprintf("%s&continuationToken=%s", url, continuationToken)
408-
}
409-
410-
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
411-
if err != nil {
412-
return nil, errors.Wrap(err, "creating request for package list")
413-
}
414-
415-
resp, err := http.DefaultClient.Do(req)
416-
if err != nil {
417-
return nil, errors.Wrap(err, "fetching package list from API")
418-
}
419-
defer resp.Body.Close()
420-
421-
if resp.StatusCode != http.StatusOK {
422-
return nil, errors.Errorf("unexpected status code %d when fetching package list", resp.StatusCode)
423-
}
424-
425-
var response struct {
426-
Packages []PackageMetadata `json:"packages"`
427-
ContinuationToken *string `json:"continuationToken"`
428-
}
429-
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
430-
return nil, errors.Wrap(err, "decoding API response")
431-
}
432-
433-
for _, apiPkg := range response.Packages {
434-
metadata, err := convertAPIPackageToPackageMeta(apiPkg)
435-
if err != nil {
436-
return nil, err
437-
}
438-
allPackages = append(allPackages, *metadata)
439-
}
440-
441-
// If there's no continuation token, we've reached the end
442-
if response.ContinuationToken == nil {
443-
break
444-
}
445-
446-
continuationToken = *response.ContinuationToken
447-
}
448-
449-
return allPackages, nil
450-
}

0 commit comments

Comments
 (0)