Skip to content

Commit

Permalink
feat: add the ability to specify a category specific context
Browse files Browse the repository at this point in the history
  • Loading branch information
tinygrasshopper committed May 30, 2024
1 parent cb4f64b commit 9791798
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 20 deletions.
5 changes: 4 additions & 1 deletion tools/api-docs-generator/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ specs:
docsHint: This document uses the v1 API. For more details, see the [v1 API](../v1-api-overview/).
- path: docs/.gitbook/assets/rest-spec.json
docsHint: This document uses the REST API. For more details, see the [Authentication for API](../authentication-for-api/) page.

categoryContext:
- name: licenses-v1
hint: |
**Note:** When you import or update Projects, changes will be reflected in the endpoint results after a one-hour delay.
output:
apiReferencePath: docs/snyk-api/reference
31 changes: 25 additions & 6 deletions tools/api-docs-generator/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,36 @@ type Output struct {
}

type Config struct {
Fetcher Fetcher `yaml:"fetcher"`
Specs []Spec `yaml:"specs"`
Output Output `yaml:"output"`
Fetcher Fetcher `yaml:"fetcher"`
Specs []Spec `yaml:"specs"`
Output Output `yaml:"output"`
CategoryContext CategoryContexts `yaml:"categoryContext"`
}

func Parse(filename string) (Config, error) {
type CategoryContexts []CategoryContext

func (contexts CategoryContexts) ToMap() map[string]string {
m := make(map[string]string)
for i := range contexts {
m[contexts[i].Name] = contexts[i].Hint
}
return m
}

type CategoryContext struct {
Name string `yaml:"name"`
Hint string `yaml:"hint"`
}

func Parse(filename string) (*Config, error) {
cfg := Config{}
file, err := os.Open(filename)
if err != nil {
return cfg, err
return nil, err
}
err = yaml.NewDecoder(file).Decode(&cfg)
return cfg, err
if err != nil {
return nil, err
}
return &cfg, err
}
4 changes: 2 additions & 2 deletions tools/api-docs-generator/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func TestParse(t *testing.T) {
tests := []struct {
name string
args func() string
want Config
want *Config
wantErr bool
}{
{
Expand All @@ -29,7 +29,7 @@ specs:
output:
apiReferencePath: snyk-api/reference`)
},
want: Config{
want: &Config{
Fetcher: Fetcher{"source", "destination"},
Specs: []Spec{
{".gitbook/assets/spec.yaml", " (v1)", "hint 1"},
Expand Down
2 changes: 1 addition & 1 deletion tools/api-docs-generator/fetcher/spec_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/snyk/user-docs/tools/api-docs-generator/config"
)

func FetchSpec(ctx context.Context, cfg config.Config, directory string) error {
func FetchSpec(ctx context.Context, cfg *config.Config, directory string) error {
// #nosec G107 // cfg.Fetcher.Source is a URL from config and does not contain user input
resp, err := get(ctx, cfg.Fetcher.Source)
if err != nil {
Expand Down
19 changes: 14 additions & 5 deletions tools/api-docs-generator/generator/reference_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type operationPath struct {
docsHint string
}

func GenerateReferenceDocs(cfg config.Config, docsBasePath string) error {
func GenerateReferenceDocs(cfg *config.Config, docsBasePath string) error {
aggregatedDocs, err := aggregateSpecs(cfg, docsBasePath)
if err != nil {
return err
Expand All @@ -33,7 +33,7 @@ func GenerateReferenceDocs(cfg config.Config, docsBasePath string) error {
destinationPath := path.Join(docsBasePath, cfg.Output.APIReferencePath, labelToFileName(label))
summary = append(summary, fmt.Sprintf("* [%s](%s)\n", label, path.Join(cfg.Output.APIReferencePath, labelToFileName(label))))

err := renderReferenceDocsPage(destinationPath, label, docsBasePath, operations)
err := renderReferenceDocsPage(destinationPath, label, docsBasePath, operations, cfg.CategoryContext)
if err != nil {
return err
}
Expand All @@ -45,7 +45,7 @@ func GenerateReferenceDocs(cfg config.Config, docsBasePath string) error {
return nil
}

func aggregateSpecs(cfg config.Config, docsBasePath string) (map[string][]operationPath, error) {
func aggregateSpecs(cfg *config.Config, docsBasePath string) (map[string][]operationPath, error) {
aggregatedDocs := map[string][]operationPath{}

for _, spec := range cfg.Specs {
Expand Down Expand Up @@ -73,7 +73,7 @@ func aggregateSpecs(cfg config.Config, docsBasePath string) (map[string][]operat
return aggregatedDocs, nil
}

func renderReferenceDocsPage(filePath, label, docsPath string, operation []operationPath) error {
func renderReferenceDocsPage(filePath, label, docsPath string, operation []operationPath, categoryContext config.CategoryContexts) error {
docsFile, err := os.Create(filePath)
if err != nil {
return err
Expand All @@ -84,6 +84,10 @@ func renderReferenceDocsPage(filePath, label, docsPath string, operation []opera
{%% hint style="info" %%}
%s
{%% endhint %%}`, label, operation[0].docsHint)
if categoryContextHint, found := categoryContext.ToMap()[labelToKey(label)]; found {
fmt.Fprintln(docsFile)
fmt.Fprint(docsFile, categoryContextHint)
}

// sort for stability
sort.Slice(operation, func(i, j int) bool {
Expand Down Expand Up @@ -113,9 +117,14 @@ func renderReferenceDocsPage(filePath, label, docsPath string, operation []opera
}

func labelToFileName(label string) string {
return labelToKey(label) + ".md"
}

func labelToKey(label string) string {
replacements := []string{"(", ")"}
for _, replacement := range replacements {
label = strings.ReplaceAll(label, replacement, "")
}
return strings.ToLower(strings.ReplaceAll(label, " ", "-")) + ".md"

return strings.ToLower(strings.ReplaceAll(label, " ", "-"))
}
96 changes: 91 additions & 5 deletions tools/api-docs-generator/generator/reference_docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"path"
"testing"

"github.com/snyk/user-docs/tools/api-docs-generator/config"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -43,10 +45,11 @@ func Test_labelToFileName(t *testing.T) {

func Test_renderReferenceDocsPage(t *testing.T) {
type args struct {
filePath string
label string
docsPath string
operation []operationPath
filePath string
label string
docsPath string
operation []operationPath
categoryContext config.CategoryContexts
}
testDir := t.TempDir()
tests := []struct {
Expand All @@ -69,6 +72,84 @@ func Test_renderReferenceDocsPage(t *testing.T) {
docsHint: "This is a hint",
},
},
categoryContext: config.CategoryContexts{},
},
checker: func(t *testing.T, args args) {
t.Helper()
content, err := os.ReadFile(args.filePath)
if err != nil {
t.Fatal(err)
}
renderedFileContents := string(content)
assert.Contains(t, renderedFileContents, "# Apps", "rendered file contains header")
assert.Contains(t, renderedFileContents, "This is a hint", "rendered file does not contain hint")
assert.Contains(t, renderedFileContents, `path="/apps"`, "rendered file does not contain path")
assert.Contains(t, renderedFileContents, `method="get"`, "rendered file does not contain method")
assert.Contains(t, renderedFileContents, `src="../foo/test/apps-spec.yaml"`, "renders relative path to spec file")
},
},
{
name: "renders reference docs page, with category context hint",

args: args{
filePath: createTempFile(t, testDir, "existing content"),
label: "Apps",
docsPath: testDir,
operation: []operationPath{
{
specPath: "foo/test/apps-spec.yaml",
pathURL: "/apps",
method: "GET",
docsHint: "This is a hint",
},
},
categoryContext: config.CategoryContexts{
{
Name: "apps",
Hint: "This is a hint from category context",
},
{
Name: "not-apps",
Hint: "This is a hint from another context",
},
},
},
checker: func(t *testing.T, args args) {
t.Helper()
content, err := os.ReadFile(args.filePath)
if err != nil {
t.Fatal(err)
}
renderedFileContents := string(content)
assert.Contains(t, renderedFileContents, "# Apps", "rendered file contains header")
assert.Contains(t, renderedFileContents, "This is a hint", "rendered file does not contain hint")
assert.Contains(t, renderedFileContents, `path="/apps"`, "rendered file does not contain path")
assert.Contains(t, renderedFileContents, `method="get"`, "rendered file does not contain method")
assert.Contains(t, renderedFileContents, `src="../foo/test/apps-spec.yaml"`, "renders relative path to spec file")
assert.Contains(t, renderedFileContents, `This is a hint from category context`, "renders category context hint")
},
},
{
name: "renders reference docs page, without category context hint if no matches",

args: args{
filePath: createTempFile(t, testDir, "existing content"),
label: "Apps",
docsPath: testDir,
operation: []operationPath{
{
specPath: "foo/test/apps-spec.yaml",
pathURL: "/apps",
method: "GET",
docsHint: "This is a hint",
},
},
categoryContext: config.CategoryContexts{
{
Name: "not-apps",
Hint: "This is a hint from category context",
},
},
},
checker: func(t *testing.T, args args) {
t.Helper()
Expand All @@ -82,12 +163,17 @@ func Test_renderReferenceDocsPage(t *testing.T) {
assert.Contains(t, renderedFileContents, `path="/apps"`, "rendered file does not contain path")
assert.Contains(t, renderedFileContents, `method="get"`, "rendered file does not contain method")
assert.Contains(t, renderedFileContents, `src="../foo/test/apps-spec.yaml"`, "renders relative path to spec file")
assert.NotContains(t, renderedFileContents, `This is a hint from category context`, "does not render category context hint")
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := renderReferenceDocsPage(tt.args.filePath, tt.args.label, tt.args.docsPath, tt.args.operation); (err != nil) != tt.wantErr {
if err := renderReferenceDocsPage(tt.args.filePath,
tt.args.label,
tt.args.docsPath,
tt.args.operation,
tt.args.categoryContext); (err != nil) != tt.wantErr {
t.Errorf("renderReferenceDocsPage() error = %v, wantErr %v", err, tt.wantErr)
}
tt.checker(t, tt.args)
Expand Down

0 comments on commit 9791798

Please sign in to comment.