260 lines
5.4 KiB
Go
260 lines
5.4 KiB
Go
// Copyright (c) Mainflux
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package mocks
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/mainflux/mainflux/pkg/errors"
|
|
"github.com/mainflux/mainflux/pkg/uuid"
|
|
"github.com/mainflux/mainflux/twins"
|
|
)
|
|
|
|
var _ twins.TwinRepository = (*twinRepositoryMock)(nil)
|
|
|
|
type twinRepositoryMock struct {
|
|
mu sync.Mutex
|
|
twins map[string]twins.Twin
|
|
}
|
|
|
|
// NewTwinRepository creates in-memory twin repository.
|
|
func NewTwinRepository() twins.TwinRepository {
|
|
return &twinRepositoryMock{
|
|
twins: make(map[string]twins.Twin),
|
|
}
|
|
}
|
|
|
|
func (trm *twinRepositoryMock) Save(ctx context.Context, twin twins.Twin) (string, error) {
|
|
trm.mu.Lock()
|
|
defer trm.mu.Unlock()
|
|
|
|
for _, tw := range trm.twins {
|
|
if tw.ID == twin.ID {
|
|
return "", errors.ErrConflict
|
|
}
|
|
}
|
|
|
|
trm.twins[key(twin.Owner, twin.ID)] = twin
|
|
|
|
return twin.ID, nil
|
|
}
|
|
|
|
func (trm *twinRepositoryMock) Update(ctx context.Context, twin twins.Twin) error {
|
|
trm.mu.Lock()
|
|
defer trm.mu.Unlock()
|
|
|
|
dbKey := key(twin.Owner, twin.ID)
|
|
if _, ok := trm.twins[dbKey]; !ok {
|
|
return errors.ErrNotFound
|
|
}
|
|
|
|
trm.twins[dbKey] = twin
|
|
|
|
return nil
|
|
}
|
|
|
|
func (trm *twinRepositoryMock) RetrieveByID(_ context.Context, twinID string) (twins.Twin, error) {
|
|
trm.mu.Lock()
|
|
defer trm.mu.Unlock()
|
|
|
|
for k, v := range trm.twins {
|
|
if twinID == v.ID {
|
|
return trm.twins[k], nil
|
|
}
|
|
}
|
|
|
|
return twins.Twin{}, errors.ErrNotFound
|
|
}
|
|
|
|
func (trm *twinRepositoryMock) RetrieveByAttribute(ctx context.Context, channel, subtopic string) ([]string, error) {
|
|
var ids []string
|
|
for _, twin := range trm.twins {
|
|
def := twin.Definitions[len(twin.Definitions)-1]
|
|
for _, attr := range def.Attributes {
|
|
if attr.Channel == channel && (attr.Subtopic == twins.SubtopicWildcard || attr.Subtopic == subtopic) {
|
|
ids = append(ids, twin.ID)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func (trm *twinRepositoryMock) RetrieveAll(_ context.Context, owner string, offset uint64, limit uint64, name string, metadata twins.Metadata) (twins.Page, error) {
|
|
trm.mu.Lock()
|
|
defer trm.mu.Unlock()
|
|
|
|
items := make([]twins.Twin, 0)
|
|
|
|
if limit <= 0 {
|
|
return twins.Page{}, nil
|
|
}
|
|
|
|
for k, v := range trm.twins {
|
|
if (uint64)(len(items)) >= limit {
|
|
break
|
|
}
|
|
if len(name) > 0 && v.Name != name {
|
|
continue
|
|
}
|
|
if !strings.HasPrefix(k, owner) {
|
|
continue
|
|
}
|
|
suffix := string(v.ID[len(uuid.Prefix):])
|
|
id, _ := strconv.ParseUint(suffix, 10, 64)
|
|
if id > offset && id <= offset+limit {
|
|
items = append(items, v)
|
|
}
|
|
}
|
|
|
|
sort.SliceStable(items, func(i, j int) bool {
|
|
return items[i].ID < items[j].ID
|
|
})
|
|
|
|
total := uint64(len(trm.twins))
|
|
page := twins.Page{
|
|
Twins: items,
|
|
PageMetadata: twins.PageMetadata{
|
|
Total: total,
|
|
Offset: offset,
|
|
Limit: limit,
|
|
},
|
|
}
|
|
|
|
return page, nil
|
|
}
|
|
|
|
func (trm *twinRepositoryMock) Remove(ctx context.Context, twinID string) error {
|
|
trm.mu.Lock()
|
|
defer trm.mu.Unlock()
|
|
|
|
for k, v := range trm.twins {
|
|
if twinID == v.ID {
|
|
delete(trm.twins, k)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type twinCacheMock struct {
|
|
mu sync.Mutex
|
|
attrIds map[string]map[string]bool
|
|
idAttrs map[string]map[string]bool
|
|
}
|
|
|
|
// NewTwinCache returns mock cache instance.
|
|
func NewTwinCache() twins.TwinCache {
|
|
return &twinCacheMock{
|
|
attrIds: make(map[string]map[string]bool),
|
|
idAttrs: make(map[string]map[string]bool),
|
|
}
|
|
}
|
|
|
|
func (tcm *twinCacheMock) Save(_ context.Context, twin twins.Twin) error {
|
|
tcm.mu.Lock()
|
|
defer tcm.mu.Unlock()
|
|
|
|
if len(twin.Definitions) < 1 {
|
|
return nil
|
|
}
|
|
def := twin.Definitions[len(twin.Definitions)-1]
|
|
tcm.save(def, twin.ID)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (tcm *twinCacheMock) SaveIDs(ctx context.Context, channel, subtopic string, ids []string) error {
|
|
tcm.mu.Lock()
|
|
defer tcm.mu.Unlock()
|
|
|
|
for _, id := range ids {
|
|
attrKey := channel + subtopic
|
|
if _, ok := tcm.attrIds[attrKey]; !ok {
|
|
tcm.attrIds[attrKey] = make(map[string]bool)
|
|
}
|
|
tcm.attrIds[attrKey][id] = true
|
|
|
|
if _, ok := tcm.idAttrs[id]; !ok {
|
|
tcm.idAttrs[id] = make(map[string]bool)
|
|
}
|
|
tcm.idAttrs[id][attrKey] = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (tcm *twinCacheMock) Update(_ context.Context, twin twins.Twin) error {
|
|
tcm.mu.Lock()
|
|
defer tcm.mu.Unlock()
|
|
|
|
if err := tcm.remove(twin.ID); err != nil {
|
|
return nil
|
|
}
|
|
|
|
if len(twin.Definitions) < 1 {
|
|
return nil
|
|
}
|
|
def := twin.Definitions[len(twin.Definitions)-1]
|
|
tcm.save(def, twin.ID)
|
|
return nil
|
|
}
|
|
|
|
func (tcm *twinCacheMock) IDs(_ context.Context, channel, subtopic string) ([]string, error) {
|
|
tcm.mu.Lock()
|
|
defer tcm.mu.Unlock()
|
|
|
|
var ids []string
|
|
|
|
for k := range tcm.attrIds[channel+subtopic] {
|
|
ids = append(ids, k)
|
|
}
|
|
for k := range tcm.attrIds[channel+twins.SubtopicWildcard] {
|
|
ids = append(ids, k)
|
|
}
|
|
|
|
return ids, nil
|
|
}
|
|
|
|
func (tcm *twinCacheMock) Remove(_ context.Context, twinID string) error {
|
|
tcm.mu.Lock()
|
|
defer tcm.mu.Unlock()
|
|
|
|
return tcm.remove(twinID)
|
|
}
|
|
|
|
func (tcm *twinCacheMock) remove(twinID string) error {
|
|
attrKeys, ok := tcm.idAttrs[twinID]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
delete(tcm.idAttrs, twinID)
|
|
for attrKey := range attrKeys {
|
|
delete(tcm.attrIds[attrKey], twinID)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tcm *twinCacheMock) save(def twins.Definition, twinID string) {
|
|
for _, attr := range def.Attributes {
|
|
attrKey := attr.Channel + attr.Subtopic
|
|
if _, ok := tcm.attrIds[attrKey]; !ok {
|
|
tcm.attrIds[attrKey] = make(map[string]bool)
|
|
}
|
|
tcm.attrIds[attrKey][twinID] = true
|
|
|
|
idKey := twinID
|
|
if _, ok := tcm.idAttrs[idKey]; !ok {
|
|
tcm.idAttrs[idKey] = make(map[string]bool)
|
|
}
|
|
tcm.idAttrs[idKey][attrKey] = true
|
|
}
|
|
}
|