diff --git a/tools/cli/internal/breakingchanges/exemptions.go b/tools/cli/internal/breakingchanges/exemptions.go index bad9728df1..52f05b3f48 100644 --- a/tools/cli/internal/breakingchanges/exemptions.go +++ b/tools/cli/internal/breakingchanges/exemptions.go @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + package breakingchanges import ( @@ -54,6 +55,29 @@ func isWithinExpirationDate(exemption Exemption) bool { return exemptUntil.After(date) || exemptUntil.Equal(date) } +func validateExemption(exemption Exemption) error { + if _, err := time.Parse("2006-01-02", exemption.ExemptUntil); err != nil { + return fmt.Errorf("validation error: %v. Exemption: %s", err, exemption) + } + + if err := validateField(exemption.Reason, "reason", exemption); err != nil { + return err + } + + if err := validateField(exemption.BreakingChangeDescription, "breaking_change_description", exemption); err != nil { + return err + } + + return validateField(exemption.ExemptUntil, "exempt_until", exemption) +} + +func validateField(fieldValue, fieldName string, exemption Exemption) error { + if fieldValue == "" { + return fmt.Errorf("validation error: empty value for the '%s' field is not allowed. Exemption: '%s'", fieldName, exemption) + } + return nil +} + func transformComponentEntry(breakingChangeDescription string) string { if strings.Contains(breakingChangeDescription, "api-schema-removed") && !strings.Contains(breakingChangeDescription, "in components") { return fmt.Sprintf("in components %s", breakingChangeDescription) @@ -85,6 +109,10 @@ func GetValidExemptionsList(exemptionsPath string, ignoreExpiration bool, fs afe var validExemptions []Exemption for _, exemption := range exemptions { + if err := validateExemption(exemption); err != nil { + return nil, err + } + if ignoreExpiration || isWithinExpirationDate(exemption) { validExemptions = append(validExemptions, exemption) } diff --git a/tools/cli/internal/breakingchanges/exemptions_test.go b/tools/cli/internal/breakingchanges/exemptions_test.go index bc36d3ea6a..84fad02549 100644 --- a/tools/cli/internal/breakingchanges/exemptions_test.go +++ b/tools/cli/internal/breakingchanges/exemptions_test.go @@ -118,3 +118,78 @@ func TestGenerateExemptionsFileWithFs(t *testing.T) { assert.Equal(t, expectedContent, string(data)) }) } + +func TestValidateExemption(t *testing.T) { + tests := []struct { + name string + exemption Exemption + expectedError require.ErrorAssertionFunc + }{ + { + name: "Valid exemption", + exemption: Exemption{ + Reason: "Some reason", + BreakingChangeDescription: "Description of breaking change", + HideFromChangelog: "false", + ExemptUntil: "2024-12-11", + }, + expectedError: require.NoError, + }, + { + name: "Invalid date format", + exemption: Exemption{ + Reason: "Some reason", + BreakingChangeDescription: "Description of breaking change", + HideFromChangelog: "false", + ExemptUntil: "invalid-date", + }, + expectedError: require.Error, + }, + { + name: "Empty Reason field", + exemption: Exemption{ + Reason: "", + BreakingChangeDescription: "Description of breaking change", + HideFromChangelog: "false", + ExemptUntil: "2024-12-11", + }, + expectedError: require.Error, + }, + { + name: "Empty BreakingChangeDescription field", + exemption: Exemption{ + Reason: "Some reason", + BreakingChangeDescription: "", + HideFromChangelog: "false", + ExemptUntil: "2024-12-11", + }, + expectedError: require.Error, + }, + { + name: "Empty HideFromChangelog field", + exemption: Exemption{ + Reason: "Some reason", + BreakingChangeDescription: "Description of breaking change", + HideFromChangelog: "", + ExemptUntil: "2024-12-11", + }, + expectedError: require.NoError, + }, + { + name: "Empty ExemptUntil field", + exemption: Exemption{ + Reason: "Some reason", + BreakingChangeDescription: "Description of breaking change", + HideFromChangelog: "false", + ExemptUntil: "", + }, + expectedError: require.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.expectedError(t, validateExemption(tt.exemption)) + }) + } +} diff --git a/tools/cli/internal/cli/breakingchanges/exemptions/parse.go b/tools/cli/internal/cli/breakingchanges/exemptions/parse.go index e6363fd094..f6cbd2c242 100644 --- a/tools/cli/internal/cli/breakingchanges/exemptions/parse.go +++ b/tools/cli/internal/cli/breakingchanges/exemptions/parse.go @@ -51,7 +51,7 @@ func (o *Opts) PreRunE(_ []string) error { return nil } -// Builder builds the merge command with the following signature: +// ParseBuilder builds the merge command with the following signature: // breaking-changes exemptions parse -e file_path func ParseBuilder() *cobra.Command { opts := &Opts{ diff --git a/tools/cli/test/data/changelog/new-api-version/exemptions.yaml b/tools/cli/test/data/changelog/new-api-version/exemptions.yaml index e208426476..a376ad376e 100644 --- a/tools/cli/test/data/changelog/new-api-version/exemptions.yaml +++ b/tools/cli/test/data/changelog/new-api-version/exemptions.yaml @@ -1717,6 +1717,7 @@ "hide_from_changelog": "true" - "breaking_change_description": "DELETE /api/atlas/v2/orgs/{orgId}/serviceAccounts/{serviceAccountId}/secrets/{secretId} api path removed without deprecation [api-path-removed-without-deprecation]" "exempt_until": "2024-06-01" + "reason": "Spec correction" - "breaking_change_description": "removed the schema 'DiskBackupBaseRestoreMember' [api-schema-removed]" "exempt_until": "2024-05-30" "reason": "Spec correction" diff --git a/tools/cli/test/data/changelog/same-api-version/exemptions.yaml b/tools/cli/test/data/changelog/same-api-version/exemptions.yaml index e208426476..a376ad376e 100644 --- a/tools/cli/test/data/changelog/same-api-version/exemptions.yaml +++ b/tools/cli/test/data/changelog/same-api-version/exemptions.yaml @@ -1717,6 +1717,7 @@ "hide_from_changelog": "true" - "breaking_change_description": "DELETE /api/atlas/v2/orgs/{orgId}/serviceAccounts/{serviceAccountId}/secrets/{secretId} api path removed without deprecation [api-path-removed-without-deprecation]" "exempt_until": "2024-06-01" + "reason": "Spec correction" - "breaking_change_description": "removed the schema 'DiskBackupBaseRestoreMember' [api-schema-removed]" "exempt_until": "2024-05-30" "reason": "Spec correction"