customer soft lock improved

customer version history dropped
This commit is contained in:
Frederik Löffert 2020-06-15 14:42:01 +02:00
parent d4c84c5721
commit ebdd0e529f
11 changed files with 63 additions and 608 deletions

View File

@ -19,12 +19,10 @@ var (
MONGO_URL = "mongodb://" + WithDocker
MONGO_URL_PRODUCTS = "mongodb://" + ProductsLocal
MONGO_COLLECTION_CREDENTIALS = "credentials"
MONGO_COLLECTION_ORDERS = "orders"
MONGO_COLLECTION_ORDERS_HISTORY = "orders_history"
MONGO_COLLECTION_CUSTOMERS_HISTORY = "crm_customer_cache_history"
MONGO_COLLECTION_CUSTOMERS = "crm_customer_cache"
MONGO_COLLECTION_WATCHLISTS = "watchlists"
MONGO_COLLECTION_ORDERS = "orders"
MONGO_COLLECTION_ORDERS_HISTORY = "orders_history"
MONGO_COLLECTION_CUSTOMERS = "customerscrm"
MONGO_COLLECTION_WATCHLISTS = "watchlists"
MONGO_COLLECTION_PRICERULES = "pricerules"
MONGO_COLLECTION_PRICERULES_VOUCHERS = "pricerules_vouchers"

View File

@ -5,9 +5,10 @@ package crypto
import (
"errors"
"strconv"
zxcbvn "github.com/nbutton23/zxcvbn-go"
"github.com/nbutton23/zxcvbn-go/scoring"
"strconv"
)
var minLength int = -1
@ -22,7 +23,7 @@ func SetMaxLength(max int) {
// DeterminePasswordStrength returns a detailed info about the strength of the given password
// @userInput e.g. user name. Given strings are matched against password to prohibit similarities between username and password
func DeterminePasswordStrength(password string, userInput []string) scoring.MinEntropyMatch {
func determinePasswordStrength(password string, userInput []string) scoring.MinEntropyMatch {
return zxcbvn.PasswordStrength(password, userInput)
}
@ -34,8 +35,6 @@ func GetPasswordScore(password string, userInput []string) (int, error) {
if maxLength != -1 && len(password) > maxLength {
return 0, errors.New("Password must be not longer than " + strconv.Itoa(maxLength) + " characters!")
}
match := DeterminePasswordStrength(password, userInput)
match := determinePasswordStrength(password, userInput)
return match.Score, nil
}

View File

@ -11,13 +11,13 @@ func TestCryptoPasswordStrength(t *testing.T) {
passwords := []string{"mypassword", "summertablecactus+", "osome+#,,brassford"}
for _, password := range passwords {
fmt.Println("---------- Password:", password, "-------------")
utils.PrintJSON(DeterminePasswordStrength(password, nil))
utils.PrintJSON(determinePasswordStrength(password, nil))
}
fmt.Println("---------- Password with userInput-------------")
// Test with user input, expected score 1
DeterminePasswordStrength(passwords[1], []string{"Table"})
utils.PrintJSON(DeterminePasswordStrength(passwords[1], []string{"Table"}))
determinePasswordStrength(passwords[1], []string{"Table"})
utils.PrintJSON(determinePasswordStrength(passwords[1], []string{"Table"}))
//for i := 0; i < 1000; i++ {
for _, password := range passwords {

View File

@ -1,171 +0,0 @@
package customer
import (
"errors"
"log"
"strings"
"github.com/foomo/shop/crypto"
"github.com/foomo/shop/shop_error"
"github.com/foomo/shop/version"
"gopkg.in/mgo.v2/bson"
)
//------------------------------------------------------------------
// ~ PUBLIC TYPES
//------------------------------------------------------------------
type CustomerCredentials struct {
BsonId bson.ObjectId `bson:"_id,omitempty"`
Version *version.Version
Email string // always stored lowercase
Crypto *crypto.Crypto
}
//------------------------------------------------------------------
// ~ PUBLIC METHODS
//------------------------------------------------------------------
// GetCredentials from db
func GetCredentials(email string) (*CustomerCredentials, error) {
session, collection := GetCredentialsPersistor().GetCollection()
defer session.Close()
credentials := &CustomerCredentials{}
err := collection.Find(&bson.M{"email": lc(email)}).One(credentials)
if err != nil {
return nil, err
}
return credentials, nil
}
// CreateCustomerCredentials
func CreateCustomerCredentials(email, password string) error {
if password == "" {
log.Println("WARNING: Empty password is reserved for guest customer (and will not grant access).")
}
available, err := CheckLoginAvailable(lc(email))
if err != nil {
return err
}
if !available {
return errors.New(lc(email) + " is already taken!")
}
crypto, err := crypto.HashPassword(password)
if err != nil {
return err
}
credentials := &CustomerCredentials{
Version: version.NewVersion(),
Email: lc(email),
Crypto: crypto,
}
session, collection := GetCredentialsPersistor().GetCollection()
defer session.Close()
return collection.Insert(credentials)
}
// CheckLoginAvailable returns true if the email address is available as login credential (ignores guest accounts)
func CheckLoginAvailable(email string) (bool, error) {
session, collection := GetCustomerPersistor().GetCollection()
defer session.Close()
query := collection.Find(&bson.M{
"$and": []*bson.M{
&bson.M{"email": lc(email)},
&bson.M{"isguest": false},
}})
count, err := query.Count()
if err != nil {
return false, err
}
return count == 0, nil
}
// CheckLoginCredentials returns true if customer with email exists and password matches with the hash stores in customers Crypto.
// Email is not case-sensitive to avoid user frustration
func CheckLoginCredentials(email, password string) (bool, error) {
if password == "" {
log.Println("INFO: No access granted, because password is empty. (Empty password is used for guest users)")
return false, nil
}
credentials, err := GetCredentials(lc(email))
if err != nil {
return false, err
}
return crypto.VerifyPassword(credentials.Crypto, password), nil
}
// ChangePassword changes the password of the user.
// If force, passworldOld is irrelevant and the password is changed in any case.
func ChangePassword(email, password, passwordNew string, force bool) error {
credentials, err := GetCredentials(lc(email))
if err != nil {
return err
}
auth := force || crypto.VerifyPassword(credentials.Crypto, password)
if auth {
newCrypto, err := crypto.HashPassword(passwordNew)
if err != nil {
return err
}
credentials.Crypto = newCrypto
credentials.Version.Increment()
session, collection := GetCredentialsPersistor().GetCollection()
defer session.Close()
_, err = collection.UpsertId(credentials.BsonId, credentials)
return err
}
return errors.New("Authorization Error: Could not change password.")
}
func ChangeEmail(email, newEmail string) error {
available, err := CheckLoginAvailable(lc(newEmail))
if err != nil {
return err
}
if !available {
return errors.New("Could not change Email: \"" + lc(newEmail) + "\" is already taken!")
}
credentials, err := GetCredentials(lc(email))
if err != nil {
return err
}
credentials.Email = lc(newEmail)
credentials.Version.Increment()
session, collection := GetCredentialsPersistor().GetCollection()
defer session.Close()
_, err = collection.UpsertId(credentials.BsonId, credentials)
return err
}
func DeleteCredential(email string) error {
session, collection := GetCredentialsPersistor().GetCollection()
defer session.Close()
// remove credentials
err := collection.Remove(&bson.M{"email": lc(email)})
if err != nil && err.Error() != shop_error.ErrorNotInDatabase {
return err
}
return nil
}
//------------------------------------------------------------------
// ~ PRIVATE METHODS
//------------------------------------------------------------------
// lc returns lowercase version of string
func lc(s string) string {
return strings.ToLower(s)
}

View File

@ -1,87 +0,0 @@
package customer
import (
"testing"
"github.com/foomo/shop/test_utils"
)
func TestCredentials(t *testing.T) {
test_utils.DropAllCollections()
// Create credentials for a user
email := "foo@bar.com"
password := "123456"
err := CreateCustomerCredentials(email, password)
if err != nil {
t.Fatal(err)
}
// Create credentials for another user
email = "alice@bar.com"
password = "wonderland"
err = CreateCustomerCredentials(email, password)
if err != nil {
t.Fatal(err)
}
// ! ----------------------------------------------------------------------
// ~ due to non unique mails creating multiple creds with same mail is allowed now
// ! ----------------------------------------------------------------------
// // Try to create credentials for already taken email.
// // This should fail
// email = "alice@bar.com"
// password = "wonderland"
// err = CreateCustomerCredentials(email, password)
// if err == nil {
// t.Fatal(err)
// }
// Change email
err = ChangeEmail("foo@bar.com", "trent@bar.com")
if err != nil {
t.Fatal(err)
}
// Try to change email that does not exist.
err = ChangeEmail("idont@exist.com", "a@b.com")
if err == nil {
t.Fatal(err)
}
// Try to change email with incorrect password
err = ChangePassword("alice@bar.com", "wrong", "myNewPassWord", false)
if err == nil {
t.Fatal(err)
}
// Try to change email with correct password
err = ChangePassword("alice@bar.com", "wonderland", "myNewPassWord", false)
if err != nil {
t.Fatal(err)
}
// Try new Password
auth, err := CheckLoginCredentials("alice@bar.com", "myNewPassWord")
if !auth || err != nil {
t.Fatal(err)
}
// Delete Credentials of trent@bar.com
err = DeleteCredential("trent@bar.com")
}
func TestCredentialsForGuestCustomer(t *testing.T) {
test_utils.DropAllCollections()
// Create credentials for a user
email := "foo@bar.com"
password := ""
err := CreateCustomerCredentials(email, password)
if err != nil {
t.Fatal(err)
}
auth, err := CheckLoginCredentials(email, password)
if auth || err != nil {
t.Fatal(err)
}
}

View File

@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"log"
"strconv"
"strings"
"time"
"github.com/foomo/shop/address"
@ -53,7 +53,6 @@ type Customer struct {
ExternalID string
Id string
unlinkDB bool // if true, changes to Customer are not stored in database
Flags *Flags
Version *version.Version
CreatedAt time.Time
LastModifiedAt time.Time
@ -68,10 +67,6 @@ type Customer struct {
Custom interface{}
}
type Flags struct {
forceUpsert bool // if true, Upsert is performed even if there is a version conflict. This is important for rollbacks.
}
type Company struct {
Name string
Type string
@ -126,7 +121,6 @@ func NewCustomer(addrkey string, addrkeyHash string, externalID string, mailCont
email := lc(mailContact.Value)
customer := &Customer{
Flags: &Flags{},
Version: version.NewVersion(),
Id: unique.GetNewID(),
ExternalID: externalID,
@ -148,6 +142,9 @@ func NewCustomer(addrkey string, addrkeyHash string, externalID string, mailCont
Custom: customProvider.NewCustomerCustom(),
}
// initial version should be 1
customer.Version.Increment()
// persist customer in database
errInsert := customer.insert()
if errInsert != nil {
@ -163,40 +160,6 @@ func NewCustomer(addrkey string, addrkeyHash string, externalID string, mailCont
// ~ PUBLIC METHODS ON CUSTOMER
//------------------------------------------------------------------
func (customer *Customer) ChangeEmail(email, newEmail string) error {
// lower case
email = lc(email)
newEmail = lc(newEmail)
customer.Email = newEmail
for _, addr := range customer.GetAddresses() {
for _, contact := range addr.Person.Contacts {
if contact.IsMail() && contact.Value == email {
contact.Value = newEmail
}
}
}
return customer.Upsert()
}
func (customer *Customer) ChangePassword(password, passwordNew string, force bool) error {
err := ChangePassword(customer.Email, password, passwordNew, force)
if err != nil {
return err
}
return customer.Upsert()
}
// Unlinks customer from database
// After unlink, persistent changes on customer are no longer possible until it is retrieved again from db.
func (customer *Customer) UnlinkFromDB() {
customer.unlinkDB = true
}
func (customer *Customer) LinkDB() {
customer.unlinkDB = false
}
func (customer *Customer) insert() error {
return insertCustomer(customer)
}
@ -213,10 +176,6 @@ func (customer *Customer) Delete() error {
return DeleteCustomer(customer)
}
func (customer *Customer) Rollback(version int) error {
return Rollback(customer.GetID(), version)
}
func (customer *Customer) OverrideId(id string) error {
customer.Id = id
return customer.Upsert()
@ -350,43 +309,10 @@ func (customer *Customer) ChangeAddress(addr *address.Address) error {
}
//------------------------------------------------------------------
// ~ PUBLIC METHODS
// ~ PRIVATE METHODS
//------------------------------------------------------------------
// DiffTwoLatestCustomerVersions compares the two latest Versions of Customer found in version.
// If openInBrowser, the result is automatically displayed in the default browser.
func DiffTwoLatestCustomerVersions(customerId string, customProvider CustomerCustomProvider, openInBrowser bool) (string, error) {
version, err := GetCurrentVersionOfCustomerFromVersionsHistory(customerId)
if err != nil {
return "", err
}
return DiffCustomerVersions(customerId, version.Current-1, version.Current, customProvider, openInBrowser)
}
func DiffCustomerVersions(customerId string, versionA int, versionB int, customProvider CustomerCustomProvider, openInBrowser bool) (string, error) {
if versionA <= 0 || versionB <= 0 {
return "", errors.New("Error: Version must be greater than 0")
}
name := "customer_v" + strconv.Itoa(versionA) + "_vs_v" + strconv.Itoa(versionB)
customerVersionA, err := GetCustomerByVersion(customerId, versionA, customProvider)
if err != nil {
return "", err
}
customerVersionB, err := GetCustomerByVersion(customerId, versionB, customProvider)
if err != nil {
return "", err
}
html, err := version.DiffVersions(customerVersionA, customerVersionB)
if err != nil {
return "", err
}
if openInBrowser {
err := utils.OpenInBrowser(name, html)
if err != nil {
log.Println(err)
}
}
return html, err
// lc returns lowercase version of string
func lc(s string) string {
return strings.ToLower(s)
}

View File

@ -2,7 +2,6 @@ package customer
import (
"crypto/md5"
"fmt"
"io"
"log"
"testing"
@ -50,74 +49,6 @@ func createNewTestCustomer(email string) (*Customer, error) {
return NewCustomer(addrKey, addrKeyHash, externalID, mailContact, FooCustomProvider{})
}
func TestCustomerGetLatestCustomerFromDb(t *testing.T) {
test_utils.DropAllCollections()
customer, err := createNewTestCustomer(MOCK_EMAIL)
if err != nil {
t.Fatal(err)
}
// Perform 3 Upserts
customer.Person.FirstName = "Foo"
err = customer.Upsert()
customer.Person.MiddleName = "Bob"
err = customer.Upsert()
customer.Person.LastName = "Bar"
err = customer.Upsert()
if err != nil {
t.Fatal(err)
}
// Check if version number is 3
customer, err = GetCurrentCustomerByIdFromVersionsHistory(customer.GetID(), nil)
if customer.GetVersion().Current != 3 {
log.Println("Version is ", customer.GetVersion().Current, "- should have been 3.")
t.Fail()
}
}
func TestCustomerDiff2LatestCustomerVersions(t *testing.T) {
customer1, _ := create2CustomersAndPerformSomeUpserts(t)
_, err := DiffTwoLatestCustomerVersions(customer1.GetID(), nil, OPEN_DIFFS_IN_BROWSER)
if err != nil {
t.Fatal(err)
}
}
func TestCustomerRollbackAndDiff(t *testing.T) {
customer1, _ := create2CustomersAndPerformSomeUpserts(t)
errRoll := customer1.Rollback(customer1.GetVersion().Current - 1)
if errRoll != nil {
t.Fatal(errRoll)
}
customer1, errRoll = GetCustomerById(customer1.GetID(), nil)
_, err := DiffTwoLatestCustomerVersions(customer1.GetID(), nil, OPEN_DIFFS_IN_BROWSER)
if err != nil {
t.Fatal(err)
}
}
func TestCustomerRollback(t *testing.T) {
customer1, _ := create2CustomersAndPerformSomeUpserts(t)
utils.PrintJSON(customer1)
log.Println("Version", customer1.GetVersion(), "FirstName", customer1.Person.FirstName)
err := customer1.Rollback(customer1.GetVersion().Current - 2) // We need tp go 2 versions back to see the name change
if err != nil {
fmt.Println("Error: Could not roll back to previous version!")
t.Fatal(err)
}
customer1, err = GetCustomerById(customer1.GetID(), nil)
log.Println("Version", customer1.GetVersion(), "FirstName", customer1.Person.FirstName)
// Due to Rollback, FirstName should be "Foo" again
if customer1.Person.FirstName != "Foo" {
fmt.Println("Error: Expected Name to be Foo but got " + customer1.Person.FirstName)
t.Fail()
}
}
func create2CustomersAndPerformSomeUpserts(t *testing.T) (*Customer, *Customer) {
test_utils.DropAllCollections()
customer, err := createNewTestCustomer(MOCK_EMAIL)

View File

@ -1,16 +1,13 @@
package customer
import (
"errors"
"fmt"
"log"
"strconv"
stderr "errors"
"github.com/foomo/shop/configuration"
"github.com/foomo/shop/persistence"
"github.com/foomo/shop/shop_error"
"github.com/foomo/shop/version"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
@ -21,9 +18,8 @@ import (
//------------------------------------------------------------------
var (
globalCustomerPersistor *persistence.Persistor
globalCustomerVersionsPersistor *persistence.Persistor
globalCredentialsPersistor *persistence.Persistor
globalCustomerPersistor *persistence.Persistor
globalCredentialsPersistor *persistence.Persistor
customerEnsuredIndexes = []mgo.Index{
{
@ -88,56 +84,6 @@ func GetCustomerPersistor() *persistence.Persistor {
return globalCustomerPersistor
}
// GetCustomerVersionsPersistor will return a singleton instance of a versioned customer mongo persistor
func GetCustomerVersionsPersistor() *persistence.Persistor {
url := configuration.GetMongoURL()
collection := configuration.MONGO_COLLECTION_CUSTOMERS_HISTORY
if globalCustomerVersionsPersistor == nil {
p, err := persistence.NewPersistor(url, collection)
if err != nil || p == nil {
panic(errors.New("failed to create mongoDB order persistor: " + err.Error()))
}
globalCustomerVersionsPersistor = p
return globalCustomerVersionsPersistor
}
if url == globalCustomerVersionsPersistor.GetURL() && collection == globalCustomerVersionsPersistor.GetCollectionName() {
return globalCustomerVersionsPersistor
}
p, err := persistence.NewPersistor(url, collection)
if err != nil || p == nil {
panic(err)
}
globalCustomerVersionsPersistor = p
return globalCustomerVersionsPersistor
}
// GetCredentialsPersistor will return a singleton instance of a credentials mongo persistor
func GetCredentialsPersistor() *persistence.Persistor {
url := configuration.GetMongoURL()
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) {
session, collection := GetCustomerPersistor().GetCollection()
@ -165,7 +111,7 @@ func Find(query *bson.M, customProvider CustomerCustomProvider) (iter func() (cu
_, errCount := q.Count()
if errCount != nil {
if errors.Is(errCount, mgo.ErrNotFound) {
if stderr.Is(errCount, mgo.ErrNotFound) {
return nil, ErrCustomerNotFound
}
return nil, errCount
@ -185,61 +131,29 @@ func Find(query *bson.M, customProvider CustomerCustomProvider) (iter func() (cu
// UpsertCustomer will save a given customer in mongo collection
func UpsertCustomer(c *Customer) error {
// order is unlinked or not yet inserted in db
if c.unlinkDB || c.BsonId == "" {
return nil
}
session, collection := GetCustomerPersistor().GetCollection()
defer session.Close()
// 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 := collection.Find(&bson.M{"_id": c.BsonId}).Select(&bson.M{"version": 1}).One(customerLatestFromDb)
if err != nil {
log.Println("Upsert failed: Could not find customer with id", c.GetID(), "Error:", err)
return err
if c.Version == nil {
return errors.Wrap(shop_error.ErrorVersionConflict, "version must not be empty")
}
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)
currentVersion := c.Version.Current
c.Version.Increment()
if currentVersion == 0 {
// it is not allowed to insert customers, @see NewCustomer method instead
return shop_error.ErrorVersionConflict
}
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()
// upsert existing customer
err := collection.Update(bson.M{
"$and": []bson.M{
{KeyAddrKey: c.AddrKey},
{"version.current": currentVersion},
}}, c)
if stderr.Is(err, mgo.ErrNotFound) {
return shop_error.ErrorVersionConflict
}
_, err = collection.UpsertId(c.BsonId, c)
if err != nil {
return err
}
return saveCustomerVersionHistory(c)
}
func saveCustomerVersionHistory(c *Customer) error {
// Store version in history
currentID := c.BsonId
c.BsonId = "" // Temporarily reset Mongo ObjectId, so that we can perfrom an Insert.
session, collection := GetCustomerVersionsPersistor().GetCollection()
defer session.Close()
err := collection.Insert(c)
c.BsonId = currentID // restore bsonId
return err
}
@ -257,12 +171,10 @@ func DeleteCustomer(c *Customer) error {
// remove customer
err := collection.Remove(bson.M{"_id": c.BsonId})
if err != nil && err.Error() != shop_error.ErrorNotInDatabase {
return err
if stderr.Is(mgo.ErrNotFound, err) {
return nil
}
// remove credentials
return DeleteCredential(c.Email)
return err
}
func DeleteCustomerById(id string) error {
customer, err := GetCustomerById(id, nil)
@ -282,45 +194,12 @@ func GetCustomerByAddrKeyHash(addrKeyHash string, customProvider CustomerCustomP
}
func GetCustomerByQuery(query *bson.M, customProvider CustomerCustomProvider) (*Customer, error) {
return findOneCustomer(query, nil, "", customProvider, false)
return findOneCustomer(query, nil, "", customProvider)
}
// GetCustomerById returns the customer with id
func GetCustomerById(id string, customProvider CustomerCustomProvider) (*Customer, error) {
return findOneCustomer(&bson.M{"id": id}, nil, "", customProvider, false)
}
func GetCurrentCustomerByIdFromVersionsHistory(customerId string, customProvider CustomerCustomProvider) (*Customer, error) {
return findOneCustomer(&bson.M{"id": customerId}, nil, "-version.current", customProvider, true)
}
func GetCurrentVersionOfCustomerFromVersionsHistory(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))
}
customerFromVersionsHistory, 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.
customerFromVersionsHistory.BsonId = currentCustomer.BsonId
customerFromVersionsHistory.Flags.forceUpsert = true
return customerFromVersionsHistory.Upsert()
return findOneCustomer(&bson.M{"id": id}, nil, "", customProvider)
}
func DropAllCustomers() error {
@ -330,34 +209,15 @@ func DropAllCustomers() error {
return collection.DropCollection()
}
func DropAllCredentials() error {
session, collection := GetCredentialsPersistor().GetCollection()
defer session.Close()
return collection.DropCollection()
}
func DropAllCustomersAndCredentials() error {
if err := DropAllCustomers(); err != nil {
return err
}
return DropAllCredentials()
}
//------------------------------------------------------------------
// ~ 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 session *mgo.Session
var collection *mgo.Collection
func findOneCustomer(find *bson.M, selection *bson.M, sort string, customProvider CustomerCustomProvider) (*Customer, error) {
if fromHistory {
session, collection = GetCustomerVersionsPersistor().GetCollection()
} else {
session, collection = GetCustomerPersistor().GetCollection()
}
session, collection := GetCustomerPersistor().GetCollection()
defer session.Close()
customer := &Customer{}
@ -370,7 +230,7 @@ func findOneCustomer(find *bson.M, selection *bson.M, sort string, customProvide
if sort != "" {
err := collection.Find(find).Select(selection).Sort(sort).One(customer)
if err != nil {
if errors.Is(err, mgo.ErrNotFound) {
if stderr.Is(err, mgo.ErrNotFound) {
return nil, ErrCustomerNotFound
}
return nil, err
@ -378,7 +238,7 @@ func findOneCustomer(find *bson.M, selection *bson.M, sort string, customProvide
} else {
err := collection.Find(find).Select(selection).One(customer)
if err != nil {
if errors.Is(err, mgo.ErrNotFound) {
if stderr.Is(err, mgo.ErrNotFound) {
return nil, ErrCustomerNotFound
}
return nil, err
@ -402,22 +262,10 @@ func findOneCustomer(find *bson.M, selection *bson.M, sort string, customProvide
func insertCustomer(c *Customer) error {
session, collection := GetCustomerPersistor().GetCollection()
defer session.Close()
alreadyExists, err := AlreadyExistsInDB(c.GetID())
if err != nil {
return err
err := collection.Insert(c)
if mgo.IsDup(err) {
return errors.Wrap(shop_error.ErrorDuplicateKey, err.Error())
}
if alreadyExists {
log.Println("User with id", c.GetID(), "already exists in the database!")
return nil
}
err = collection.Insert(c)
if err != nil {
return err
}
hsession, hcollection := GetCustomerVersionsPersistor().GetCollection()
defer hsession.Close()
err = hcollection.Insert(c)
return err
}

1
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/mitchellh/mapstructure v1.3.0
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v1.6.0
github.com/satori/go.uuid v1.2.0
github.com/sergi/go-diff v1.1.0

1
go.sum
View File

@ -64,6 +64,7 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96d
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

View File

@ -1,6 +1,15 @@
package shop_error
import "strings"
import (
"errors"
"strings"
)
// ErrorVersionConflict version conflict while upserting to mongo
var ErrorVersionConflict = errors.New("version conflict")
// ErrorDuplicateKey an index key constraint mismatch
var ErrorDuplicateKey = errors.New("duplicate key")
const (
ErrorAlreadyExists = "already existing in db" // do not change, this string is returned by MongoDB