diff --git a/pkg/cookiebanner/equal.go b/pkg/cookiebanner/equal.go new file mode 100644 index 000000000..ee32194af --- /dev/null +++ b/pkg/cookiebanner/equal.go @@ -0,0 +1,45 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package cookiebanner + +import ( + "encoding/json" + "fmt" + "reflect" +) + +// ptrEqual reports whether two nullable values are equal: both nil are equal, +// one nil is not equal to a non-nil, otherwise the pointed-to values are +// compared with ==. +func ptrEqual[T comparable](a, b *T) bool { + if a == nil || b == nil { + return a == b + } + return *a == *b +} + +// jsonEqual reports whether two JSON blobs are semantically identical after +// normalising whitespace and key ordering. Array element order is preserved +// as significant. +func jsonEqual(a, b json.RawMessage) (bool, error) { + var av, bv any + if err := json.Unmarshal(a, &av); err != nil { + return false, fmt.Errorf("cannot unmarshal first json blob: %w", err) + } + if err := json.Unmarshal(b, &bv); err != nil { + return false, fmt.Errorf("cannot unmarshal second json blob: %w", err) + } + return reflect.DeepEqual(av, bv), nil +} diff --git a/pkg/cookiebanner/service.go b/pkg/cookiebanner/service.go index 7895e2621..9ed3b0d60 100644 --- a/pkg/cookiebanner/service.go +++ b/pkg/cookiebanner/service.go @@ -27,7 +27,6 @@ import ( "go.gearno.de/kit/pg" "go.probo.inc/probo/pkg/coredata" - "go.probo.inc/probo/pkg/equal" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/validator" @@ -759,7 +758,7 @@ func (s *Service) UpdateCookieBanner( } nameChanged := req.Name != nil && *req.Name != banner.Name - privacyChanged := req.PrivacyPolicyURL != nil && !equal.Ptr(req.PrivacyPolicyURL, banner.PrivacyPolicyURL) + privacyChanged := req.PrivacyPolicyURL != nil && !ptrEqual(req.PrivacyPolicyURL, banner.PrivacyPolicyURL) cookiePolicyChanged := req.CookiePolicyURL != nil && *req.CookiePolicyURL != banner.CookiePolicyURL expiryChanged := req.ConsentExpiryDays != nil && *req.ConsentExpiryDays != banner.ConsentExpiryDays consentModeChanged := req.ConsentMode != nil && *req.ConsentMode != banner.ConsentMode @@ -1229,7 +1228,7 @@ func (s *Service) UpdateCookiePattern( } displayNameChanged := req.DisplayName != nil && *req.DisplayName != pattern.DisplayName - maxAgeChanged := req.MaxAgeSeconds != nil && !equal.Ptr(*req.MaxAgeSeconds, pattern.MaxAgeSeconds) + maxAgeChanged := req.MaxAgeSeconds != nil && !ptrEqual(*req.MaxAgeSeconds, pattern.MaxAgeSeconds) descChanged := req.Description != nil && *req.Description != pattern.Description excludedChanged := req.Excluded != nil && *req.Excluded != pattern.Excluded @@ -2019,7 +2018,7 @@ func (s *Service) UpsertCookieBannerTranslation( err := existing.LoadByCookieBannerIDAndLanguage(ctx, tx, scope, req.CookieBannerID, req.Language) if err == nil { - same, eqErr := equal.JSON(existing.Translations, req.Translations) + same, eqErr := jsonEqual(existing.Translations, req.Translations) if eqErr == nil && same { result = &existing return nil