File size: 2,036 Bytes
fea99b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package entutils

import (
	"errors"
	"fmt"

	"github.com/samber/lo"

	"github.com/openmeterio/openmeter/pkg/models"
)

type InIDOrderAccessor interface {
	IDMixinGetter
	NamespaceMixinGetter
}

var (
	ErrNamespaceRequired = errors.New("namespace is required")
	ErrIDRequired        = errors.New("id is required")
	ErrDuplicateID       = errors.New("duplicate id")
	ErrNotFound          = errors.New("not found")
)

func InIDOrder[T InIDOrderAccessor](namespace string, targetOrderIDs []string, results []T) ([]T, error) {
	// Input validation (let's make sure that namespace/id is set for all entities)
	if namespace == "" {
		return nil, ErrNamespaceRequired
	}

	for _, id := range targetOrderIDs {
		if id == "" {
			return nil, ErrIDRequired
		}
	}

	for _, result := range results {
		namespacedID := models.NamespacedID{
			Namespace: result.GetNamespace(),
			ID:        result.GetID(),
		}
		if err := namespacedID.Validate(); err != nil {
			return nil, err
		}
	}

	// Logic implementation
	if len(targetOrderIDs) == 0 && len(results) == 0 {
		return results, nil
	}

	entitiesByID := lo.GroupBy(results, func(result T) models.NamespacedID {
		return models.NamespacedID{
			Namespace: result.GetNamespace(),
			ID:        result.GetID(),
		}
	})

	// Check for duplicate results
	for id, entities := range entitiesByID {
		if len(entities) > 1 {
			return nil, fmt.Errorf("%w [id=%s, count=%d]", ErrDuplicateID, id, len(entities))
		}
	}

	// We allow for more entities being present in the results set, as we are not filtering for namespace for the query to allow
	// multi-namespace listing as needed.
	var errs []error
	out := make([]T, 0, len(targetOrderIDs))
	for _, id := range targetOrderIDs {
		entities, ok := entitiesByID[models.NamespacedID{Namespace: namespace, ID: id}]
		if !ok {
			errs = append(errs, fmt.Errorf("%w [id=%s]", ErrNotFound, id))
			continue
		}

		out = append(out, entities...)
	}

	if len(errs) > 0 {
		return nil, models.NewGenericNotFoundError(errors.Join(errs...))
	}

	return out, nil
}