File size: 2,476 Bytes
429334c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
package charges

import (
	"errors"
	"fmt"

	"github.com/samber/lo"

	"github.com/openmeterio/openmeter/openmeter/billing/charges/meta"
	"github.com/openmeterio/openmeter/openmeter/customer"
	"github.com/openmeterio/openmeter/pkg/models"
)

type Patch = meta.Patch

var _ models.Validator = (*ApplyPatchesInput)(nil)

type ApplyPatchesInput struct {
	CustomerID customer.CustomerID
	Creates    ChargeIntents

	// PatchesByChargeID is a map of charge ID to the patches to apply to the charge. This format is used to make sure
	// there's only a single patch affecting a single charge.
	PatchesByChargeID map[string]Patch
}

func (i ApplyPatchesInput) Validate() error {
	var errs []error
	if err := i.CustomerID.Validate(); err != nil {
		errs = append(errs, fmt.Errorf("customer ID: %w", err))
	}

	if err := i.Creates.Validate(); err != nil {
		errs = append(errs, fmt.Errorf("creates: %w", err))
	}

	for chargeID, patch := range i.PatchesByChargeID {
		if chargeID == "" {
			errs = append(errs, fmt.Errorf("charge ID is required"))
			continue
		}

		if patch == nil {
			errs = append(errs, fmt.Errorf("patch for charge ID %s is nil", chargeID))
			continue
		}

		if err := patch.Validate(); err != nil {
			errs = append(errs, fmt.Errorf("patch for charge ID %s: %w", chargeID, err))
		}
	}

	return models.NewNillableGenericValidationError(errors.Join(errs...))
}

// ConcatenateApplyPatchesInputs concatenates the given inputs into a single input, while enforcing uniqueness constraints.
func ConcatenateApplyPatchesInputs(inputs ...ApplyPatchesInput) (ApplyPatchesInput, error) {
	if len(inputs) == 0 {
		return ApplyPatchesInput{}, nil
	}

	result := ApplyPatchesInput{
		CustomerID:        inputs[0].CustomerID,
		Creates:           make(ChargeIntents, 0, lo.SumBy(inputs, func(input ApplyPatchesInput) int { return len(input.Creates) })),
		PatchesByChargeID: make(map[string]Patch, lo.SumBy(inputs, func(input ApplyPatchesInput) int { return len(input.PatchesByChargeID) })),
	}

	for _, input := range inputs {
		result.Creates = append(result.Creates, input.Creates...)
		for chargeID, patch := range input.PatchesByChargeID {
			if _, exists := result.PatchesByChargeID[chargeID]; exists {
				return ApplyPatchesInput{}, fmt.Errorf("duplicate charge ID: %s", chargeID)
			}
			result.PatchesByChargeID[chargeID] = patch
		}
	}

	return result, nil
}

func (i ApplyPatchesInput) IsEmpty() bool {
	return len(i.PatchesByChargeID) == 0 && len(i.Creates) == 0
}