flamingo.me/flamingo-commerce/v3@v3.11.0/w3cdatalayer/application/service.go (about) 1 package application 2 3 import ( 4 "context" 5 "fmt" 6 7 "flamingo.me/flamingo-commerce/v3/cart/domain/decorator" 8 9 productDomain "flamingo.me/flamingo-commerce/v3/product/domain" 10 "flamingo.me/flamingo-commerce/v3/w3cdatalayer/domain" 11 "flamingo.me/flamingo/v3/framework/flamingo" 12 "flamingo.me/flamingo/v3/framework/web" 13 "flamingo.me/pugtemplate/pugjs" 14 "github.com/pkg/errors" 15 ) 16 17 type ( 18 // ServiceProvider func 19 ServiceProvider func() *Service 20 21 // Service can be used from outside is expected to be initialized with the current request context 22 // It stores a dataLayer Value object for the current request context and allows interaction with it 23 Service struct { 24 // currentContext need to be set when using the service 25 currentContext context.Context 26 logger flamingo.Logger 27 factory *Factory 28 productDomain.ProductService 29 } 30 ) 31 32 // constants 33 const ( 34 SessionEventsKey = "w3cdatalayer_events" 35 DatalayerReqKey = "w3cDatalayer" 36 ) 37 38 // Inject method 39 func (s *Service) Inject(logger flamingo.Logger, factory *Factory, service productDomain.ProductService) { 40 s.logger = logger 41 s.factory = factory 42 s.ProductService = service 43 } 44 45 // Init method - sets the context 46 func (s *Service) Init(ctx context.Context) { 47 s.currentContext = ctx 48 } 49 50 // Get gets the data layer value object stored in the current context - or a freshly new build one if its the first call 51 func (s *Service) Get() domain.Datalayer { 52 if s.currentContext == nil { 53 s.logger.WithField("category", "w3cDatalayer").Error("Get called without context!") 54 55 return domain.Datalayer{} 56 } 57 req := web.RequestFromContext(s.currentContext) 58 if _, ok := req.Values.Load(DatalayerReqKey); !ok { 59 _ = s.store(s.factory.BuildForCurrentRequest(s.currentContext, req)) 60 } 61 62 _ = s.AddSessionEvents() 63 64 layer, _ := req.Values.Load(DatalayerReqKey) 65 if savedDataLayer, ok := layer.(domain.Datalayer); ok { 66 return savedDataLayer 67 } 68 69 // error 70 s.logger.WithField("category", "w3cDatalayer").Warn("Receiving datalayer from context failed") 71 return domain.Datalayer{} 72 } 73 74 // SetBreadCrumb to data layer 75 func (s *Service) SetBreadCrumb(breadcrumb string) error { 76 if s.currentContext == nil { 77 return errors.New("Service can only be used with currentContext - call Init() first") 78 } 79 layer := s.Get() 80 if layer.Page != nil { 81 layer.Page.PageInfo.BreadCrumbs = breadcrumb 82 } 83 84 return s.store(layer) 85 } 86 87 // AddSessionEvents to data layer 88 func (s *Service) AddSessionEvents() error { 89 if s.currentContext == nil { 90 return errors.New("Service can only be used with currentContext - call Init() first") 91 } 92 session := web.SessionFromContext(s.currentContext) 93 sessionEvents := session.Flashes(SessionEventsKey) 94 95 // early return if there are no events 96 if len(sessionEvents) == 0 { 97 return nil 98 } 99 100 layer := s.Get() 101 102 for _, event := range sessionEvents { 103 if event, ok := event.(domain.Event); ok { 104 s.logger.WithField("category", "w3cDatalayer").Debug("SESSION_EVENTS_KEY Event", event.EventInfo) 105 layer.Event = append(layer.Event, event) 106 } 107 } 108 109 err := s.store(layer) 110 if err != nil { 111 return err 112 } 113 114 return nil 115 } 116 117 // SetPageCategories to data layer 118 func (s *Service) SetPageCategories(category string, subcategory1 string, subcategory2 string) error { 119 if s.currentContext == nil { 120 return errors.New("Service can only be used with currentContext - call Init() first") 121 } 122 layer := s.Get() 123 if layer.Page == nil { 124 layer.Page = &domain.Page{} 125 } 126 layer.Page.Category.PrimaryCategory = category 127 layer.Page.Category.Section = category 128 129 layer.Page.Category.SubCategory1 = subcategory1 130 layer.Page.Category.SubCategory2 = subcategory2 131 132 return s.store(layer) 133 } 134 135 // SetPageInfos to data layer 136 func (s *Service) SetPageInfos(pageID string, pageName string) error { 137 if s.currentContext == nil { 138 return errors.New("Service can only be used with currentContext - call Init() first") 139 } 140 layer := s.Get() 141 if layer.Page == nil { 142 layer.Page = &domain.Page{} 143 } 144 if pageID != "" { 145 layer.Page.PageInfo.PageID = pageID 146 } 147 if pageName != "" { 148 layer.Page.PageInfo.PageName = pageName 149 } 150 return s.store(layer) 151 } 152 153 // SetUserEmail to a User object 154 func (s *Service) SetUserEmail(mail string) error { 155 if s.currentContext == nil { 156 return errors.New("Service can only be used with currentContext - call Init() first") 157 } 158 s.logger.WithField("category", "w3cDatalayer").Debug(fmt.Sprintf("Set Usermail %v", mail)) 159 layer := s.Get() 160 layer.User = append(layer.User, domain.User{ 161 Profile: []domain.UserProfile{{ 162 ProfileInfo: domain.UserProfileInfo{ 163 EmailID: s.factory.HashValueIfConfigured(mail), 164 }, 165 }}, 166 }) 167 return s.store(layer) 168 } 169 170 // SetPageInfoLanguage to the layer 171 func (s *Service) SetPageInfoLanguage(language string) error { 172 if s.currentContext == nil { 173 return errors.New("Service can only be used with currentContext - call Init() first") 174 } 175 s.logger.WithField("language", "w3cDatalayer").Debug(fmt.Sprintf("Set page language %v", language)) 176 layer := s.Get() 177 178 if layer.Page == nil { 179 return nil 180 } 181 layer.Page.PageInfo.Language = language 182 return s.store(layer) 183 } 184 185 // SetSearchData to data layer 186 func (s *Service) SetSearchData(keyword string, results interface{}) error { 187 if s.currentContext == nil { 188 return errors.New("Service can only be used with currentContext - call Init() first") 189 } 190 s.logger.WithField("category", "w3cDatalayer").Debug(fmt.Sprintf("SetSearchData Keyword %v Result: %#v", keyword, results)) 191 layer := s.Get() 192 if layer.Page != nil { 193 layer.Page.Search = domain.SearchInfo{ 194 SearchKeyword: keyword, 195 Result: results, 196 } 197 } 198 return s.store(layer) 199 } 200 201 // SetCartData to data layer 202 func (s *Service) SetCartData(cart decorator.DecoratedCart) error { 203 if s.currentContext == nil { 204 return errors.New("Service can only be used with currentContext - call Init() first") 205 } 206 s.logger.WithField("category", "w3cDatalayer").Debug(fmt.Sprintf("Set Cart Data for cart %v", cart.Cart.ID)) 207 layer := s.Get() 208 layer.Cart = s.factory.BuildCartData(cart) 209 return s.store(layer) 210 } 211 212 // SetTransaction information to data layer 213 func (s *Service) SetTransaction(cart decorator.DecoratedCart, decoratedItems []decorator.DecoratedCartItem, orderID string, email string) error { 214 if s.currentContext == nil { 215 return errors.New("Service can only be used with currentContext - call Init() first") 216 } 217 s.logger.WithField("category", "w3cDatalayer").Debug(fmt.Sprintf("Set Transaction Data for order %v mail %v", orderID, email)) 218 layer := s.Get() 219 layer.Transaction = s.factory.BuildTransactionData(s.currentContext, cart, decoratedItems, orderID, email) 220 return s.store(layer) 221 } 222 223 // AddTransactionAttribute to data layer 224 func (s *Service) AddTransactionAttribute(key string, value string) error { 225 if s.currentContext == nil { 226 return errors.New("Service can only be used with currentContext - call Init() first") 227 } 228 layer := s.Get() 229 if layer.Transaction != nil && layer.Transaction.Attributes != nil { 230 layer.Transaction.Attributes[key] = value 231 } 232 return s.store(layer) 233 } 234 235 // AddProduct - appends the productData to the data layer 236 func (s *Service) AddProduct(product productDomain.BasicProduct) error { 237 if s.currentContext == nil { 238 return errors.New("Service can only be used with currentContext - call Init() first") 239 } 240 layer := s.Get() 241 layer.Product = append(layer.Product, s.factory.BuildProductData(product)) 242 return s.store(layer) 243 } 244 245 // AddEvent - adds an event with the given eventName to the data layer 246 func (s *Service) AddEvent(eventName string, params ...*pugjs.Map) error { 247 if s.currentContext == nil { 248 return errors.New("Service can only be used with currentContext - call Init() first") 249 } 250 layer := s.Get() 251 252 event := domain.Event{EventInfo: make(map[string]interface{})} 253 event.EventInfo["eventName"] = eventName 254 255 if len(params) == 1 { 256 for k, v := range params[0].AsStringMap() { 257 event.EventInfo[k] = v 258 } 259 } 260 261 layer.Event = append(layer.Event, event) 262 return s.store(layer) 263 } 264 265 // store data layer in current context 266 func (s *Service) store(layer domain.Datalayer) error { 267 s.logger.Debug(fmt.Sprintf("Update %#v", layer)) 268 if s.currentContext == nil { 269 s.logger.WithField("category", "w3cDatalayer").Error("Update called without context!") 270 return errors.New("Update called without context") 271 } 272 req := web.RequestFromContext(s.currentContext) 273 req.Values.Store(DatalayerReqKey, layer) 274 275 return nil 276 }