Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
108 changes: 69 additions & 39 deletions pkg/cmd/generate/clients/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type ExternalDocs struct {
type OperationData struct {
ACL string
APIName string
Beta bool
CodeSamples []CodeSample
Deprecated bool
Description string
Expand Down Expand Up @@ -152,8 +153,6 @@ func runCommand(opts *Options, printer *output.Printer) error {
}

// getAPIData reads the OpenAPI spec and parses the operation data.
//
//nolint:funlen
func getAPIData(
doc *libopenapi.DocumentModel[v3.Document],
opts *Options,
Expand All @@ -162,6 +161,11 @@ func getAPIData(

count := 0

beta, err := utils.IsBetaAPI(&doc.Model)
if err != nil {
return nil, fmt.Errorf("get beta status: %w", err)
}

prefix := fmt.Sprintf("%s/%s", opts.OutputDirectory, opts.APIName)

for pathPairs := doc.Model.Paths.PathItems.First(); pathPairs != nil; pathPairs = pathPairs.Next() {
Expand All @@ -174,44 +178,16 @@ func getAPIData(
pathItem := pathPairs.Value()

for opPairs := pathItem.GetOperations().First(); opPairs != nil; opPairs = opPairs.Next() {
op := opPairs.Value()

acl, err := utils.GetACL(op)
data, err := buildOperationData(
opPairs.Key(),
pathName,
opPairs.Value(),
opts,
prefix,
beta,
)
if err != nil {
return nil, fmt.Errorf("get ACL for %s %s: %w", opPairs.Key(), pathName, err)
}

short, long := utils.SplitDescription(op.Description)
short = utils.StripMarkdown(short)

data := OperationData{
ACL: utils.AclToString(acl),
APIName: opts.APIName,
CodeSamples: getCodeSamples(op),
Deprecated: boolOrFalse(op.Deprecated),
Description: long,
OutputFilename: utils.GetOutputFilename(op),
OutputPath: prefix,
OperationIDKebab: utils.ToKebabCase(op.OperationId),
Params: getParameters(op),
RequiresAdmin: false,
RequestBody: getRequestBody(op),
ShortDescription: short,
Summary: op.Summary,
}

if data.ACL == "`admin`" {
data.RequiresAdmin = true
}

if op.ExternalDocs != nil {
desc := strings.TrimSpace(op.ExternalDocs.Description)
data.ExternalDocs.Description = strings.TrimSuffix(desc, ".")
data.ExternalDocs.URL = op.ExternalDocs.URL
}

if data.ExternalDocs.Description != "" && data.ExternalDocs.URL != "" {
data.SeeAlso = true
return nil, err
}

result = append(result, data)
Expand All @@ -222,6 +198,60 @@ func getAPIData(
return result, nil
}

func buildOperationData(
verb, pathName string,
op *v3.Operation,
opts *Options,
prefix string,
beta bool,
) (OperationData, error) {
opBeta, err := utils.IsBetaOperation(op)
if err != nil {
return OperationData{}, fmt.Errorf("get beta status for %s %s: %w", verb, pathName, err)
}

acl, err := utils.GetACL(op)
if err != nil {
return OperationData{}, fmt.Errorf("get ACL for %s %s: %w", verb, pathName, err)
}

short, long := utils.SplitDescription(op.Description)
short = utils.StripMarkdown(short)

data := OperationData{
ACL: utils.AclToString(acl),
APIName: opts.APIName,
Beta: beta || opBeta,
CodeSamples: getCodeSamples(op),
Deprecated: boolOrFalse(op.Deprecated),
Description: long,
OutputFilename: utils.GetOutputFilename(op),
OutputPath: prefix,
OperationIDKebab: utils.ToKebabCase(op.OperationId),
Params: getParameters(op),
RequiresAdmin: false,
RequestBody: getRequestBody(op),
ShortDescription: short,
Summary: op.Summary,
}

if data.ACL == "`admin`" {
data.RequiresAdmin = true
}

if op.ExternalDocs != nil {
desc := strings.TrimSpace(op.ExternalDocs.Description)
data.ExternalDocs.Description = strings.TrimSuffix(desc, ".")
data.ExternalDocs.URL = op.ExternalDocs.URL
}

if data.ExternalDocs.Description != "" && data.ExternalDocs.URL != "" {
data.SeeAlso = true
}

return data, nil
}

// writeAPIData writes the OpenAPI data to MDX files.
func writeAPIData(
data []OperationData,
Expand Down
3 changes: 3 additions & 0 deletions pkg/cmd/generate/clients/clients_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ paths:
Use the [API key](https://algolia.com) endpoint with **filters:active**.

This body keeps [markdown](https://algolia.com/doc) and **details** intact.
x-beta: true
x-acl:
- search
`)
Expand Down Expand Up @@ -78,6 +79,8 @@ paths:
assertFrontmatterDescription(t, frontmatter, "Use the API key endpoint with filters:active.")

assertRenderedContains(t, rendered.String(), []string{
`import Beta from "/snippets/beta.mdx";`,
"<Beta />",
`description: "Use the API key endpoint with filters:active."`,
"This body keeps [markdown](https://algolia.com/doc) and **details** intact.",
"**Required ACL:** `search`",
Expand Down
6 changes: 6 additions & 0 deletions pkg/cmd/generate/clients/method.mdx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ title: {{ .Summary }}
description: {{ frontmatterString .ShortDescription }}
public: true
---
{{- if .Beta }}

import Beta from "/snippets/beta.mdx";

<Beta />
{{- end }}
{{- if .Deprecated }}

<Warning>This method is **deprecated.**</Warning>
Expand Down
22 changes: 21 additions & 1 deletion pkg/cmd/generate/openapi/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type OverviewData struct {
type OperationData struct {
ACL string
APIPath string
Beta bool
Description string
ExternalDocs ExternalDocs
InputFilename string
Expand Down Expand Up @@ -187,6 +188,11 @@ func getAPIData(

count := 0

beta, err := utils.IsBetaAPI(&doc.Model)
if err != nil {
return nil, fmt.Errorf("get beta status: %w", err)
}

prefix := fmt.Sprintf("%s/%s", opts.OutputDirectory, opts.APIName)

for pathPairs := doc.Model.Paths.PathItems.First(); pathPairs != nil; pathPairs = pathPairs.Next() {
Expand All @@ -199,7 +205,14 @@ func getAPIData(
pathItem := pathPairs.Value()

for opPairs := pathItem.GetOperations().First(); opPairs != nil; opPairs = opPairs.Next() {
data, err := buildOperationData(opPairs.Key(), pathName, opPairs.Value(), opts, prefix)
data, err := buildOperationData(
opPairs.Key(),
pathName,
opPairs.Value(),
opts,
prefix,
beta,
)
if err != nil {
return nil, err
}
Expand All @@ -217,6 +230,7 @@ func buildOperationData(
op *v3.Operation,
opts *Options,
prefix string,
beta bool,
) (OperationData, error) {
short, long := utils.SplitDescription(op.Description)

Expand All @@ -225,9 +239,15 @@ func buildOperationData(
return OperationData{}, fmt.Errorf("get ACL for %s %s: %w", verb, pathName, err)
}

opBeta, err := utils.IsBetaOperation(op)
if err != nil {
return OperationData{}, fmt.Errorf("get beta status for %s %s: %w", verb, pathName, err)
}

data := OperationData{
ACL: utils.AclToString(acl),
APIPath: pathName,
Beta: beta || opBeta,
Description: long,
InputFilename: normalizePath(opts.InputFileName),
OutputFilename: utils.GetOutputFilename(op),
Expand Down
3 changes: 3 additions & 0 deletions pkg/cmd/generate/openapi/openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ paths:
Retrieve the [API key](https://algolia.com) with **filters:active**.

Use this endpoint to fetch a key by its value with **sample** output.
x-beta: true
x-acl:
- search
`)
Expand Down Expand Up @@ -75,6 +76,8 @@ paths:
assertFrontmatterDescription(t, frontmatter, "Retrieve the API key with filters:active.")

assertRenderedContains(t, rendered, []string{
`import Beta from "/snippets/beta.mdx";`,
"<Beta />",
"title: Get an API key",
`description: "Retrieve the API key with filters:active."`,
"Use this endpoint to fetch a key by its value with **sample** output.",
Expand Down
6 changes: 6 additions & 0 deletions pkg/cmd/generate/openapi/stub.mdx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ description: {{ frontmatterString .ShortDescription }}
openapi: {{ .InputFilename }} {{ .Verb }} {{ .APIPath }}
public: true
---
{{- if .Beta }}

import Beta from "/snippets/beta.mdx";

<Beta />
{{- end }}
{{- if .Description }}

{{ .Description }}
Expand Down
41 changes: 41 additions & 0 deletions pkg/cmd/generate/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,47 @@ func GetAPIName(path string) string {
return strings.TrimSuffix(base, filepath.Ext(base))
}

// IsBetaAPI returns true if the root document has an `x-beta: true` extension.
func IsBetaAPI(doc *v3.Document) (bool, error) {
if doc.Extensions == nil {
return false, nil
}

node, ok := doc.Extensions.Get("x-beta")
if !ok {
return false, nil
}

return parseBetaNode(node)
}

// IsBetaOperation returns true if the operation has an `x-beta: true` extension.
func IsBetaOperation(op *v3.Operation) (bool, error) {
if op.Extensions == nil {
return false, nil
}

node, ok := op.Extensions.Get("x-beta")
if !ok {
return false, nil
}

return parseBetaNode(node)
}

func parseBetaNode(node *yaml.Node) (bool, error) {
if node.Kind != yaml.ScalarNode {
return false, fmt.Errorf("expected a scalar node, got kind %d", node.Kind)
}

var result bool
if err := node.Decode(&result); err != nil {
return false, fmt.Errorf("expected a boolean node: %w", err)
}

return result, nil
}

// GetACL returns the ACL required to perform the given operation.
func GetACL(op *v3.Operation) ([]string, error) {
node, ok := op.Extensions.Get("x-acl")
Expand Down
Loading