Skip to content

Commit

Permalink
add markdown format for changelog (#588)
Browse files Browse the repository at this point in the history
  • Loading branch information
reuvenharrison authored Jul 21, 2024
1 parent ca3ab5d commit 4822df6
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Additional formats can be generated using the `--format` flag:
- githubactions: suitable for integration with github
- junit: suitable for integration with gitlab
- html: [see example](https://html-preview.github.io/?url=https://github.com/Tufin/oasdiff/blob/main/docs/changelog.html)
- markdown: [see example](changelog.md)
- text: the default, human-readable, format
- singleline: displays each change on a single line, this can be useful to prepare [ignore files](#ignoring-specific-breaking-changes)

Expand Down
3 changes: 2 additions & 1 deletion docs/DIFF.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ The default diff output format is `yaml`.
Additional formats can be generated using the `--format` flag:
- yaml: includes all diff details
- json: includes all diff details
- text: designed to be more user-friendly and provide only the most important parts of the diff (also compatible with markdown)
- text: designed to be more user-friendly and provide only the most important parts of the diff (same as markdown)
- markdown: designed to be more user-friendly and provide only the most important parts of the diff (same as text)
- html: designed to be more user-friendly and provide only the most important parts of the diff (see also [changelog with html](BREAKING-CHANGES.md#output-formats))

Notes:
Expand Down
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ docker run --rm -t tufin/oasdiff changelog https://raw.githubusercontent.com/Tuf
## Features
- Detect [breaking changes](BREAKING-CHANGES.md)
- Display a user-friendly [changelog](BREAKING-CHANGES.md) of all important API changes
- OpenAPI [diff](DIFF.md) in YAML, JSON, Text, Markdown or HTML
- Generate comprehensive [diff](DIFF.md) reports including all aspects of [OpenAPI Specification](https://swagger.io/specification/): paths, operations, parameters, request bodies, responses, schemas, enums, callbacks, security etc.
- Output reports in YAML, JSON, Text, Markdown, HTML, JUnit XML or the [github actions annotation format](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-warning-message)
- Compare local files or remote files over http/s
- Compare specs in YAML or JSON format
- [Compare two collections of specs](COMPOSED.md)
- Comprehensive diff including all aspects of [OpenAPI Specification](https://swagger.io/specification/): paths, operations, parameters, request bodies, responses, schemas, enums, callbacks, security etc.
- [API deprecation](DEPRECATION.md)
- [API stability levels](STABILITY.md)
- [Multiple versions of the same endpoint](MATCHING-ENDPOINTS.md#duplicate-endpoints)
Expand Down
25 changes: 25 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# API Changelog 1.0.0 vs. 1.0.1

## GET /api/{domain}/{project}/badges/security-score
- :warning: removed the success response with the status '200'
- :warning: removed the success response with the status '201'
- :warning: deleted the 'cookie' request parameter 'test'
- :warning: deleted the 'header' request parameter 'user'
- :warning: deleted the 'query' request parameter 'filter'
- api operation id 'GetSecurityScores' removed and replaced with 'GetSecurityScore'
- api tag 'security' removed
- for the 'query' request parameter 'token', the maxLength was increased from '29' to '30'
- removed the pattern '^(?:[\w-./:]+)$' from the 'query' request parameter 'image'
- for the 'query' request parameter 'image', the type/format was generalized from 'string'/'general string' to ''/''
- removed the non-success response with the status '400'


## GET /api/{domain}/{project}/install-command
- :warning: deleted the 'header' request parameter 'network-policies'
- added the new optional 'header' request parameter 'name' to all path's operations
- added the new enum value 'test1' to the 'path' request parameter 'project'


## POST /register
- the endpoint scheme security 'bearerAuth' was removed from the API
- the security scope 'write:pets' was added to the endpoint's security scheme 'OAuth'
10 changes: 6 additions & 4 deletions formatters/format_html.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (f HTMLFormatter) RenderDiff(diff *diff.Diff, opts RenderOpts) ([]byte, err
}

//go:embed templates/changelog.html
var changelog string
var changelogHtml string

type TemplateData struct {
APIChanges ChangesByEndpoint
Expand All @@ -43,13 +43,15 @@ type TemplateData struct {
}

func (f HTMLFormatter) RenderChangelog(changes checker.Changes, opts RenderOpts, specInfoPair *load.SpecInfoPair) ([]byte, error) {
tmpl := template.Must(template.New("changelog").Parse(changelog))
tmpl := template.Must(template.New("changelog").Parse(changelogHtml))
return ExecuteHtmlTemplate(tmpl, GroupChanges(changes, f.Localizer), specInfoPair)
}

func ExecuteHtmlTemplate(tmpl *template.Template, changes ChangesByEndpoint, specInfoPair *load.SpecInfoPair) ([]byte, error) {
var out bytes.Buffer
if err := tmpl.Execute(&out, TemplateData{GroupChanges(changes, f.Localizer), specInfoPair.GetBaseVersion(), specInfoPair.GetRevisionVersion()}); err != nil {
if err := tmpl.Execute(&out, TemplateData{changes, specInfoPair.GetBaseVersion(), specInfoPair.GetRevisionVersion()}); err != nil {
return nil, err
}

return out.Bytes(), nil
}

Expand Down
13 changes: 13 additions & 0 deletions formatters/format_html_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package formatters_test

import (
"fmt"
"html/template"
"testing"

_ "embed"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tufin/oasdiff/checker"
Expand Down Expand Up @@ -72,3 +75,13 @@ func TestHtmlFormatter_NotImplemented(t *testing.T) {
_, err = htmlFormatter.RenderSummary(nil, formatters.NewRenderOpts())
assert.Error(t, err)
}

//go:embed templates/changelog.html
var changelogHtml string

func TestExecuteHtmlTemplate_Err(t *testing.T) {
tmpl := template.Must(template.New("changelog").Parse(changelogHtml))
tmpl.Tree = nil
_, err := formatters.ExecuteHtmlTemplate(tmpl, nil, nil)
assert.Error(t, err)
}
48 changes: 48 additions & 0 deletions formatters/format_markup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package formatters

import (
"bytes"
"text/template"

_ "embed"

"github.com/tufin/oasdiff/checker"
"github.com/tufin/oasdiff/diff"
"github.com/tufin/oasdiff/load"
"github.com/tufin/oasdiff/report"
)

type MarkupFormatter struct {
notImplementedFormatter
Localizer checker.Localizer
}

func newMarkupFormatter(l checker.Localizer) MarkupFormatter {
return MarkupFormatter{
Localizer: l,
}
}

func (f MarkupFormatter) RenderDiff(diff *diff.Diff, opts RenderOpts) ([]byte, error) {
return []byte(report.GetTextReportAsString(diff)), nil
}

//go:embed templates/changelog.md
var changelogMarkdown string

func (f MarkupFormatter) RenderChangelog(changes checker.Changes, opts RenderOpts, specInfoPair *load.SpecInfoPair) ([]byte, error) {
tmpl := template.Must(template.New("changelog").Parse(changelogMarkdown))
return ExecuteTextTemplate(tmpl, GroupChanges(changes, f.Localizer), specInfoPair)
}

func ExecuteTextTemplate(tmpl *template.Template, changes ChangesByEndpoint, specInfoPair *load.SpecInfoPair) ([]byte, error) {
var out bytes.Buffer
if err := tmpl.Execute(&out, TemplateData{changes, specInfoPair.GetBaseVersion(), specInfoPair.GetRevisionVersion()}); err != nil {
return nil, err
}
return out.Bytes(), nil
}

func (f MarkupFormatter) SupportedOutputs() []Output {
return []Output{OutputDiff, OutputChangelog}
}
60 changes: 60 additions & 0 deletions formatters/format_markup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package formatters_test

import (
"testing"
"text/template"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tufin/oasdiff/checker"
"github.com/tufin/oasdiff/formatters"
)

var markupFormatter = formatters.MarkupFormatter{
Localizer: MockLocalizer,
}

func TestMarkupLookup(t *testing.T) {
f, err := formatters.Lookup(string(formatters.FormatMarkup), formatters.DefaultFormatterOpts())
require.NoError(t, err)
require.IsType(t, formatters.MarkupFormatter{}, f)
}

func TestMarkupFormatter_RenderDiff(t *testing.T) {
out, err := markupFormatter.RenderDiff(nil, formatters.NewRenderOpts())
require.NoError(t, err)
require.Equal(t, string(out), "No changes\n")
}

func TestMarkupFormatter_RenderChangelog(t *testing.T) {
testChanges := checker.Changes{
checker.ApiChange{
Path: "/test",
Operation: "GET",
Id: "change_id",
Level: checker.ERR,
},
}

out, err := markupFormatter.RenderChangelog(testChanges, formatters.NewRenderOpts(), nil)
require.NoError(t, err)
require.NotEmpty(t, string(out))
}

func TestMarkupFormatter_NotImplemented(t *testing.T) {
var err error

_, err = markupFormatter.RenderChecks(formatters.Checks{}, formatters.NewRenderOpts())
assert.Error(t, err)

_, err = markupFormatter.RenderFlatten(nil, formatters.NewRenderOpts())
assert.Error(t, err)

_, err = markupFormatter.RenderSummary(nil, formatters.NewRenderOpts())
assert.Error(t, err)
}

func TestExecuteMarkupTemplate_Err(t *testing.T) {
_, err := formatters.ExecuteTextTemplate(&template.Template{}, nil, nil)
assert.Error(t, err)
}
3 changes: 3 additions & 0 deletions formatters/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var formatters = map[Format]Formatter{
FormatYAML: YAMLFormatter{},
FormatJSON: JSONFormatter{},
FormatText: TEXTFormatter{},
FormatMarkup: MarkupFormatter{},
FormatSingleLine: SingleLineFormatter{},
FormatHTML: HTMLFormatter{},
FormatGithubActions: GitHubActionsFormatter{},
Expand All @@ -44,6 +45,8 @@ func Lookup(format string, opts FormatterOpts) (Formatter, error) {
return newJSONFormatter(l), nil
case FormatText:
return newTEXTFormatter(l), nil
case FormatMarkup:
return newMarkupFormatter(l), nil
case FormatSingleLine:
return newSingleLineFormatter(l), nil
case FormatHTML:
Expand Down
6 changes: 4 additions & 2 deletions formatters/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ func TestUnsupportedLookup(t *testing.T) {

func TestDiffOutputFormats(t *testing.T) {
supportedFormats := formatters.SupportedFormatsByContentType(formatters.OutputDiff)
assert.Len(t, supportedFormats, 4)
assert.Len(t, supportedFormats, 5)
assert.Contains(t, supportedFormats, string(formatters.FormatYAML))
assert.Contains(t, supportedFormats, string(formatters.FormatJSON))
assert.Contains(t, supportedFormats, string(formatters.FormatText))
assert.Contains(t, supportedFormats, string(formatters.FormatMarkup))
assert.Contains(t, supportedFormats, string(formatters.FormatHTML))
}

Expand All @@ -31,10 +32,11 @@ func TestSummaryOutputFormats(t *testing.T) {

func TestChangelogOutputFormats(t *testing.T) {
supportedFormats := formatters.SupportedFormatsByContentType(formatters.OutputChangelog)
assert.Len(t, supportedFormats, 7)
assert.Len(t, supportedFormats, 8)
assert.Contains(t, supportedFormats, string(formatters.FormatYAML))
assert.Contains(t, supportedFormats, string(formatters.FormatJSON))
assert.Contains(t, supportedFormats, string(formatters.FormatText))
assert.Contains(t, supportedFormats, string(formatters.FormatMarkup))
assert.Contains(t, supportedFormats, string(formatters.FormatSingleLine))
assert.Contains(t, supportedFormats, string(formatters.FormatHTML))
assert.Contains(t, supportedFormats, string(formatters.FormatGithubActions))
Expand Down
6 changes: 6 additions & 0 deletions formatters/templates/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# API Changelog {{ .BaseVersion }}{{ if ne .BaseVersion .RevisionVersion }} vs. {{ .RevisionVersion }}{{ end }}
{{ range $endpoint, $changes := .APIChanges }}
## {{ $endpoint.Operation }} {{ $endpoint.Path }}
{{ range $changes }}- {{ if .IsBreaking }}:warning:{{ end }} {{ .Text }}
{{ end }}
{{ end }}
1 change: 1 addition & 0 deletions formatters/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const (
FormatYAML Format = "yaml"
FormatJSON Format = "json"
FormatText Format = "text"
FormatMarkup Format = "markup"
FormatSingleLine Format = "singleline"
FormatHTML Format = "html"
FormatGithubActions Format = "githubactions"
Expand Down

0 comments on commit 4822df6

Please sign in to comment.