// NewService creates a new store Service which handles websites, groups and stores. // A Service can only act on a certain scope (MAGE_RUN_TYPE) and scope ID (MAGE_RUN_CODE). // Default scope.Scope is always the scope.WebsiteID constant. // This function is mainly used when booting the app to set the environment configuration // Also all other calls to any method receiver with nil arguments depends on the internal // appStore which reflects the default store ID. func NewService(so scope.Option, storage Storager, opts ...ServiceOption) (*Service, error) { scopeID := so.Scope() if scopeID == scope.DefaultID { scopeID = scope.WebsiteID } s := &Service{ cr: config.DefaultService, boundToScope: scopeID, storage: storage, mu: sync.RWMutex{}, websiteMap: make(map[uint64]*Website), groupMap: make(map[uint64]*Group), storeMap: make(map[uint64]*Store), } for _, opt := range opts { if opt != nil { opt(s) } } var err error s.appStore, err = s.findDefaultStoreByScope(s.boundToScope, so) if err != nil { if PkgLog.IsDebug() { PkgLog.Debug("store.Service.Init", "err", err, "ScopeOption", so) } return nil, errgo.Mask(err) } return s, nil }
func TestApplyStore(t *testing.T) { so := scope.Option{Store: scope.MockID(3)} assert.NotNil(t, so) assert.Equal(t, int64(3), so.Store.StoreID()) assert.Nil(t, so.Website) assert.Nil(t, so.Group) assert.Exactly(t, scope.StoreID.String(), so.String()) }
// RequestedStore see interface description Reader.RequestedStore func (sm *Service) RequestedStore(so scope.Option) (activeStore *Store, err error) { activeStore, err = sm.findDefaultStoreByScope(so.Scope(), so) if err != nil { if PkgLog.IsDebug() { PkgLog.Debug("store.Service.RequestedStore.FindDefaultStoreByScope", "err", err, "so", so) } return nil, err } // activeStore, err = sm.newActiveStore(activeStore) // this is the active store from a request. // todo rethink here if we really need a newActiveStore // newActiveStore creates a new Store, Website and Group pointers !!! // if activeStore == nil || err != nil { // // store is not active so ignore // return nil, err // } if false == activeStore.Data.IsActive { return nil, ErrStoreNotActive } allowStoreChange := false switch sm.boundToScope { case scope.StoreID: allowStoreChange = true break case scope.GroupID: allowStoreChange = activeStore.Data.GroupID == sm.appStore.Data.GroupID break case scope.WebsiteID: allowStoreChange = activeStore.Data.WebsiteID == sm.appStore.Data.WebsiteID break } if allowStoreChange { return activeStore, nil } return nil, ErrStoreChangeNotAllowed }
func testStoreCodeFrom(t *testing.T, i int, haveErr, wantErr error, haveScope scope.Option, wantScope scope.Scope, wantCode string, wantID int64) { if wantErr != nil { assert.EqualError(t, haveErr, wantErr.Error(), "Index: %d", i) } switch sos := haveScope.Scope(); sos { case scope.StoreID: assert.Exactly(t, wantID, haveScope.Store.StoreID(), "Index: %d", i) case scope.GroupID: assert.Exactly(t, wantID, haveScope.Group.GroupID(), "Index: %d", i) case scope.WebsiteID: assert.Exactly(t, wantID, haveScope.Website.WebsiteID(), "Index: %d", i) case scope.DefaultID: assert.Nil(t, haveScope.Store, "Index: %d", i) assert.Nil(t, haveScope.Group, "Index: %d", i) assert.Nil(t, haveScope.Website, "Index: %d", i) default: t.Fatalf("Unknown scope: %d", sos) } assert.Exactly(t, wantScope, haveScope.Scope(), "Index: %d", i) assert.Exactly(t, wantCode, haveScope.StoreCode(), "Index: %d", i) }
// WithInitStoreByFormCookie reads from a GET parameter or cookie the store code. // Checks if the store code is valid and allowed. If so it adjusts the context.Context // to provide the new requestedStore. // // It calls Reader.RequestedStore() to determine the correct store. // 1. check cookie store, always a string and the store code // 2. check for GET ___store variable, always a string and the store code func WithInitStoreByFormCookie() ctxhttp.Middleware { return func(hf ctxhttp.HandlerFunc) ctxhttp.HandlerFunc { return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { storeService, requestedStore, err := FromContextReader(ctx) if err != nil { if PkgLog.IsDebug() { PkgLog.Debug("store.WithInitStoreByToken.FromContextServiceReader", "err", err, "ctx", ctx) } return errgo.Mask(err) } var reqSO scope.Option reqSO, err = CodeFromRequestGET(r) if err != nil { if PkgLog.IsDebug() { PkgLog.Debug("store.WithInitStoreByFormCookie.StoreCodeFromRequestGET", "err", err, "req", r, "scope", reqSO) } reqSO, err = CodeFromCookie(r) if err != nil { // ignore further processing because all codes are invalid or not found if PkgLog.IsDebug() { PkgLog.Debug("store.WithInitStoreByFormCookie.StoreCodeFromCookie", "err", err, "req", r, "scope", reqSO) } return hf.ServeHTTPContext(ctx, w, r) } } var newRequestedStore *Store if newRequestedStore, err = storeService.RequestedStore(reqSO); err != nil { if PkgLog.IsDebug() { PkgLog.Debug("store.WithInitStoreByFormCookie.storeService.RequestedStore", "err", err, "req", r, "scope", reqSO) } return errgo.Mask(err) } soStoreCode := reqSO.StoreCode() // delete and re-set a new cookie, adjust context.Context if newRequestedStore != nil && newRequestedStore.Data.Code.String == soStoreCode { wds, err := newRequestedStore.Website.DefaultStore() if err != nil { if PkgLog.IsDebug() { PkgLog.Debug("store.WithInitStoreByFormCookie.Website.DefaultStore", "err", err, "soStoreCode", soStoreCode) } return errgo.Mask(err) } if wds.Data.Code.String == soStoreCode { newRequestedStore.DeleteCookie(w) // cookie not needed anymore } else { newRequestedStore.SetCookie(w) // make sure we force set the new store if newRequestedStore.StoreID() != requestedStore.StoreID() { // this may lead to a bug because the previously set storeService and requestedStore // will still exists and have not been removed. ctx = WithContextReader(ctx, storeService, newRequestedStore) } } } return hf(ctx, w, r) } } }
func TestApplyDefault(t *testing.T) { so := scope.Option{} assert.NotNil(t, so) assert.Exactly(t, scope.DefaultID, so.Scope()) }