diff --git a/checker/checker.go b/checker/checker.go index 93f0c7dc..ca42c796 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -35,6 +35,14 @@ func (errs BackwardCompatibilityErrors) HasLevelOrHigher(level Level) bool { return false } +func (errs BackwardCompatibilityErrors) GetLevelCount() map[Level]int { + counts := map[Level]int{} + for _, err := range errs { + counts[err.Level] = counts[err.Level] + 1 + } + return counts +} + func (bcErrors BackwardCompatibilityErrors) Len() int { return len(bcErrors) } @@ -106,13 +114,9 @@ func IsPipedOutput() bool { return *pipedOutput } -func (r *BackwardCompatibilityError) PrettyErrorText(l localizations.Localizer) string { - if IsPipedOutput() { - return r.LocalizedError(l) - } - +func PrettyLevelText(level Level) string { var levelName string - switch r.Level { + switch level { case ERR: levelName = color.InRed("error") case WARN: @@ -122,6 +126,16 @@ func (r *BackwardCompatibilityError) PrettyErrorText(l localizations.Localizer) default: levelName = color.InGray("issue") } + + return levelName +} + +func (r *BackwardCompatibilityError) PrettyErrorText(l localizations.Localizer) string { + if IsPipedOutput() { + return r.LocalizedError(l) + } + + levelName := PrettyLevelText(r.Level) comment := "" if r.Comment != "" { comment = fmt.Sprintf("\n\t\t%s", r.Comment) diff --git a/checker/localizations/localizations.go b/checker/localizations/localizations.go index 7a23b284..b24da58d 100644 --- a/checker/localizations/localizations.go +++ b/checker/localizations/localizations.go @@ -1,6 +1,6 @@ // Code generated by go-localize; DO NOT EDIT. // This file was generated by robots at -// 2023-06-29 18:16:42.541901 +0100 IST m=+0.004539542 +// 2023-07-07 00:15:12.124643 +0300 IDT m=+0.003223091 package localizations @@ -160,7 +160,8 @@ var localizations = map[string]string{ "en.messages.response-success-status-added": "added the success response with the status %s", "en.messages.response-success-status-removed": "removed the success response with the status %s", "en.messages.sunset-deleted": "api sunset date deleted, but deprecated=true kept", - "en.messages.total-errors": "Backward compatibility errors (%d):\n", + "en.messages.total-changes": "%d changes: %d %s, %d %s, %d %s\n", + "en.messages.total-errors": "%d breaking changes: %d %s, %d %s\n", "ru.messages.added-required-request-body": "добавлено обязательное тело запроса", "ru.messages.api-deprecated-sunset-parse": "API deprecated без валидно парсящейся '%s' даты sunset: %v", "ru.messages.api-global-security-added": "схема безопасности %s была добавлена к API", @@ -309,7 +310,8 @@ var localizations = map[string]string{ "ru.messages.response-success-status-added": "добавлен ответ об успехе со статусом %s", "ru.messages.response-success-status-removed": "удален успешный (2xx) статус ответа %s", "ru.messages.sunset-deleted": "удалена дата sunset date у API, но сохранён deprecated=true", - "ru.messages.total-errors": "Ошибки обратной совместимости (всего: %d):\n", + "ru.messages.total-changes": "%d изменений: %d %s, %d %s, %d %s\n", + "ru.messages.total-errors": "%d критические изменения: %d %s, %d %s\n", } type Replacements map[string]interface{} diff --git a/checker/localizations_src/en/messages.yaml b/checker/localizations_src/en/messages.yaml index ae20a49c..4e3d84fc 100644 --- a/checker/localizations_src/en/messages.yaml +++ b/checker/localizations_src/en/messages.yaml @@ -2,7 +2,8 @@ at: at in: in added-required-request-body: added required request body request-parameter-removed: deleted the %s request parameter %s -total-errors: "Backward compatibility errors (%d):\\n" +total-errors: "%d breaking changes: %d %s, %d %s\\n" +total-changes: "%d changes: %d %s, %d %s, %d %s\\n" request-parameter-pattern-added: "added the pattern '%s' for the %s request parameter %s" request-parameter-pattern-changed: "changed the pattern for the %s request parameter %s from '%s' to '%s'" request-property-pattern-added: "added the pattern '%s' for the request property %s" diff --git a/checker/localizations_src/ru/messages.yaml b/checker/localizations_src/ru/messages.yaml index 1c041d9f..64bdc9dc 100644 --- a/checker/localizations_src/ru/messages.yaml +++ b/checker/localizations_src/ru/messages.yaml @@ -2,7 +2,8 @@ at: в in: в added-required-request-body: добавлено обязательное тело запроса request-parameter-removed: удалён %s параметр запроса %s -total-errors: "Ошибки обратной совместимости (всего: %d):\\n" +total-errors: "%d критические изменения: %d %s, %d %s\\n" +total-changes: "%d изменений: %d %s, %d %s, %d %s\\n" request-parameter-pattern-added: добавлен pattern '%s' у %s параметра запроса %s request-parameter-pattern-changed: изменён pattern у %s параметра запроса %s со значения '%s' на значение '%s' request-property-pattern-added: добавлен pattern '%s' у поля запроса %s diff --git a/diff/example_test.go b/diff/example_test.go index 8aac3c0c..5a2c5504 100644 --- a/diff/example_test.go +++ b/diff/example_test.go @@ -109,14 +109,15 @@ func ExampleGetPathsDiff() { // pretty print breaking changes errors if len(errs) > 0 { - fmt.Printf(c.Localizer.Get("messages.total-errors"), len(errs)) + count := errs.GetLevelCount() + fmt.Printf(c.Localizer.Get("messages.total-errors"), len(errs), count[checker.ERR], "error", count[checker.WARN], "warning") for _, bcerr := range errs { fmt.Printf("%s\n\n", strings.TrimRight(bcerr.PrettyErrorText(c.Localizer), " ")) } } // Output: - // Backward compatibility errors (4): + // 4 breaking changes: 1 error, 3 warning // error at ../data/openapi-test3.yaml, in API GET /api/{domain}/{project}/badges/security-score removed the success response with the status '201' [response-success-status-removed]. // // warning at ../data/openapi-test3.yaml, in API GET /api/{domain}/{project}/badges/security-score deleted the 'cookie' request parameter 'test' [request-parameter-removed]. diff --git a/internal/breaking_changes.go b/internal/breaking_changes.go index b2628b84..78dd9f79 100644 --- a/internal/breaking_changes.go +++ b/internal/breaking_changes.go @@ -1,6 +1,7 @@ package internal import ( + "fmt" "io" "github.com/spf13/cobra" @@ -64,5 +65,18 @@ In 'composed' mode, base and revision can be a glob and oasdiff will compare mat } func runBreakingChanges(flags *ChangelogFlags, stdout io.Writer) (bool, *ReturnError) { - return getChangelog(flags, stdout, checker.WARN) + return getChangelog(flags, stdout, checker.WARN, getBreakingChangesTitle) +} + +func getBreakingChangesTitle(config checker.BackwardCompatibilityCheckConfig, errs checker.BackwardCompatibilityErrors) string { + count := errs.GetLevelCount() + + return fmt.Sprintf( + config.Localizer.Get("messages.total-errors"), + len(errs), + count[checker.ERR], + checker.PrettyLevelText(checker.ERR), + count[checker.WARN], + checker.PrettyLevelText(checker.WARN), + ) } diff --git a/internal/changelog.go b/internal/changelog.go index 94945791..2698c145 100644 --- a/internal/changelog.go +++ b/internal/changelog.go @@ -66,10 +66,10 @@ In 'composed' mode, base and revision can be a glob and oasdiff will compare mat } func runChangelog(flags *ChangelogFlags, stdout io.Writer) (bool, *ReturnError) { - return getChangelog(flags, stdout, checker.INFO) + return getChangelog(flags, stdout, checker.INFO, getChangelogTitle) } -func getChangelog(flags *ChangelogFlags, stdout io.Writer, level checker.Level) (bool, *ReturnError) { +func getChangelog(flags *ChangelogFlags, stdout io.Writer, level checker.Level, getOutputTitle GetOutputTitle) (bool, *ReturnError) { openapi3.CircularReferenceCounter = flags.circularReferenceCounter @@ -89,7 +89,7 @@ func getChangelog(flags *ChangelogFlags, stdout io.Writer, level checker.Level) return false, returnErr } - if returnErr := outputChangelog(bcConfig, flags.format, stdout, errs); returnErr != nil { + if returnErr := outputChangelog(bcConfig, flags.format, stdout, errs, getOutputTitle); returnErr != nil { return false, returnErr } @@ -125,7 +125,24 @@ func filterIgnored(errs checker.BackwardCompatibilityErrors, warnIgnoreFile stri return errs, nil } -func outputChangelog(config checker.BackwardCompatibilityCheckConfig, format string, stdout io.Writer, errs checker.BackwardCompatibilityErrors) *ReturnError { +func getChangelogTitle(config checker.BackwardCompatibilityCheckConfig, errs checker.BackwardCompatibilityErrors) string { + count := errs.GetLevelCount() + + return fmt.Sprintf( + config.Localizer.Get("messages.total-changes"), + len(errs), + count[checker.ERR], + checker.PrettyLevelText(checker.ERR), + count[checker.WARN], + checker.PrettyLevelText(checker.WARN), + count[checker.INFO], + checker.PrettyLevelText(checker.INFO), + ) +} + +type GetOutputTitle func(config checker.BackwardCompatibilityCheckConfig, errs checker.BackwardCompatibilityErrors) string + +func outputChangelog(config checker.BackwardCompatibilityCheckConfig, format string, stdout io.Writer, errs checker.BackwardCompatibilityErrors, getOutputTitle GetOutputTitle) *ReturnError { switch format { case FormatYAML: if err := printYAML(stdout, errs); err != nil { @@ -137,7 +154,7 @@ func outputChangelog(config checker.BackwardCompatibilityCheckConfig, format str } case FormatText: if len(errs) > 0 { - fmt.Fprintf(stdout, config.Localizer.Get("messages.total-errors"), len(errs)) + fmt.Fprint(stdout, getOutputTitle(config, errs)) } for _, bcerr := range errs { diff --git a/internal/run_test.go b/internal/run_test.go index 7fbfd79d..55e6a734 100644 --- a/internal/run_test.go +++ b/internal/run_test.go @@ -207,3 +207,14 @@ func Test_BreakingChangesChangelogOptionalCheckersAreInfoLevel(t *testing.T) { require.Equal(t, c.Level, checker.INFO) } } + +func Test_BreakingChangesChangelogOptionalCheckersAreErrorLevelWhenSpecified(t *testing.T) { + var stdout bytes.Buffer + require.Zero(t, internal.Run(cmdToArgs("oasdiff changelog ../data/run_test/changelog_include_checks_base.yaml ../data/run_test/changelog_include_checks_revision.yaml --format json --include-checks api-tag-removed,response-non-success-status-removed"), &stdout, io.Discard)) + cl := checker.BackwardCompatibilityErrors{} + require.NoError(t, json.Unmarshal(stdout.Bytes(), &cl)) + require.Len(t, cl, 2) + for _, c := range cl { + require.Equal(t, c.Level, checker.ERR) + } +}