Skip to content

Commit

Permalink
feat: filter lower stabilites from docs and changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
RaduPetreTarean committed Oct 9, 2024
1 parent ebd213b commit 50105fc
Show file tree
Hide file tree
Showing 16 changed files with 283 additions and 39 deletions.
47 changes: 37 additions & 10 deletions tools/api-docs-generator/changelog/changelog.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,19 @@ func writeOperationChangeDetails(markdown *md.Markdown, changeGroup ChangesByEnd
}
}

func groupChanges(changes checker.Changes) []ChangesByEndpoint {
func groupChanges(changes checker.Changes, gaEndpoints map[Endpoint]bool) []ChangesByEndpoint {
apiChanges := map[Endpoint]checker.Changes{}

for _, change := range changes {
if change, ok := change.(checker.ApiChange); ok {
ep := Endpoint{Path: change.GetPath(), Operation: change.GetOperation()}
if changesForEndpoint, ok := apiChanges[ep]; ok {
apiChanges[ep] = append(changesForEndpoint, change)
} else {
apiChanges[ep] = checker.Changes{change}
if apiChange, ok := change.(checker.ApiChange); ok {
ep := Endpoint{Path: apiChange.GetPath(), Operation: apiChange.GetOperation()}
// Only include changes for GA endpoints
if gaEndpoints[ep] {
if changesForEndpoint, ok := apiChanges[ep]; ok {
apiChanges[ep] = append(changesForEndpoint, apiChange)
} else {
apiChanges[ep] = checker.Changes{apiChange}
}
}
}
}
Expand All @@ -231,6 +234,25 @@ func groupChanges(changes checker.Changes) []ChangesByEndpoint {
return apiChangesSlice
}

func getGAEndpoints(spec *openapi3.T) map[Endpoint]bool {
gaEndpoints := make(map[Endpoint]bool)
for currentPath, pathItem := range spec.Paths.Map() {
for operationName, operation := range pathItem.Operations() {
if ext, ok := operation.Extensions["x-snyk-api-stability"]; ok {
if stabilityStr, ok := ext.(string); ok {
if stabilityStr == "ga" {
ep := Endpoint{Path: currentPath, Operation: operationName}
gaEndpoints[ep] = true
}
} else {
fmt.Printf("Extension 'x-snyk-api-stability' for %s %s is not a string\n", operationName, currentPath)
}
}
}
}
return gaEndpoints
}

func getChangeLog(nextVersionURI, baseVersionURI string, loader *openapi3.Loader) ([]ChangesByEndpoint, error) {
baseVersionURL, err := url.Parse(baseVersionURI)
if err != nil {
Expand All @@ -255,14 +277,19 @@ func getChangeLog(nextVersionURI, baseVersionURI string, loader *openapi3.Loader
return nil, err
}

diffReport, sourcesMap, err := diff.GetWithOperationsSourcesMap(diff.NewConfig(), s1, s2)
changes := checker.CheckBackwardCompatibilityUntilLevel(checker.GetDefaultChecks(), diffReport, sourcesMap, checker.INFO)
// Collect GA endpoints from the next version spec
gaEndpoints := getGAEndpoints(s2.Spec)

groupedChanges := groupChanges(changes)
diffReport, sourcesMap, err := diff.GetWithOperationsSourcesMap(diff.NewConfig(), s1, s2)
if err != nil {
return nil, err
}

changes := checker.CheckBackwardCompatibilityUntilLevel(checker.GetDefaultChecks(), diffReport, sourcesMap, checker.INFO)

// Group and filter changes based on GA endpoints
groupedChanges := groupChanges(changes, gaEndpoints)

return groupedChanges, nil
}

Expand Down
18 changes: 16 additions & 2 deletions tools/api-docs-generator/changelog/changelog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ var tests = []*testConfig{
lastSyncVersion: "2024-05-23",
want: fmt.Sprintf("## %s - Updated %s", "2024-05-23", time.Now().Format("2006-01-02")),
},
{
name: "04_non_ga_changes_not_added",
baseURL: "../testdata/changelog/04_non_ga_changes_not_added/2024-04-25.yaml",
nextURL: "../testdata/changelog/04_non_ga_changes_not_added/2024-05-23.yaml",
latestGAVersion: "2024-05-23",
lastSyncVersion: "2024-04-25",
want: "",
},
}

func testFn(config *testConfig) (string, error) {
Expand Down Expand Up @@ -83,8 +91,14 @@ func Test_delta(t *testing.T) {
if gotErr != nil {
t.Errorf("Expected not to fail %v", gotErr)
}
if !strings.Contains(gotRes, tt.want) {
t.Errorf("Expected markdown to contain %v", tt.want)
if tt.want == "" {
if gotRes != "" {
t.Errorf("Expected output to be empty, but got: %v", gotRes)
}
} else {
if !strings.Contains(gotRes, tt.want) {
t.Errorf("Expected markdown to contain: %v, but got: %v", tt.want, gotRes)
}
}
})
}
Expand Down
108 changes: 81 additions & 27 deletions tools/api-docs-generator/generator/reference_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,43 +90,97 @@ func clearDir(dirName string) error {
}

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

for _, spec := range cfg.Specs {
loader := openapi3.NewLoader()
doc, err := loader.LoadFromFile(path.Join(docsBasePath, spec.Path))
specDocs, err := processSpec(spec, docsBasePath)
if err != nil {
return nil, err
}
for pathURL, pathItem := range doc.Paths.Map() {
for method, operation := range pathItem.Operations() {
for _, tag := range operation.Tags {
if tag == "OpenAPI" {
continue
}
snykDocsExtension := operation.Extensions["x-snyk-documentation"]
if snykDocsExtension != nil {
tag, err = extractCategoryNameFromExtension(snykDocsExtension)
if err != nil {
return nil, err
}
}
tag += spec.Suffix
aggregatedDocs[tag] = append(aggregatedDocs[tag], operationPath{
operation: operation,
pathItem: pathItem,
pathURL: pathURL,
specPath: spec.Path,
method: method,
docsHint: spec.DocsHint,
})
}
}

for tag, ops := range specDocs {
aggregatedDocs[tag] = append(aggregatedDocs[tag], ops...)
}
}

return aggregatedDocs, nil
}

func processSpec(spec config.Spec, docsBasePath string) (map[string][]operationPath, error) {
loader := openapi3.NewLoader()
doc, err := loader.LoadFromFile(path.Join(docsBasePath, spec.Path))
if err != nil {
return nil, err
}

specDocs := make(map[string][]operationPath)
for pathURL, pathItem := range doc.Paths.Map() {
err := processPathItem(pathURL, pathItem, spec, specDocs)
if err != nil {
return nil, err
}
}
return specDocs, nil
}

func processPathItem(pathURL string, pathItem *openapi3.PathItem, spec config.Spec, specDocs map[string][]operationPath) error {
for method, operation := range pathItem.Operations() {
err := processOperation(pathURL, pathItem, method, operation, spec, specDocs)
if err != nil {
return err
}
}
return nil
}

func processOperation(pathURL string,
pathItem *openapi3.PathItem,
method string,
operation *openapi3.Operation,
spec config.Spec,
specDocs map[string][]operationPath) error {
for _, tag := range operation.Tags {
if tag == "OpenAPI" {
continue
}

if snykDocsExtension, ok := operation.Extensions["x-snyk-documentation"]; ok && snykDocsExtension != nil {
var err error
tag, err = extractCategoryNameFromExtension(snykDocsExtension)
if err != nil {
return err
}
}

if !isGAOperation(operation) {
continue
}

tag += spec.Suffix
specDocs[tag] = append(specDocs[tag], operationPath{
operation: operation,
pathItem: pathItem,
pathURL: pathURL,
specPath: spec.Path,
method: method,
docsHint: spec.DocsHint,
})
}
return nil
}

func isGAOperation(operation *openapi3.Operation) bool {
apiStabilityExtension, ok := operation.Extensions["x-snyk-api-stability"]
if !ok || apiStabilityExtension == nil {
return false
}
stabilityStr, ok := apiStabilityExtension.(string)
if !ok {
return false
}
return stabilityStr == "ga"
}

func extractCategoryNameFromExtension(extension interface{}) (string, error) {
extensionMap, worked := extension.(map[string]interface{})
if !worked {
Expand Down
29 changes: 29 additions & 0 deletions tools/api-docs-generator/generator/reference_docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,29 @@ func Test_aggregateSpecs(t *testing.T) {
},
wantErr: assert.NoError,
},
{
name: "filters out non-stable paths",
args: args{
cfg: &config.Config{
Specs: []config.Spec{
{
Path: "spec_with_stability.yaml",
},
},
},
docsBasePath: "../testdata/reference_docs/",
},
want: map[string][]operationPath{
"stable": {
{
method: "GET",
specPath: "spec_with_stability.yaml",
pathURL: "/stable-path",
},
},
},
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -302,6 +325,12 @@ func Test_aggregateSpecs(t *testing.T) {
return
}
compareForTest(t, tt.want, got)
for tag := range got {
for _, op := range got[tag] {
assert.NotEqual(t, "/unstable-path", op.pathURL)
assert.NotEqual(t, "/no-stability-path", op.pathURL)
}
}
})
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ tags:
paths:
/test:
post:
x-snyk-api-stability: ga
description: test
operationId: test
tags:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ tags:
paths:
/test:
post:
x-snyk-api-stability: ga
description: test
operationId: test
tags:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ tags:
paths:
/test:
post:
x-snyk-api-stability: ga
description: test
operationId: test
tags:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ tags:
paths:
/test:
post:
x-snyk-api-stability: ga
description: test
operationId: test
tags:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ tags:
paths:
/test:
post:
x-snyk-api-stability: ga
description: test
operationId: test
tags:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ tags:
paths:
/test:
post:
x-snyk-api-stability: ga
description: test
operationId: test
tags:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
openapi: 3.0.3
info:
title: Test
contact: {}
version: 3.0.0
description: Sample API
servers:
- url: /test
description: Test
tags:
- name: Test
description: test
paths:
/test:
post:
x-snyk-api-stability: beta
description: test
operationId: test
tags:
- Test
requestBody:
content:
application/vnd.api+json:
schema:
type: object
properties:
prop1:
type: string
responses:
"201":
description: success
content:
application/vnd.api+json:
schema:
type: object
location:
schema:
type: string
Loading

0 comments on commit 50105fc

Please sign in to comment.