@@ -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
4039var 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- }
0 commit comments