Skip to content

Commit f3f68da

Browse files
authored
feat: generate guides (#30)
Generate the 'guides' snippets from a JSON file.
1 parent 86c6deb commit f3f68da

8 files changed

Lines changed: 298 additions & 6 deletions

File tree

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ See the individual subcommands to learn what content you can generate.
6565

6666
**Aliases:** `gen`, `g`
6767

68-
**Subcommands:** `cdn`, `clients`, `openapi`, `sla`, `snippets`
68+
**Subcommands:** `cdn`, `clients`, `guides`, `openapi`, `sla`, `snippets`
6969

7070
#### `docli generate cdn`
7171

@@ -135,6 +135,31 @@ docli gen clients specs/search.yml -o doc/libraries/sdk/methods
135135
`-o, --output string` Output directory for generated MDX files (default: `out`)
136136

137137

138+
#### `docli generate guides`
139+
140+
```sh
141+
docli generate guides <guides> [flags]
142+
```
143+
144+
Generate guide snippets from a JSON file.
145+
146+
This command reads a data file with guide snippets.
147+
It generates an MDX file for each guide.
148+
149+
**Examples**
150+
151+
```sh
152+
# Run from root of algolia/docs-new
153+
docli gen guides guides.json -o snippets/guides
154+
```
155+
156+
**Flags**
157+
158+
`-h, --help` Help for this command
159+
160+
`-o, --output string` Output directory for generated MDX files (default: `out`)
161+
162+
138163
#### `docli generate openapi`
139164

140165
```sh

pkg/cmd/generate/cdn/cdn.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func runCommand(opts *Options) {
8989
log.Fatalf("error: %e", err)
9090
}
9191

92-
if err = os.MkdirAll(opts.OutputDirectory, 0o755); err != nil {
92+
if err = os.MkdirAll(opts.OutputDirectory, 0o700); err != nil {
9393
log.Fatalf("error: %e", err)
9494
}
9595

pkg/cmd/generate/clients/clients.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func getAPIData(
203203
// writeAPIData writes the OpenAPI data to MDX files.
204204
func writeAPIData(data []OperationData, template *template.Template) error {
205205
for _, item := range data {
206-
if err := os.MkdirAll(item.OutputPath, 0o755); err != nil {
206+
if err := os.MkdirAll(item.OutputPath, 0o700); err != nil {
207207
return err
208208
}
209209

pkg/cmd/generate/guides/guides.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package guides
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"os"
8+
"path/filepath"
9+
"sort"
10+
"strings"
11+
12+
"github.com/MakeNowJust/heredoc"
13+
"github.com/algolia/docli/pkg/cmd/generate/utils"
14+
"github.com/algolia/docli/pkg/dictionary"
15+
"github.com/spf13/cobra"
16+
)
17+
18+
type Options struct {
19+
GuidesFile string
20+
OutputDirectory string
21+
}
22+
23+
// GuidesMap represents the data from a guide file.
24+
type GuidesMap map[string]map[string]string
25+
26+
func NewGuidesCommand() *cobra.Command {
27+
opts := &Options{}
28+
29+
cmd := &cobra.Command{
30+
Use: "guides <guides>",
31+
Short: "Generate guide snippets from a JSON file",
32+
Long: heredoc.Doc(`
33+
This command reads a data file with guide snippets.
34+
It generates an MDX file for each guide.
35+
`),
36+
Example: heredoc.Doc(`
37+
# Run from root of algolia/docs-new
38+
docli gen guides guides.json -o snippets/guides
39+
`),
40+
Args: cobra.ExactArgs(1),
41+
Run: func(cmd *cobra.Command, args []string) {
42+
opts.GuidesFile = args[0]
43+
runCommand(opts)
44+
},
45+
}
46+
47+
cmd.Flags().
48+
StringVarP(&opts.OutputDirectory, "output", "o", "out", "Output directory for generated MDX files")
49+
50+
return cmd
51+
}
52+
53+
func runCommand(opts *Options) {
54+
bytes, err := os.ReadFile(opts.GuidesFile)
55+
if err != nil {
56+
log.Fatalf("Error: %e", err)
57+
}
58+
59+
var data GuidesMap
60+
if err := json.Unmarshal(bytes, &data); err != nil {
61+
log.Fatalf("Error: %e", err)
62+
}
63+
64+
fmt.Printf("Generating guide snippet files for: %s\n", opts.GuidesFile)
65+
fmt.Printf("Writing output in: %s\n", opts.OutputDirectory)
66+
67+
guideNames := make([]string, 0, len(data))
68+
for guide := range data {
69+
guideNames = append(guideNames, guide)
70+
}
71+
72+
sort.Strings(guideNames)
73+
74+
for _, guide := range guideNames {
75+
err := writeGuide(
76+
opts.OutputDirectory,
77+
fmt.Sprintf("%s.mdx", utils.ToKebabCase(guide)),
78+
generateMarkdownSnippet(data[guide]),
79+
)
80+
if err != nil {
81+
log.Fatalf("Error: %e", err)
82+
}
83+
}
84+
}
85+
86+
// generateMarkdownSnippet generates a CodeGroup block.
87+
func generateMarkdownSnippet(snippet map[string]string) string {
88+
result := "<CodeGroup>\n"
89+
languages := sortLanguages(snippet)
90+
91+
for _, lang := range languages {
92+
result += fmt.Sprintf(
93+
"\n```%s %s\n",
94+
dictionary.NormalizeLang(lang),
95+
utils.GetLanguageName(lang),
96+
)
97+
replaced := strings.ReplaceAll(snippet[lang], "<YOUR_INDEX_NAME>", "ALGOLIA_INDEX_NAME")
98+
replaced = strings.ReplaceAll(
99+
replaced,
100+
"cts_e2e_deleteObjects_javascript",
101+
"ALGOLIA_INDEX_NAME",
102+
)
103+
result += replaced
104+
result += "\n```\n"
105+
}
106+
107+
result += "\n</CodeGroup>"
108+
109+
return result
110+
}
111+
112+
// sortLanguages returns a list of sorted languages.
113+
func sortLanguages(snippet map[string]string) []string {
114+
sorted := make([]string, 0, len(snippet))
115+
116+
for lang := range snippet {
117+
sorted = append(sorted, lang)
118+
}
119+
120+
sort.Strings(sorted)
121+
122+
return sorted
123+
}
124+
125+
// writeGuide writes the guide snippets into MDX files.
126+
func writeGuide(path string, filename string, snippet string) error {
127+
err := os.MkdirAll(path, 0o700)
128+
if err != nil {
129+
return err
130+
}
131+
132+
fullPath := filepath.Join(path, filename)
133+
134+
out, err := os.Create(fullPath)
135+
if err != nil {
136+
return err
137+
}
138+
defer out.Close()
139+
140+
if _, err := out.WriteString(snippet); err != nil {
141+
return err
142+
}
143+
144+
return nil
145+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package guides
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestSortLanguages(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
input map[string]string
12+
want []string
13+
}{
14+
{
15+
name: "empty map",
16+
input: map[string]string{},
17+
want: []string{},
18+
},
19+
{
20+
name: "single key",
21+
input: map[string]string{"go": "fmt.Println"},
22+
want: []string{"go"},
23+
},
24+
{
25+
name: "already sorted keys",
26+
input: map[string]string{
27+
"c": "printf",
28+
"go": "fmt.Println",
29+
"py": "print",
30+
},
31+
want: []string{"c", "go", "py"},
32+
},
33+
{
34+
name: "unsorted keys",
35+
input: map[string]string{
36+
"py": "print",
37+
"go": "fmt.Println",
38+
"c": "printf",
39+
},
40+
want: []string{"c", "go", "py"},
41+
},
42+
}
43+
44+
for _, tt := range tests {
45+
t.Run(tt.name, func(t *testing.T) {
46+
got := sortLanguages(tt.input)
47+
if !reflect.DeepEqual(got, tt.want) {
48+
t.Errorf("sortLanguages(%v) = %v; want %v", tt.input, got, tt.want)
49+
}
50+
})
51+
}
52+
}
53+
54+
func TestGenerateMarkdownSnippet(t *testing.T) {
55+
tests := []struct {
56+
name string
57+
snippet map[string]string
58+
want string
59+
}{
60+
{
61+
name: "empty map",
62+
snippet: map[string]string{},
63+
want: "<CodeGroup>\n\n</CodeGroup>",
64+
},
65+
{
66+
name: "single language",
67+
snippet: map[string]string{
68+
"go": `fmt.Println("hello")`,
69+
},
70+
want: "<CodeGroup>\n\n" +
71+
"```go Go\n" +
72+
"fmt.Println(\"hello\")\n" +
73+
"```\n\n" +
74+
"</CodeGroup>",
75+
},
76+
{
77+
name: "two languages unsorted input",
78+
snippet: map[string]string{
79+
"python": `print("hi")`,
80+
"go": `fmt.Println("hi")`,
81+
},
82+
want: "<CodeGroup>\n\n" +
83+
"```go Go\n" +
84+
"fmt.Println(\"hi\")\n" +
85+
"```\n\n" +
86+
"```python Python\n" +
87+
"print(\"hi\")\n" +
88+
"```\n\n" +
89+
"</CodeGroup>",
90+
},
91+
{
92+
name: "multiple languages arbitrary order",
93+
snippet: map[string]string{
94+
"ruby": `puts "hey"`,
95+
"js": `console.log("hey")`,
96+
"go": `fmt.Println("hey")`,
97+
},
98+
want: "<CodeGroup>\n\n" +
99+
"```go Go\n" +
100+
"fmt.Println(\"hey\")\n" +
101+
"```\n\n" +
102+
"```js JavaScript\n" +
103+
"console.log(\"hey\")\n" +
104+
"```\n\n" +
105+
"```ruby Ruby\n" +
106+
"puts \"hey\"\n" +
107+
"```\n\n" +
108+
"</CodeGroup>",
109+
},
110+
}
111+
112+
for _, tt := range tests {
113+
t.Run(tt.name, func(t *testing.T) {
114+
got := generateMarkdownSnippet(tt.snippet)
115+
if got != tt.want {
116+
t.Errorf("generateMarkdownSnippet(%v) =\n%q\nwant:\n%q", tt.snippet, got, tt.want)
117+
}
118+
})
119+
}
120+
}

