shop/customer/persistor.go

376 lines
12 KiB
Go

package customer
import (
"errors"
"fmt"
"log"
"strconv"
"github.com/foomo/shop/configuration"
"github.com/foomo/shop/event_log"
"github.com/foomo/shop/persistence"
"github.com/foomo/shop/version"
"github.com/mitchellh/mapstructure"
"gopkg.in/mgo.v2/bson"
)
// !! NOTE: customer must not import order !!
//------------------------------------------------------------------
// ~ CONSTANTS / VARS
//------------------------------------------------------------------
const (
VERBOSE = false
)
var globalCustomerPersistor *persistence.Persistor
var globalCustomerHistoryPersistor *persistence.Persistor
var globalCredentialsPersistor *persistence.Persistor
//------------------------------------------------------------------
// ~ PUBLIC METHODS
//------------------------------------------------------------------
// Returns GLOBAL_PERSISTOR. If GLOBAL_PERSISTOR is nil, a new persistor is created, set as GLOBAL_PERSISTOR and returned
func GetCustomerPersistor() *persistence.Persistor {
url := configuration.MONGO_URL
collection := configuration.MONGO_COLLECTION_CUSTOMERS
if globalCustomerPersistor == nil {
p, err := persistence.NewPersistor(url, collection)
if err != nil || p == nil {
panic(errors.New("failed to create mongoDB global persistor: " + err.Error()))
}
globalCustomerPersistor = p
return globalCustomerPersistor
}
if url == globalCustomerPersistor.GetURL() && collection == globalCustomerPersistor.GetCollectionName() {
return globalCustomerPersistor
}
p, err := persistence.NewPersistor(url, collection)
if err != nil || p == nil {
panic(err)
}
globalCustomerPersistor = p
return globalCustomerPersistor
}
// Returns GLOBAL_PERSISTOR. If GLOBAL_PERSISTOR is nil, a new persistor is created, set as GLOBAL_PERSISTOR and returned
func GetCustomerHistoryPersistor() *persistence.Persistor {
url := configuration.MONGO_URL
collection := configuration.MONGO_COLLECTION_CUSTOMERS_HISTORY
if globalCustomerHistoryPersistor == nil {
p, err := persistence.NewPersistor(url, collection)
if err != nil || p == nil {
panic(errors.New("failed to create mongoDB order persistor: " + err.Error()))
}
globalCustomerHistoryPersistor = p
return globalCustomerHistoryPersistor
}
if url == globalCustomerHistoryPersistor.GetURL() && collection == globalCustomerHistoryPersistor.GetCollectionName() {
return globalCustomerHistoryPersistor
}
p, err := persistence.NewPersistor(url, collection)
if err != nil || p == nil {
panic(err)
}
globalCustomerHistoryPersistor = p
return globalCustomerHistoryPersistor
}
// Returns GLOBAL_PERSISTOR. If GLOBAL_PERSISTOR is nil, a new persistor is created, set as GLOBAL_PERSISTOR and returned
func GetCredentialsPersistor() *persistence.Persistor {
url := configuration.MONGO_URL
collection := configuration.MONGO_COLLECTION_CREDENTIALS
if globalCredentialsPersistor == nil {
p, err := persistence.NewPersistor(url, collection)
if err != nil || p == nil {
panic(errors.New("failed to create mongoDB order persistor: " + err.Error()))
}
globalCredentialsPersistor = p
return globalCredentialsPersistor
}
if url == globalCredentialsPersistor.GetURL() && collection == globalCredentialsPersistor.GetCollectionName() {
return globalCredentialsPersistor
}
p, err := persistence.NewPersistor(url, collection)
if err != nil || p == nil {
panic(err)
}
globalCredentialsPersistor = p
return globalCredentialsPersistor
}
// AlreadyExistsInDB checks if a customer with given customerID already exists in the database
func AlreadyExistsInDB(customerID string) (bool, error) {
p := GetCustomerPersistor()
q := p.GetCollection().Find(&bson.M{"id": customerID})
count, err := q.Count()
if err != nil {
return false, err
}
return count > 0, nil
}
// Find returns an iterator for all entries found matching on query.
func Find(query *bson.M, customProvider CustomerCustomProvider) (iter func() (cust *Customer, err error), err error) {
p := GetCustomerPersistor()
_, err = p.GetCollection().Find(query).Count()
if err != nil {
log.Println(err)
}
q := p.GetCollection().Find(query)
fields := customProvider.Fields()
if fields != nil {
q.Select(fields)
}
_, err = q.Count()
if err != nil {
return
}
mgoiter := q.Iter()
iter = func() (cust *Customer, err error) {
cust = &Customer{}
if mgoiter.Next(cust) {
return mapDecode(cust, customProvider)
}
return nil, nil
}
return
}
func UpsertCustomer(c *Customer) error {
//log.Println("WhoCalledMe: ", utils.WhoCalledMe())
//log.Println("UPSERT CUSTOMER with id", c.GetID())
// order is unlinked or not yet inserted in db
if c.unlinkDB || c.BsonId == "" {
return nil
}
p := GetCustomerPersistor()
// Get current version from db and check against verssion of c
// If they are not identical, there must have been another upsert which would be overwritten by this one.
// In this case upsert is skipped and an error is returned,
customerLatestFromDb := &Customer{}
err := p.GetCollection().Find(&bson.M{"id": c.GetID()}).Select(&bson.M{"version": 1}).One(customerLatestFromDb)
if err != nil {
log.Println("Error Upsert Customer", err)
return err
}
latestVersionInDb := customerLatestFromDb.Version.GetVersion()
if latestVersionInDb != c.Version.GetVersion() && !c.Flags.forceUpsert {
errMsg := fmt.Sprintln("WARNING: Cannot upsert latest version ", strconv.Itoa(latestVersionInDb), "in db with version", strconv.Itoa(c.Version.GetVersion()), "!")
log.Println(errMsg)
return errors.New(errMsg)
}
if c.Flags.forceUpsert {
// Remember this number, so that we later know from which version we came from
v := c.Version.Current
// Set the current version number to keep history consistent
c.Version.Current = latestVersionInDb
c.Version.Increment()
c.Flags.forceUpsert = false
// Overwrite NumberPrevious, to remember where we came from
c.Version.Previous = v
} else {
c.Version.Increment()
}
_, err = p.GetCollection().UpsertId(c.BsonId, c)
if err != nil {
return err
}
// Store version in history
bsonId := c.BsonId
c.BsonId = "" // Temporarily reset Mongo ObjectId, so that we can perfrom an Insert.
pHistory := GetCustomerHistoryPersistor()
pHistory.GetCollection().Insert(c)
c.BsonId = bsonId // restore bsonId
event_log.SaveShopEvent(event_log.ActionUpsertingCustomer, c.GetID(), err, "")
return err
}
func UpsertAndGetCustomer(c *Customer, customProvider CustomerCustomProvider) (*Customer, error) {
err := UpsertCustomer(c)
if err != nil {
return nil, err
}
return GetCustomerById(c.GetID(), customProvider)
}
func DeleteCustomer(c *Customer) error {
err := GetCustomerPersistor().GetCollection().Remove(bson.M{"_id": c.BsonId})
if err != nil {
return err
}
err = DeleteCredential(c.Email)
event_log.SaveShopEvent(event_log.ActionDeleteCustomer, c.GetID(), err, "")
return err
}
func DeleteCustomerById(id string) error {
customer, err := GetCustomerById(id, nil)
if err != nil {
return err
}
DeleteCustomer(customer)
event_log.SaveShopEvent(event_log.ActionDeleteCustomer, id, err, "")
return err
}
// GetCustomerById returns the customer with id
func GetCustomerById(id string, customProvider CustomerCustomProvider) (*Customer, error) {
return findOneCustomer(&bson.M{"id": id}, nil, "", customProvider, false)
}
// GetCustomerByEmail
func GetCustomerByEmail(email string, customProvider CustomerCustomProvider) (*Customer, error) {
return findOneCustomer(&bson.M{"email": email}, nil, "", customProvider, false)
}
func GetCurrentCustomerByIdFromHistory(customerId string, customProvider CustomerCustomProvider) (*Customer, error) {
return findOneCustomer(&bson.M{"id": customerId}, nil, "-version.current", customProvider, true)
}
func GetCurrentVersionOfCustomerFromHistory(customerId string) (*version.Version, error) {
customer, err := findOneCustomer(&bson.M{"id": customerId}, &bson.M{"version": 1}, "-version.current", nil, true)
if err != nil {
return nil, err
}
return customer.GetVersion(), nil
}
func GetCustomerByVersion(customerId string, version int, customProvider CustomerCustomProvider) (*Customer, error) {
return findOneCustomer(&bson.M{"id": customerId, "version.current": version}, nil, "", customProvider, true)
}
func Rollback(customerId string, version int) error {
currentCustomer, err := GetCustomerById(customerId, nil)
if err != nil {
return err
}
if version >= currentCustomer.GetVersion().Current || version < 0 {
return errors.New("Cannot perform rollback to " + strconv.Itoa(version) + " from version " + strconv.Itoa(currentCustomer.GetVersion().Current))
}
customerFromHistory, err := GetCustomerByVersion(customerId, version, nil)
if err != nil {
return err
}
// Set bsonId from current customer to customer from history to overwrite current customer on next upsert.
customerFromHistory.BsonId = currentCustomer.BsonId
customerFromHistory.Flags.forceUpsert = true
return customerFromHistory.Upsert()
}
func DropAllCustomers() error {
return GetCustomerPersistor().GetCollection().DropCollection()
}
func DropAllCredentials() error {
return GetCredentialsPersistor().GetCollection().DropCollection()
}
func DropAllCustomersAndCredentials() error {
err := GetCustomerPersistor().GetCollection().DropCollection()
if err != nil {
return err
}
return GetCredentialsPersistor().GetCollection().DropCollection()
}
//------------------------------------------------------------------
// ~ PRIVATE METHODS
//------------------------------------------------------------------
// findOneCustomer returns one Customer from the customer database or from the customer history database
func findOneCustomer(find *bson.M, selection *bson.M, sort string, customProvider CustomerCustomProvider, fromHistory bool) (*Customer, error) {
var p *persistence.Persistor
if fromHistory {
p = GetCustomerHistoryPersistor()
} else {
p = GetCustomerPersistor()
}
customer := &Customer{}
if find == nil {
find = &bson.M{}
}
if selection == nil {
selection = &bson.M{}
}
if sort != "" {
err := p.GetCollection().Find(find).Select(selection).Sort(sort).One(customer)
if err != nil {
return nil, err
}
} else {
err := p.GetCollection().Find(find).Select(selection).One(customer)
if err != nil {
return nil, err
}
}
if customProvider != nil {
var err error
customer, err = mapDecode(customer, customProvider)
if err != nil {
return nil, err
}
}
event_log.SaveShopEvent(event_log.ActionRetrieveCustomer, customer.GetID(), nil, customer.GetEmail())
return customer, nil
}
// insertCustomer inserts a customer into the database
func insertCustomer(c *Customer) error {
p := GetCustomerPersistor()
alreadyExists, err := AlreadyExistsInDB(c.GetID())
if err != nil {
return err
}
if alreadyExists {
log.Println("User with id", c.GetID(), "already exists in the database!")
return nil
}
err = p.GetCollection().Insert(c)
if err != nil {
return err
}
pHistory := GetCustomerHistoryPersistor()
err = pHistory.GetCollection().Insert(c)
event_log.SaveShopEvent(event_log.ActionCreateCustomer, c.GetID(), err, "")
return err
}
// mapDecode maps interfaces to specific types provided by customProvider
func mapDecode(cust *Customer, customProvider CustomerCustomProvider) (customer *Customer, err error) {
/* Map CustomerCustom */
customerCustom := customProvider.NewCustomerCustom()
if customerCustom != nil && cust.Custom != nil {
err = mapstructure.Decode(cust.Custom, customerCustom)
if err != nil {
return nil, err
}
cust.Custom = customerCustom
}
/* Map AddressCustom */
for _, address := range cust.Addresses {
addressCustom := customProvider.NewAddressCustom()
if addressCustom != nil && address.Custom != nil {
err = mapstructure.Decode(address.Custom, addressCustom)
if err != nil {
return nil, err
}
address.Custom = addressCustom
}
}
return cust, nil
}