Add console GraphQL schema and resolvers for i18n
Add CookieBannerTranslation type, defaultLanguage field on CookieBanner, and upsert/delete mutations for managing per-language translations from the console. Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -64,6 +64,7 @@ type (
|
||||
PrivacyPolicyURL *string
|
||||
ConsentExpiryDays *int
|
||||
ConsentMode *coredata.CookieConsentMode
|
||||
DefaultLanguage *string
|
||||
}
|
||||
|
||||
UpdateCookieCategoryRequest struct {
|
||||
@@ -706,7 +707,7 @@ func (s *Service) UpdateCookieBanner(
|
||||
return fmt.Errorf("cannot load cookie banner: %w", err)
|
||||
}
|
||||
|
||||
consentChanged := req.PrivacyPolicyURL != nil || req.ConsentExpiryDays != nil || req.ConsentMode != nil
|
||||
consentChanged := req.PrivacyPolicyURL != nil || req.ConsentExpiryDays != nil || req.ConsentMode != nil || req.DefaultLanguage != nil
|
||||
|
||||
if req.Name != nil {
|
||||
banner.Name = *req.Name
|
||||
@@ -723,6 +724,9 @@ func (s *Service) UpdateCookieBanner(
|
||||
if req.ConsentMode != nil {
|
||||
banner.ConsentMode = *req.ConsentMode
|
||||
}
|
||||
if req.DefaultLanguage != nil {
|
||||
banner.DefaultLanguage = *req.DefaultLanguage
|
||||
}
|
||||
|
||||
banner.UpdatedAt = time.Now()
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ package console_v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
@@ -66,6 +67,24 @@ func (r *cookieBannerResolver) Categories(ctx context.Context, obj *types.Cookie
|
||||
return types.NewCookieCategoryConnection(p, r, obj.ID), nil
|
||||
}
|
||||
|
||||
// Translations is the resolver for the translations field.
|
||||
func (r *cookieBannerResolver) Translations(ctx context.Context, obj *types.CookieBanner) ([]*types.CookieBannerTranslation, error) {
|
||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||
|
||||
translations, err := r.cookieBanner.ListCookieBannerTranslations(ctx, scope, obj.ID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot list cookie banner translations", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
result := make([]*types.CookieBannerTranslation, len(translations))
|
||||
for i, t := range translations {
|
||||
result[i] = types.NewCookieBannerTranslation(t)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// LatestVersion is the resolver for the latestVersion field.
|
||||
func (r *cookieBannerResolver) LatestVersion(ctx context.Context, obj *types.CookieBanner) (*types.CookieBannerVersion, error) {
|
||||
if err := r.authorize(ctx, obj.ID, probo.ActionCookieBannerVersionList); err != nil {
|
||||
@@ -254,6 +273,7 @@ func (r *mutationResolver) UpdateCookieBanner(ctx context.Context, input types.U
|
||||
PrivacyPolicyURL: input.PrivacyPolicyURL,
|
||||
ConsentExpiryDays: input.ConsentExpiryDays,
|
||||
ConsentMode: input.ConsentMode,
|
||||
DefaultLanguage: input.DefaultLanguage,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -717,6 +737,85 @@ func (r *mutationResolver) DeleteCookie(ctx context.Context, input types.DeleteC
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpsertCookieBannerTranslation is the resolver for the upsertCookieBannerTranslation field.
|
||||
func (r *mutationResolver) UpsertCookieBannerTranslation(ctx context.Context, input types.UpsertCookieBannerTranslationInput) (*types.UpsertCookieBannerTranslationPayload, error) {
|
||||
if err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerUpdate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scope := coredata.NewScopeFromObjectID(input.CookieBannerID)
|
||||
|
||||
translation, err := r.cookieBanner.UpsertCookieBannerTranslation(
|
||||
ctx,
|
||||
scope,
|
||||
cookiebanner.UpsertCookieBannerTranslationRequest{
|
||||
CookieBannerID: input.CookieBannerID,
|
||||
Language: input.Language,
|
||||
Translations: json.RawMessage(input.Translations),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, cookiebanner.ErrBannerNotFound) {
|
||||
return nil, gqlutils.NotFound(ctx, err)
|
||||
}
|
||||
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
||||
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
||||
}
|
||||
r.logger.ErrorCtx(ctx, "cannot upsert cookie banner translation", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
banner, err := r.cookieBanner.GetCookieBanner(ctx, scope, input.CookieBannerID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load cookie banner", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.UpsertCookieBannerTranslationPayload{
|
||||
CookieBannerTranslation: types.NewCookieBannerTranslation(translation),
|
||||
CookieBanner: types.NewCookieBanner(banner),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteCookieBannerTranslation is the resolver for the deleteCookieBannerTranslation field.
|
||||
func (r *mutationResolver) DeleteCookieBannerTranslation(ctx context.Context, input types.DeleteCookieBannerTranslationInput) (*types.DeleteCookieBannerTranslationPayload, error) {
|
||||
if err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerUpdate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scope := coredata.NewScopeFromObjectID(input.CookieBannerID)
|
||||
|
||||
err := r.cookieBanner.DeleteCookieBannerTranslation(
|
||||
ctx,
|
||||
scope,
|
||||
cookiebanner.DeleteCookieBannerTranslationRequest{
|
||||
CookieBannerID: input.CookieBannerID,
|
||||
Language: input.Language,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, cookiebanner.ErrTranslationNotFound) {
|
||||
return nil, gqlutils.NotFound(ctx, err)
|
||||
}
|
||||
if errors.Is(err, cookiebanner.ErrBannerNotFound) {
|
||||
return nil, gqlutils.NotFound(ctx, err)
|
||||
}
|
||||
r.logger.ErrorCtx(ctx, "cannot delete cookie banner translation", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
banner, err := r.cookieBanner.GetCookieBanner(ctx, scope, input.CookieBannerID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load cookie banner", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.DeleteCookieBannerTranslationPayload{
|
||||
DeletedLanguage: input.Language,
|
||||
CookieBanner: types.NewCookieBanner(banner),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Cookie returns schema.CookieResolver implementation.
|
||||
func (r *Resolver) Cookie() schema.CookieResolver { return &cookieResolver{r} }
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ type CookieBanner implements Node {
|
||||
consentExpiryDays: Int!
|
||||
consentMode: CookieConsentMode!
|
||||
showBranding: Boolean!
|
||||
defaultLanguage: String!
|
||||
|
||||
organization: Organization @goField(forceResolver: true)
|
||||
|
||||
@@ -96,6 +97,8 @@ type CookieBanner implements Node {
|
||||
orderBy: CookieCategoryOrder
|
||||
): CookieCategoryConnection @goField(forceResolver: true)
|
||||
|
||||
translations: [CookieBannerTranslation!]! @goField(forceResolver: true)
|
||||
|
||||
latestVersion: CookieBannerVersion @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
@@ -104,6 +107,14 @@ type CookieBanner implements Node {
|
||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type CookieBannerTranslation implements Node {
|
||||
id: ID!
|
||||
language: String!
|
||||
translations: String!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type CookieCategory implements Node {
|
||||
id: ID!
|
||||
cookieBanner: CookieBanner @goField(forceResolver: true)
|
||||
@@ -243,6 +254,12 @@ extend type Mutation {
|
||||
createCookie(input: CreateCookieInput!): CreateCookiePayload!
|
||||
updateCookie(input: UpdateCookieInput!): UpdateCookiePayload!
|
||||
deleteCookie(input: DeleteCookieInput!): DeleteCookiePayload!
|
||||
upsertCookieBannerTranslation(
|
||||
input: UpsertCookieBannerTranslationInput!
|
||||
): UpsertCookieBannerTranslationPayload!
|
||||
deleteCookieBannerTranslation(
|
||||
input: DeleteCookieBannerTranslationInput!
|
||||
): DeleteCookieBannerTranslationPayload!
|
||||
}
|
||||
|
||||
input CreateCookieBannerInput {
|
||||
@@ -261,6 +278,7 @@ input UpdateCookieBannerInput {
|
||||
privacyPolicyUrl: String
|
||||
consentExpiryDays: Int
|
||||
consentMode: CookieConsentMode
|
||||
defaultLanguage: String
|
||||
}
|
||||
|
||||
input DeleteCookieBannerInput {
|
||||
@@ -387,3 +405,24 @@ type DeleteCookiePayload {
|
||||
deletedCookieId: ID!
|
||||
cookieBanner: CookieBanner!
|
||||
}
|
||||
|
||||
input UpsertCookieBannerTranslationInput {
|
||||
cookieBannerId: ID!
|
||||
language: String!
|
||||
translations: String!
|
||||
}
|
||||
|
||||
input DeleteCookieBannerTranslationInput {
|
||||
cookieBannerId: ID!
|
||||
language: String!
|
||||
}
|
||||
|
||||
type UpsertCookieBannerTranslationPayload {
|
||||
cookieBannerTranslation: CookieBannerTranslation!
|
||||
cookieBanner: CookieBanner!
|
||||
}
|
||||
|
||||
type DeleteCookieBannerTranslationPayload {
|
||||
deletedLanguage: String!
|
||||
cookieBanner: CookieBanner!
|
||||
}
|
||||
|
||||
@@ -73,7 +73,18 @@ func NewCookieBanner(b *coredata.CookieBanner) *CookieBanner {
|
||||
ConsentExpiryDays: b.ConsentExpiryDays,
|
||||
ConsentMode: b.ConsentMode,
|
||||
ShowBranding: b.ShowBranding,
|
||||
DefaultLanguage: b.DefaultLanguage,
|
||||
CreatedAt: b.CreatedAt,
|
||||
UpdatedAt: b.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCookieBannerTranslation(t *coredata.CookieBannerTranslation) *CookieBannerTranslation {
|
||||
return &CookieBannerTranslation{
|
||||
ID: t.ID,
|
||||
Language: t.Language,
|
||||
Translations: string(t.Translations),
|
||||
CreatedAt: t.CreatedAt,
|
||||
UpdatedAt: t.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user