github.com/timoth-y/kicksware-api/order-service@v0.0.0-20201002192818-87b546a7ae5a/usecase/storage/mongo/repository.go (about)

     1  package mongo
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"github.com/pkg/errors"
     8  	"go.mongodb.org/mongo-driver/bson"
     9  	"go.mongodb.org/mongo-driver/mongo"
    10  	"go.mongodb.org/mongo-driver/mongo/options"
    11  	"go.mongodb.org/mongo-driver/mongo/readpref"
    12  	"io/ioutil"
    13  	"time"
    14  
    15  	"github.com/golang/glog"
    16  	TLS "github.com/timoth-y/kicksware-api/service-common/core/meta"
    17  	"github.com/timoth-y/kicksware-api/service-common/util"
    18  
    19  	"github.com/timoth-y/kicksware-api/service-common/core/meta"
    20  	"github.com/timoth-y/kicksware-api/order-service/core/model"
    21  	"github.com/timoth-y/kicksware-api/order-service/core/repo"
    22  	"github.com/timoth-y/kicksware-api/order-service/env"
    23  	"github.com/timoth-y/kicksware-api/order-service/usecase/business"
    24  )
    25  
    26  type repository struct {
    27  	client     *mongo.Client
    28  	database   *mongo.Database
    29  	collection *mongo.Collection
    30  	timeout    time.Duration
    31  }
    32  
    33  func NewRepository(config env.DataStoreConfig) (repo.OrderRepository, error) {
    34  	repo := &repository{
    35  		timeout:  time.Duration(config.Timeout) * time.Second,
    36  	}
    37  	client, err := newMongoClient(config); if err != nil {
    38  		return nil, errors.Wrap(err, "repository.NewRepository")
    39  	}
    40  	repo.client = client
    41  	database := client.Database(config.Database)
    42  	repo.database = database
    43  	repo.collection = database.Collection(config.Collection)
    44  	return repo, nil
    45  }
    46  
    47  func newMongoClient(config env.DataStoreConfig) (*mongo.Client, error) {
    48  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.Timeout)*time.Second)
    49  	defer cancel()
    50  	client, err := mongo.Connect(ctx, options.Client().
    51  		ApplyURI(config.URL).
    52  		SetTLSConfig(newTLSConfig(config.TLS)).
    53  		SetAuth(options.Credential{
    54  			Username: config.Login, Password: config.Password,
    55  		}),
    56  	)
    57  	err = client.Ping(ctx, readpref.Primary()); if err != nil {
    58  		return nil, err
    59  	}
    60  	return client, nil
    61  }
    62  
    63  func newTLSConfig(tlsConfig *TLS.TLSCertificate) *tls.Config {
    64  	if !tlsConfig.EnableTLS {
    65  		return nil
    66  	}
    67  	certs := x509.NewCertPool()
    68  	pem, err := ioutil.ReadFile(tlsConfig.CertFile); if err != nil {
    69  		glog.Fatalln(err)
    70  	}
    71  	certs.AppendCertsFromPEM(pem)
    72  	return &tls.Config{
    73  		RootCAs: certs,
    74  	}
    75  }
    76  
    77  func (r *repository) FetchOne(code string, params *meta.RequestParams) (*model.Order, error) {
    78  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
    79  	defer cancel()
    80  
    81  	query := r.buildQueryPipeline(bson.M{"unique_id": code}, params)
    82  	cursor, err := r.collection.Aggregate(ctx, query); if err != nil {
    83  		return nil, errors.Wrap(err, "repository.Order.FetchOne")
    84  	}
    85  	defer cursor.Close(ctx)
    86  
    87  	var orders []*model.Order
    88  	if err = cursor.All(ctx, &orders); err != nil {
    89  		return nil, errors.Wrap(err, "repository.Order.FetchOne")
    90  	}
    91  	if orders == nil || len(orders) == 0 {
    92  		if err == mongo.ErrNoDocuments{
    93  			return nil, errors.Wrap(business.ErrOrderNotFound, "repository.Order.FetchOne")
    94  		}
    95  		return nil, errors.Wrap(err, "repository.Order.FetchOne")
    96  	}
    97  	return orders[0], nil
    98  }
    99  
   100  func (r *repository) Fetch(codes []string, params *meta.RequestParams) ([]*model.Order, error) {
   101  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
   102  	defer cancel()
   103  
   104  	query := r.buildQueryPipeline(bson.M{"unique_id": bson.M{"$in": codes}}, params)
   105  	cursor, err := r.collection.Aggregate(ctx, query); if err != nil  {
   106  		return nil, errors.Wrap(err, "repository.Order.Fetch")
   107  	}
   108  	defer cursor.Close(ctx)
   109  
   110  	var orders []*model.Order
   111  	if err = cursor.All(ctx, &orders); err != nil {
   112  		return nil, errors.Wrap(err, "repository.Order.Fetch")
   113  	}
   114  	if orders == nil || len(orders) == 0 {
   115  		if err == mongo.ErrNoDocuments {
   116  			return nil, errors.Wrap(business.ErrOrderNotFound, "repository.Order.Fetch")
   117  		}
   118  		return nil, errors.Wrap(err, "repository.Order.Fetch")
   119  	}
   120  	return orders, nil
   121  }
   122  
   123  func (r *repository) FetchAll(params *meta.RequestParams) ([]*model.Order, error) {
   124  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
   125  	defer cancel()
   126  
   127  	query := r.buildQueryPipeline(bson.M{}, params)
   128  	cursor, err := r.collection.Aggregate(ctx, query); if err != nil  {
   129  		return nil, errors.Wrap(err, "repository.Order.FetchAll")
   130  	}
   131  	defer cursor.Close(ctx)
   132  
   133  	var order []*model.Order
   134  	if err = cursor.All(ctx, &order); err != nil {
   135  		if err == mongo.ErrNoDocuments {
   136  			return nil, errors.Wrap(business.ErrOrderNotFound, "repository.Order.FetchAll")
   137  		}
   138  		return nil, errors.Wrap(err, "repository.Order.FetchAll")
   139  	}
   140  	return order, nil
   141  }
   142  
   143  func (r *repository) FetchQuery(query meta.RequestQuery, params *meta.RequestParams) ([]*model.Order, error) {
   144  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
   145  	defer cancel()
   146  
   147  	filter, err := query.ToBson(); if err != nil {
   148  		return nil, errors.Wrap(err, "repository.Order.FetchQuery")
   149  	}
   150  	queryPipe := r.buildQueryPipeline(filter, params)
   151  	cursor, err := r.collection.Aggregate(ctx, queryPipe)
   152  	if err != nil  {
   153  		return nil, errors.Wrap(err, "repository.Order.FetchQuery")
   154  	}
   155  	defer cursor.Close(ctx)
   156  
   157  	var orders []*model.Order
   158  	if err = cursor.All(ctx, &orders); err != nil {
   159  		return nil, errors.Wrap(err, "repository.Order.FetchQuery")
   160  	}
   161  	if orders == nil || len(orders) == 0 {
   162  		return nil, errors.Wrap(business.ErrOrderNotFound, "repository.Order.FetchQuery")
   163  	}
   164  	return orders, nil
   165  }
   166  
   167  func (r *repository) StoreOne(order *model.Order) error {
   168  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
   169  	defer cancel()
   170  	_, err := r.collection.InsertOne(ctx, order)
   171  	if err != nil {
   172  		return errors.Wrap(err, "repository.Order.Store")
   173  	}
   174  	return nil
   175  }
   176  
   177  func (r *repository) Modify(order *model.Order) error {
   178  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
   179  	defer cancel()
   180  	doc, err := util.ToBsonMap(order); if err != nil {
   181  		return errors.Wrap(err, "repository.Order.Modify")
   182  	}
   183  	update := bson.D{
   184  		{"$set", doc},
   185  	}
   186  	filter := bson.M{"unique_id": order.UniqueID}
   187  	if _, err = r.collection.UpdateOne(ctx, filter, update); err != nil {
   188  		return errors.Wrap(err, "repository.Order.Modify")
   189  	}
   190  	return nil
   191  }
   192  
   193  func (r *repository) Remove(code string) error {
   194  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
   195  	defer cancel()
   196  	filter := bson.M{"unique_id": code}
   197  	if _, err := r.collection.DeleteOne(ctx, filter); err != nil {
   198  		return errors.Wrap(err, "repository.Order.Remove")
   199  	}
   200  	return nil
   201  }
   202  
   203  func (r *repository) Count(query meta.RequestQuery, params *meta.RequestParams) (int, error) {
   204  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
   205  	defer cancel()
   206  
   207  	filter, err := query.ToBson(); if err != nil {
   208  		return 0, errors.Wrap(err, "repository.Order.Count")
   209  	}
   210  
   211  	count, err := r.collection.CountDocuments(ctx, filter); if err != nil {
   212  		return 0, errors.Wrap(err, "repository.Order.Count")
   213  	}
   214  	return int(count), nil
   215  }
   216  
   217  func (r *repository) CountAll() (int, error) {
   218  	ctx, cancel := context.WithTimeout(context.Background(), r.timeout)
   219  	defer cancel()
   220  
   221  	filter := bson.M{}
   222  	count, err := r.collection.CountDocuments(ctx, filter); if err != nil {
   223  		return 0, errors.Wrap(err, "repository.Order.Count")
   224  	}
   225  	return int(count), nil
   226  }
   227  
   228  
   229  func (r *repository) buildQueryPipeline(matchQuery bson.M, param *meta.RequestParams) mongo.Pipeline {
   230  	pipe := mongo.Pipeline{}
   231  	pipe = append(pipe, bson.D{{"$match", matchQuery}})
   232  
   233  	return pipe
   234  }
   235