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  }