Add cookie banner i18n service logic
Seed default English UI strings on banner creation, include translations in version snapshots, and resolve language at config-serving time. Add translation CRUD methods and the language-aware buildBannerConfig helper. Extract default categories and UI strings into defaults.go. Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
63
pkg/cookiebanner/defaults.go
Normal file
63
pkg/cookiebanner/defaults.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
|
||||||
|
//
|
||||||
|
// 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 "go.probo.inc/probo/pkg/coredata"
|
||||||
|
|
||||||
|
var defaultCategories = []struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Kind coredata.CookieCategoryKind
|
||||||
|
Rank int
|
||||||
|
}{
|
||||||
|
{"Necessary", "Essential cookies required for the website to function properly.", coredata.CookieCategoryKindNecessary, 0},
|
||||||
|
{"Analytics", "Cookies that help understand how visitors interact with the website.", coredata.CookieCategoryKindNormal, 1},
|
||||||
|
{"Advertising", "Cookies used to deliver relevant advertisements and track campaigns.", coredata.CookieCategoryKindNormal, 2},
|
||||||
|
{"Functional", "Cookies that enable enhanced functionality and personalization.", coredata.CookieCategoryKindNormal, 3},
|
||||||
|
{"Uncategorised", "Cookies that have not been assigned to a category yet.", coredata.CookieCategoryKindUncategorised, 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultUIStrings = map[string]string{
|
||||||
|
"banner_title": "Cookie Preferences",
|
||||||
|
"banner_description": "We use cookies to improve your experience and analyze site traffic. {{privacy_policy_link}}",
|
||||||
|
"button_accept_all": "Accept all",
|
||||||
|
"button_reject_all": "Reject all",
|
||||||
|
"button_customize": "Customize",
|
||||||
|
"button_save": "Save preferences",
|
||||||
|
"panel_title": "Customise Preferences",
|
||||||
|
"panel_description": "Choose which cookie categories to allow. {{necessary_category}} cookies are always active as they are needed for the site to work.",
|
||||||
|
"label_description": "Description: {{value}}",
|
||||||
|
"label_duration": "Duration: {{value}}",
|
||||||
|
"aria_close": "Close",
|
||||||
|
"aria_show_details": "Show cookie details",
|
||||||
|
"aria_hide_details": "Hide cookie details",
|
||||||
|
"aria_cookie_settings": "Cookie settings",
|
||||||
|
"privacy_policy_link_text": "Privacy Policy",
|
||||||
|
"placeholder_text": "This content requires {{category}} cookies.",
|
||||||
|
"placeholder_button": "Manage cookie preferences",
|
||||||
|
"duration_year_one": "{{count}} year",
|
||||||
|
"duration_year_other": "{{count}} years",
|
||||||
|
"duration_month_one": "{{count}} month",
|
||||||
|
"duration_month_other": "{{count}} months",
|
||||||
|
"duration_week_one": "{{count}} week",
|
||||||
|
"duration_week_other": "{{count}} weeks",
|
||||||
|
"duration_day_one": "{{count}} day",
|
||||||
|
"duration_day_other": "{{count}} days",
|
||||||
|
"duration_hour_one": "{{count}} hour",
|
||||||
|
"duration_hour_other": "{{count}} hours",
|
||||||
|
"duration_minute_one": "{{count}} minute",
|
||||||
|
"duration_minute_other": "{{count}} minutes",
|
||||||
|
"duration_session": "session",
|
||||||
|
}
|
||||||
@@ -32,4 +32,6 @@ var (
|
|||||||
ErrCookieNameAlreadyExists = errors.New("a cookie with this name already exists in this banner")
|
ErrCookieNameAlreadyExists = errors.New("a cookie with this name already exists in this banner")
|
||||||
ErrCategoriesBannerMismatch = errors.New("source and target categories belong to different banners")
|
ErrCategoriesBannerMismatch = errors.New("source and target categories belong to different banners")
|
||||||
ErrSameCategoryMove = errors.New("source and target cookie categories must be different")
|
ErrSameCategoryMove = errors.New("source and target cookie categories must be different")
|
||||||
|
ErrTranslationNotFound = errors.New("cookie banner translation not found")
|
||||||
|
ErrTranslationAlreadyExists = errors.New("translation for this language already exists")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
package cookiebanner
|
package cookiebanner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"maps"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -39,19 +40,6 @@ func NewService(pgClient *pg.Client, showBranding bool) *Service {
|
|||||||
return &Service{pg: pgClient, showBranding: showBranding}
|
return &Service{pg: pgClient, showBranding: showBranding}
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultCategories = []struct {
|
|
||||||
Name string
|
|
||||||
Description string
|
|
||||||
Kind coredata.CookieCategoryKind
|
|
||||||
Rank int
|
|
||||||
}{
|
|
||||||
{"Necessary", "Essential cookies required for the website to function properly.", coredata.CookieCategoryKindNecessary, 0},
|
|
||||||
{"Analytics", "Cookies that help understand how visitors interact with the website.", coredata.CookieCategoryKindNormal, 1},
|
|
||||||
{"Advertising", "Cookies used to deliver relevant advertisements and track campaigns.", coredata.CookieCategoryKindNormal, 2},
|
|
||||||
{"Functional", "Cookies that enable enhanced functionality and personalization.", coredata.CookieCategoryKindNormal, 3},
|
|
||||||
{"Uncategorised", "Cookies that have not been assigned to a category yet.", coredata.CookieCategoryKindUncategorised, 4},
|
|
||||||
}
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
CreateCookieBannerRequest struct {
|
CreateCookieBannerRequest struct {
|
||||||
OrganizationID gid.GID
|
OrganizationID gid.GID
|
||||||
@@ -139,11 +127,26 @@ type (
|
|||||||
BannerConfig struct {
|
BannerConfig struct {
|
||||||
BannerID gid.GID `json:"banner_id"`
|
BannerID gid.GID `json:"banner_id"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
DefaultLanguage string `json:"default_language"`
|
||||||
|
AvailableLanguages []string `json:"available_languages"`
|
||||||
PrivacyPolicyURL string `json:"privacy_policy_url"`
|
PrivacyPolicyURL string `json:"privacy_policy_url"`
|
||||||
ConsentExpiryDays int `json:"consent_expiry_days"`
|
ConsentExpiryDays int `json:"consent_expiry_days"`
|
||||||
ConsentMode string `json:"consent_mode"`
|
ConsentMode string `json:"consent_mode"`
|
||||||
ShowBranding bool `json:"show_branding"`
|
ShowBranding bool `json:"show_branding"`
|
||||||
Categories []coredata.CookieBannerVersionSnapshotCategory `json:"categories"`
|
Categories []coredata.CookieBannerVersionSnapshotCategory `json:"categories"`
|
||||||
|
Texts map[string]string `json:"texts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UpsertCookieBannerTranslationRequest struct {
|
||||||
|
CookieBannerID gid.GID
|
||||||
|
Language string
|
||||||
|
Translations json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteCookieBannerTranslationRequest struct {
|
||||||
|
CookieBannerID gid.GID
|
||||||
|
Language string
|
||||||
}
|
}
|
||||||
|
|
||||||
VisitorConsent struct {
|
VisitorConsent struct {
|
||||||
@@ -263,6 +266,24 @@ func (r *RecordConsentRequest) Validate() error {
|
|||||||
return v.Error()
|
return v.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *UpsertCookieBannerTranslationRequest) Validate() error {
|
||||||
|
v := validator.New()
|
||||||
|
|
||||||
|
v.Check(r.CookieBannerID, "cookie_banner_id", validator.Required(), validator.GID(coredata.CookieBannerEntityType))
|
||||||
|
v.Check(r.Language, "language", validator.Required(), validator.SafeTextNoNewLine(10))
|
||||||
|
|
||||||
|
return v.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DeleteCookieBannerTranslationRequest) Validate() error {
|
||||||
|
v := validator.New()
|
||||||
|
|
||||||
|
v.Check(r.CookieBannerID, "cookie_banner_id", validator.Required(), validator.GID(coredata.CookieBannerEntityType))
|
||||||
|
v.Check(r.Language, "language", validator.Required(), validator.SafeTextNoNewLine(10))
|
||||||
|
|
||||||
|
return v.Error()
|
||||||
|
}
|
||||||
|
|
||||||
func CanonicalizeOrigin(raw string) string {
|
func CanonicalizeOrigin(raw string) string {
|
||||||
u, err := url.Parse(raw)
|
u, err := url.Parse(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -284,6 +305,7 @@ func buildSnapshot(
|
|||||||
banner *coredata.CookieBanner,
|
banner *coredata.CookieBanner,
|
||||||
categories coredata.CookieCategories,
|
categories coredata.CookieCategories,
|
||||||
allCookies coredata.Cookies,
|
allCookies coredata.Cookies,
|
||||||
|
translations coredata.CookieBannerTranslations,
|
||||||
) coredata.CookieBannerVersionSnapshot {
|
) coredata.CookieBannerVersionSnapshot {
|
||||||
cookiesByCategory := make(map[gid.GID]coredata.CookieItems)
|
cookiesByCategory := make(map[gid.GID]coredata.CookieItems)
|
||||||
for _, c := range allCookies {
|
for _, c := range allCookies {
|
||||||
@@ -311,14 +333,76 @@ func buildSnapshot(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
snapshotTranslations := buildSnapshotTranslations(translations, categories)
|
||||||
|
|
||||||
return coredata.CookieBannerVersionSnapshot{
|
return coredata.CookieBannerVersionSnapshot{
|
||||||
PrivacyPolicyURL: banner.PrivacyPolicyURL,
|
PrivacyPolicyURL: banner.PrivacyPolicyURL,
|
||||||
ConsentExpiryDays: banner.ConsentExpiryDays,
|
ConsentExpiryDays: banner.ConsentExpiryDays,
|
||||||
ConsentMode: string(banner.ConsentMode),
|
ConsentMode: string(banner.ConsentMode),
|
||||||
|
DefaultLanguage: banner.DefaultLanguage,
|
||||||
Categories: snapshotCategories,
|
Categories: snapshotCategories,
|
||||||
|
Translations: snapshotTranslations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildSnapshotTranslations(
|
||||||
|
translations coredata.CookieBannerTranslations,
|
||||||
|
categories coredata.CookieCategories,
|
||||||
|
) map[string]coredata.CookieBannerVersionSnapshotTranslation {
|
||||||
|
if len(translations) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]coredata.CookieBannerVersionSnapshotTranslation, len(translations))
|
||||||
|
|
||||||
|
for _, t := range translations {
|
||||||
|
var raw struct {
|
||||||
|
Categories map[string]struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
} `json:"categories"`
|
||||||
|
}
|
||||||
|
_ = json.Unmarshal(t.Translations, &raw)
|
||||||
|
|
||||||
|
ui := make(map[string]string)
|
||||||
|
var flat map[string]json.RawMessage
|
||||||
|
_ = json.Unmarshal(t.Translations, &flat)
|
||||||
|
for k, v := range flat {
|
||||||
|
if k == "categories" || k == "cookies" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
if json.Unmarshal(v, &s) == nil {
|
||||||
|
ui[k] = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catTranslations := make([]coredata.CookieBannerVersionSnapshotCategoryTranslation, len(categories))
|
||||||
|
for i, c := range categories {
|
||||||
|
if raw.Categories != nil {
|
||||||
|
if ct, ok := raw.Categories[c.ID.String()]; ok {
|
||||||
|
catTranslations[i] = coredata.CookieBannerVersionSnapshotCategoryTranslation{
|
||||||
|
Name: ct.Name,
|
||||||
|
Description: ct.Description,
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catTranslations[i] = coredata.CookieBannerVersionSnapshotCategoryTranslation{
|
||||||
|
Name: c.Name,
|
||||||
|
Description: c.Description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result[t.Language] = coredata.CookieBannerVersionSnapshotTranslation{
|
||||||
|
UI: ui,
|
||||||
|
Categories: catTranslations,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) ensureDraftVersion(
|
func (s *Service) ensureDraftVersion(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
tx pg.Tx,
|
tx pg.Tx,
|
||||||
@@ -326,8 +410,9 @@ func (s *Service) ensureDraftVersion(
|
|||||||
banner *coredata.CookieBanner,
|
banner *coredata.CookieBanner,
|
||||||
categories coredata.CookieCategories,
|
categories coredata.CookieCategories,
|
||||||
allCookies coredata.Cookies,
|
allCookies coredata.Cookies,
|
||||||
|
translations coredata.CookieBannerTranslations,
|
||||||
) (*coredata.CookieBannerVersion, error) {
|
) (*coredata.CookieBannerVersion, error) {
|
||||||
snapshot := buildSnapshot(banner, categories, allCookies)
|
snapshot := buildSnapshot(banner, categories, allCookies, translations)
|
||||||
|
|
||||||
var latest coredata.CookieBannerVersion
|
var latest coredata.CookieBannerVersion
|
||||||
err := latest.LoadLatestByCookieBannerID(ctx, tx, scope, banner.ID)
|
err := latest.LoadLatestByCookieBannerID(ctx, tx, scope, banner.ID)
|
||||||
@@ -395,7 +480,12 @@ func (s *Service) ensureDraftVersionForBanner(
|
|||||||
return nil, fmt.Errorf("cannot load cookies: %w", err)
|
return nil, fmt.Errorf("cannot load cookies: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.ensureDraftVersion(ctx, tx, scope, &banner, categories, allCookies)
|
var translations coredata.CookieBannerTranslations
|
||||||
|
if err := translations.LoadAllByCookieBannerID(ctx, tx, scope, bannerID); err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot load cookie banner translations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.ensureDraftVersion(ctx, tx, scope, &banner, categories, allCookies, translations)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) CreateCookieBanner(
|
func (s *Service) CreateCookieBanner(
|
||||||
@@ -424,6 +514,7 @@ func (s *Service) CreateCookieBanner(
|
|||||||
ConsentExpiryDays: req.ConsentExpiryDays,
|
ConsentExpiryDays: req.ConsentExpiryDays,
|
||||||
ConsentMode: req.ConsentMode,
|
ConsentMode: req.ConsentMode,
|
||||||
ShowBranding: s.showBranding,
|
ShowBranding: s.showBranding,
|
||||||
|
DefaultLanguage: "en",
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
@@ -453,6 +544,25 @@ func (s *Service) CreateCookieBanner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultTranslationsJSON, err := json.Marshal(defaultUIStrings)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot marshal default UI strings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultTranslation := &coredata.CookieBannerTranslation{
|
||||||
|
ID: gid.New(scope.GetTenantID(), coredata.CookieBannerTranslationEntityType),
|
||||||
|
OrganizationID: banner.OrganizationID,
|
||||||
|
CookieBannerID: banner.ID,
|
||||||
|
Language: "en",
|
||||||
|
Translations: defaultTranslationsJSON,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := defaultTranslation.Insert(ctx, tx, scope); err != nil {
|
||||||
|
return fmt.Errorf("cannot insert default translation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, banner.ID); err != nil {
|
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, banner.ID); err != nil {
|
||||||
return fmt.Errorf("cannot ensure draft version: %w", err)
|
return fmt.Errorf("cannot ensure draft version: %w", err)
|
||||||
}
|
}
|
||||||
@@ -1538,6 +1648,7 @@ func (s *Service) CountCookieConsentRecordsForBanner(
|
|||||||
func (s *Service) GetActiveBannerConfig(
|
func (s *Service) GetActiveBannerConfig(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
bannerID gid.GID,
|
bannerID gid.GID,
|
||||||
|
lang string,
|
||||||
) (*BannerConfig, error) {
|
) (*BannerConfig, error) {
|
||||||
var config *BannerConfig
|
var config *BannerConfig
|
||||||
|
|
||||||
@@ -1567,15 +1678,7 @@ func (s *Service) GetActiveBannerConfig(
|
|||||||
return fmt.Errorf("cannot get version snapshot: %w", err)
|
return fmt.Errorf("cannot get version snapshot: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config = &BannerConfig{
|
config = buildBannerConfig(&banner, &version, &snapshot, lang)
|
||||||
BannerID: banner.ID,
|
|
||||||
Version: version.Version,
|
|
||||||
PrivacyPolicyURL: snapshot.PrivacyPolicyURL,
|
|
||||||
ConsentExpiryDays: snapshot.ConsentExpiryDays,
|
|
||||||
ConsentMode: snapshot.ConsentMode,
|
|
||||||
ShowBranding: banner.ShowBranding,
|
|
||||||
Categories: snapshot.Categories,
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@@ -1587,6 +1690,68 @@ func (s *Service) GetActiveBannerConfig(
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildBannerConfig(
|
||||||
|
banner *coredata.CookieBanner,
|
||||||
|
version *coredata.CookieBannerVersion,
|
||||||
|
snapshot *coredata.CookieBannerVersionSnapshot,
|
||||||
|
lang string,
|
||||||
|
) *BannerConfig {
|
||||||
|
defaultLang := snapshot.DefaultLanguage
|
||||||
|
if defaultLang == "" {
|
||||||
|
defaultLang = "en"
|
||||||
|
}
|
||||||
|
|
||||||
|
availableLanguages := make([]string, 0, len(snapshot.Translations))
|
||||||
|
for l := range snapshot.Translations {
|
||||||
|
availableLanguages = append(availableLanguages, l)
|
||||||
|
}
|
||||||
|
if len(availableLanguages) == 0 {
|
||||||
|
availableLanguages = []string{defaultLang}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedLang := defaultLang
|
||||||
|
if lang != "" {
|
||||||
|
if _, ok := snapshot.Translations[lang]; ok {
|
||||||
|
resolvedLang = lang
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
categories := snapshot.Categories
|
||||||
|
texts := make(map[string]string)
|
||||||
|
|
||||||
|
if t, ok := snapshot.Translations[resolvedLang]; ok {
|
||||||
|
maps.Copy(texts, t.UI)
|
||||||
|
|
||||||
|
if len(t.Categories) == len(categories) {
|
||||||
|
translated := make([]coredata.CookieBannerVersionSnapshotCategory, len(categories))
|
||||||
|
copy(translated, categories)
|
||||||
|
for i, ct := range t.Categories {
|
||||||
|
if ct.Name != "" {
|
||||||
|
translated[i].Name = ct.Name
|
||||||
|
}
|
||||||
|
if ct.Description != "" {
|
||||||
|
translated[i].Description = ct.Description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
categories = translated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BannerConfig{
|
||||||
|
BannerID: banner.ID,
|
||||||
|
Version: version.Version,
|
||||||
|
Language: resolvedLang,
|
||||||
|
DefaultLanguage: defaultLang,
|
||||||
|
AvailableLanguages: availableLanguages,
|
||||||
|
PrivacyPolicyURL: snapshot.PrivacyPolicyURL,
|
||||||
|
ConsentExpiryDays: snapshot.ConsentExpiryDays,
|
||||||
|
ConsentMode: snapshot.ConsentMode,
|
||||||
|
ShowBranding: banner.ShowBranding,
|
||||||
|
Categories: categories,
|
||||||
|
Texts: texts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) SetShowBranding(
|
func (s *Service) SetShowBranding(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
bannerID gid.GID,
|
bannerID gid.GID,
|
||||||
@@ -1608,6 +1773,126 @@ func (s *Service) SetShowBranding(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) UpsertCookieBannerTranslation(
|
||||||
|
ctx context.Context,
|
||||||
|
scope coredata.Scoper,
|
||||||
|
req UpsertCookieBannerTranslationRequest,
|
||||||
|
) (*coredata.CookieBannerTranslation, error) {
|
||||||
|
if err := req.Validate(); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result *coredata.CookieBannerTranslation
|
||||||
|
|
||||||
|
err := s.pg.WithTx(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, tx pg.Tx) error {
|
||||||
|
var banner coredata.CookieBanner
|
||||||
|
if err := banner.LoadByID(ctx, tx, scope, req.CookieBannerID); err != nil {
|
||||||
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
|
return ErrBannerNotFound
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot load cookie banner: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
var existing coredata.CookieBannerTranslation
|
||||||
|
err := existing.LoadByCookieBannerIDAndLanguage(ctx, tx, scope, req.CookieBannerID, req.Language)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
existing.Translations = req.Translations
|
||||||
|
existing.UpdatedAt = now
|
||||||
|
if err := existing.Update(ctx, tx, scope); err != nil {
|
||||||
|
return fmt.Errorf("cannot update cookie banner translation: %w", err)
|
||||||
|
}
|
||||||
|
result = &existing
|
||||||
|
} else if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
|
t := &coredata.CookieBannerTranslation{
|
||||||
|
ID: gid.New(scope.GetTenantID(), coredata.CookieBannerTranslationEntityType),
|
||||||
|
OrganizationID: banner.OrganizationID,
|
||||||
|
CookieBannerID: req.CookieBannerID,
|
||||||
|
Language: req.Language,
|
||||||
|
Translations: req.Translations,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
if err := t.Insert(ctx, tx, scope); err != nil {
|
||||||
|
return fmt.Errorf("cannot insert cookie banner translation: %w", err)
|
||||||
|
}
|
||||||
|
result = t
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("cannot load cookie banner translation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, req.CookieBannerID); err != nil {
|
||||||
|
return fmt.Errorf("cannot ensure draft version: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) DeleteCookieBannerTranslation(
|
||||||
|
ctx context.Context,
|
||||||
|
scope coredata.Scoper,
|
||||||
|
req DeleteCookieBannerTranslationRequest,
|
||||||
|
) error {
|
||||||
|
if err := req.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.pg.WithTx(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, tx pg.Tx) error {
|
||||||
|
var translation coredata.CookieBannerTranslation
|
||||||
|
err := translation.LoadByCookieBannerIDAndLanguage(ctx, tx, scope, req.CookieBannerID, req.Language)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
|
return ErrTranslationNotFound
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot load cookie banner translation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := translation.Delete(ctx, tx, scope); err != nil {
|
||||||
|
return fmt.Errorf("cannot delete cookie banner translation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, req.CookieBannerID); err != nil {
|
||||||
|
return fmt.Errorf("cannot ensure draft version: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ListCookieBannerTranslations(
|
||||||
|
ctx context.Context,
|
||||||
|
scope coredata.Scoper,
|
||||||
|
cookieBannerID gid.GID,
|
||||||
|
) (coredata.CookieBannerTranslations, error) {
|
||||||
|
var translations coredata.CookieBannerTranslations
|
||||||
|
|
||||||
|
err := s.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
|
return translations.LoadAllByCookieBannerID(ctx, conn, scope, cookieBannerID)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return translations, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GetVisitorConsent(
|
func (s *Service) GetVisitorConsent(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
bannerID gid.GID,
|
bannerID gid.GID,
|
||||||
|
|||||||
Reference in New Issue
Block a user