pkg/cmd/generate/index.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/MakeNowJust/heredoc"
55
"github.com/algolia/docli/pkg/cmd/generate/cdn"
66
"github.com/algolia/docli/pkg/cmd/generate/clients"
7+
"github.com/algolia/docli/pkg/cmd/generate/guides"
78
"github.com/algolia/docli/pkg/cmd/generate/openapi"
89
"github.com/algolia/docli/pkg/cmd/generate/sla"
910
"github.com/algolia/docli/pkg/cmd/generate/snippets"
@@ -32,6 +33,7 @@ func NewGenerateCmd() *cobra.Command {
3233
command.AddCommand(openapi.NewOpenAPICommand())
3334
command.AddCommand(sla.NewSLACommand())
3435
command.AddCommand(snippets.NewSnippetsCommand())
36+
command.AddCommand(guides.NewGuidesCommand())
3537
command.AddCommand(cdn.NewCdnCommand())
3638

3739
return command

pkg/cmd/generate/openapi/openapi.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ func getAPIData(
212212

213213
// writeOverviewData writes the API's overview data into an MDX file.
214214
func writeOverviewData(data OverviewData, template *template.Template) error {
215-
err := os.MkdirAll(data.OutputPath, 0o755)
215+
err := os.MkdirAll(data.OutputPath, 0o700)
216216
if err != nil {
217217
return err
218218
}
@@ -235,7 +235,7 @@ func writeOverviewData(data OverviewData, template *template.Template) error {
235235
// writeAPIData writes the OpenAPI data of a single operation to an MDX stub file.
236236
func writeAPIData(data []OperationData, template *template.Template) error {
237237
for _, item := range data {
238-
err := os.MkdirAll(item.OutputPath, 0o755)
238+
err := os.MkdirAll(item.OutputPath, 0o700)
239239
if err != nil {
240240
return err
241241
}

pkg/cmd/generate/snippets/snippets.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func invertSnippets(data NestedMap) NestedMap {
145145

146146
// writeSnippet writes the snippets into MDX files.
147147
func writeSnippet(path string, filename string, snippet string) error {
148-
err := os.MkdirAll(path, 0o755)
148+
err := os.MkdirAll(path, 0o700)
149149
if err != nil {
150150
return err
151151
}

0 commit comments

Comments
 (0)