Skip to content

Commit a49e189

Browse files
authored
chore: extract utilties to utils package (#3)
There will be a couple of more `generate` commands, so it makes sense to extract the utilities from the `openapi` command to a separate `utils` package.
1 parent 88d23ee commit a49e189

File tree

8 files changed

+537
-198
lines changed

8 files changed

+537
-198
lines changed

pkg/cmd/generate/index.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func NewGenerateCmd() *cobra.Command {
1313
Short: "Generate API reference docs",
1414
}
1515

16-
command.AddCommand(openapi.NewOpenAPICommand())
16+
command.AddCommand(openapi.NewOpenApiCommand())
1717
command.AddCommand(sla.NewSlaCommand())
1818

1919
return command

pkg/cmd/generate/openapi/openapi.go

Lines changed: 13 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ import (
66
"log"
77
"os"
88
"path/filepath"
9-
"regexp"
109
"strings"
1110
"text/template"
1211

1312
"github.com/MakeNowJust/heredoc"
13+
"github.com/algolia/docli/pkg/cmd/generate/utils"
1414
"github.com/pb33f/libopenapi"
1515
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
1616
"github.com/spf13/cobra"
17-
"gopkg.in/yaml.v3"
1817
)
1918

2019
// Options represents the options and flags for this command.
@@ -39,8 +38,8 @@ type OperationData struct {
3938
//go:embed stub.mdx.tmpl
4039
var stubTemplate string
4140

42-
// NewOpenAPICommand returns a new instance of the `generate openapi` command.
43-
func NewOpenAPICommand() *cobra.Command {
41+
// NewOpenApiCommand returns a new instance of the `generate openapi` command.
42+
func NewOpenApiCommand() *cobra.Command {
4443
opts := &Options{}
4544

4645
cmd := &cobra.Command{
@@ -51,10 +50,10 @@ func NewOpenAPICommand() *cobra.Command {
5150
This command reads an OpenAPI 3 spec file and generates one MDX file per operation.
5251
The command groups the operations into subdirectories by tags.
5352
`),
54-
Args: cobra.MinimumNArgs(1),
53+
Args: cobra.ExactArgs(1),
5554
Run: func(cmd *cobra.Command, args []string) {
5655
opts.InputFileName = args[0]
57-
opts.ApiName = getApiName(opts.InputFileName)
56+
opts.ApiName = utils.GetApiName(opts.InputFileName)
5857

5958
runCommand(opts)
6059
},
@@ -78,12 +77,12 @@ func runCommand(opts *Options) {
7877

7978
opts.SpecFile = specFile
8079

81-
spec, err := loadSpec(opts)
80+
spec, err := utils.LoadSpec(opts.SpecFile)
8281
if err != nil {
8382
log.Fatalf("Error: %e", err)
8483
}
8584

86-
opData, err := apiStubData(spec, opts)
85+
opData, err := getApiData(spec, opts)
8786
if err != nil {
8887
log.Fatalf("Error: %e", err)
8988
}
@@ -97,27 +96,8 @@ func runCommand(opts *Options) {
9796
writeApiData(opData, tmpl)
9897
}
9998

100-
// loadSpec parses the file as OpenAPI 3 spec and returns the data model.
101-
func loadSpec(opts *Options) (*libopenapi.DocumentModel[v3.Document], error) {
102-
doc, err := libopenapi.NewDocument(opts.SpecFile)
103-
if err != nil {
104-
return nil, err
105-
}
106-
107-
docModel, errors := doc.BuildV3Model()
108-
if len(errors) > 0 {
109-
for i := range errors {
110-
fmt.Printf("error: %e\n", errors[i])
111-
}
112-
113-
return nil, fmt.Errorf("cannot parse spec: %d errors.", len(errors))
114-
}
115-
116-
return docModel, nil
117-
}
118-
119-
// apiStubData generates the MDX stub data for each OpenAPI operation in the spec.
120-
func apiStubData(
99+
// getApiData generates the MDX stub data for each OpenAPI operation in the spec.
100+
func getApiData(
121101
doc *libopenapi.DocumentModel[v3.Document],
122102
opts *Options,
123103
) ([]OperationData, error) {
@@ -139,17 +119,17 @@ func apiStubData(
139119
for opPairs := pathItem.GetOperations().First(); opPairs != nil; opPairs = opPairs.Next() {
140120
op := opPairs.Value()
141121

142-
acl, err := getAcl(op)
122+
acl, err := utils.GetAcl(op)
143123
if err != nil {
144124
return nil, err
145125
}
146126

147127
data := OperationData{
148-
Acl: strings.Join(acl, ","),
128+
Acl: utils.AclToString(acl),
149129
ApiPath: pathName,
150130
InputFilename: strings.TrimPrefix(opts.InputFileName, "/"),
151-
OutputFilename: outputFilename(op),
152-
OutputPath: outputPath(op, prefix),
131+
OutputFilename: utils.GetOutputFilename(op),
132+
OutputPath: utils.GetOutputPath(op, prefix),
153133
RequiresAdmin: false,
154134
Verb: opPairs.Key(),
155135
}
@@ -191,64 +171,3 @@ func writeApiData(data []OperationData, template *template.Template) error {
191171

192172
return nil
193173
}
194-
195-
// outputPath returns the output path for the MDX file for the given operation.
196-
func outputPath(op *v3.Operation, prefix string) string {
197-
if len(op.Tags) > 0 {
198-
return fmt.Sprintf("%s/%s", prefix, toKebabCase(op.Tags[0]))
199-
}
200-
201-
return fmt.Sprintf("%s", prefix)
202-
}
203-
204-
// outputFilename generates the filename from the operationId.
205-
func outputFilename(op *v3.Operation) string {
206-
return fmt.Sprintf("%s.mdx", toKebabCase(op.OperationId))
207-
}
208-
209-
// toKebabCase turns a string into kebab-case.
210-
func toKebabCase(s string) string {
211-
matchFirstCap := regexp.MustCompile(`(.)([A-Z][a-z]+)`)
212-
matchAllCap := regexp.MustCompile(`([a-z0-9])([A-Z])`)
213-
214-
out := matchFirstCap.ReplaceAllString(s, `${1}-${2}`)
215-
out = matchAllCap.ReplaceAllString(out, `${1}-${2}`)
216-
out = regexp.MustCompile(`[^a-zA-Z0-9]+`).ReplaceAllString(out, `-`)
217-
out = strings.Trim(out, `-`)
218-
219-
return strings.ToLower(out)
220-
}
221-
222-
// getAcl returns the ACL required to perform the given operation.
223-
func getAcl(op *v3.Operation) ([]string, error) {
224-
node, ok := op.Extensions.Get("x-acl")
225-
226-
// Operations can be without ACL
227-
if !ok {
228-
return nil, nil
229-
}
230-
231-
if node.Kind != yaml.SequenceNode {
232-
return nil, fmt.Errorf("expected a sequence node, got kind %d", node.Kind)
233-
}
234-
235-
var result []string
236-
237-
for _, child := range node.Content {
238-
if child.Kind != yaml.ScalarNode {
239-
return nil, fmt.Errorf("expected scalar nodes in sequence, got kind %d", child.Kind)
240-
}
241-
242-
result = append(result, child.Value)
243-
}
244-
245-
return result, nil
246-
}
247-
248-
// getApiName returns the name of the YAML file without extension as API name.
249-
func getApiName(path string) string {
250-
// Have to make an exception for the Analytics API
251-
base := filepath.Base(strings.ReplaceAll(path, "searchstats", "analytics"))
252-
253-
return strings.TrimSuffix(base, filepath.Ext(base))
254-
}

pkg/cmd/generate/openapi/openapi_test.go

Lines changed: 0 additions & 68 deletions
This file was deleted.

pkg/cmd/generate/sla/sla.go

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import (
88
"log"
99
"os"
1010
"sort"
11-
"strings"
1211
"text/template"
13-
"unicode"
1412

13+
"github.com/algolia/docli/pkg/cmd/generate/utils"
1514
"github.com/spf13/cobra"
1615
"golang.org/x/mod/semver"
1716
)
@@ -86,8 +85,8 @@ func runCommand(opts *Options) {
8685
sorted := sortVersions(&data)
8786

8887
funcMap := template.FuncMap{
89-
"capitalize": capitalize,
90-
"getLanguageName": getLanguageName,
88+
"capitalize": utils.Capitalize,
89+
"getLanguageName": utils.GetLanguageName,
9190
}
9291

9392
var output io.Writer
@@ -161,37 +160,6 @@ func sortVersions(data *Clients) []ClientEntry {
161160
return result
162161
}
163162

164-
// languages is a map for translating a language id to its proper name.
165-
var languages = map[string]string{
166-
"csharp": "C#",
167-
"javascript": "JavaScript",
168-
"php": "PHP",
169-
}
170-
171-
// capitalize returns the capitalized word.
172-
func capitalize(word string) string {
173-
word = strings.ToLower(word)
174-
runes := []rune(word)
175-
runes[0] = unicode.ToUpper(runes[0])
176-
177-
return string(runes)
178-
}
179-
180-
// getLanguageName returns the printable name of a language id.
181-
func getLanguageName(lang string) string {
182-
if lang == "" {
183-
return ""
184-
}
185-
186-
lang = strings.ToLower(lang)
187-
188-
if special, ok := languages[lang]; ok {
189-
return special
190-
}
191-
192-
return capitalize(lang)
193-
}
194-
195163
func renderPage(
196164
w io.Writer,
197165
templateString string,

0 commit comments

Comments
 (0)