File size: 4,154 Bytes
d6f631f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package httpdriver

import (
	"context"
	"fmt"
	"net/http"

	"github.com/samber/lo"

	"github.com/openmeterio/openmeter/api"
	"github.com/openmeterio/openmeter/openmeter/productcatalog"
	"github.com/openmeterio/openmeter/openmeter/productcatalog/addon"
	"github.com/openmeterio/openmeter/openmeter/subscription"
	subscriptionaddon "github.com/openmeterio/openmeter/openmeter/subscription/addon"
	subscriptionworkflow "github.com/openmeterio/openmeter/openmeter/subscription/workflow"
	"github.com/openmeterio/openmeter/pkg/framework/commonhttp"
	"github.com/openmeterio/openmeter/pkg/framework/transport/httptransport"
	"github.com/openmeterio/openmeter/pkg/models"
)

type (
	CreateSubscriptionAddonParams = struct {
		SubscriptionID string
	}
	CreateSubscriptionAddonRequest = struct {
		SubscriptionID models.NamespacedID
		AddonInput     subscriptionworkflow.AddAddonWorkflowInput
	}
	CreateSubscriptionAddonResponse = api.SubscriptionAddon
	CreateSubscriptionAddonHandler  = httptransport.HandlerWithArgs[CreateSubscriptionAddonRequest, CreateSubscriptionAddonResponse, CreateSubscriptionAddonParams]
)

func (h *handler) CreateSubscriptionAddon() CreateSubscriptionAddonHandler {
	return httptransport.NewHandlerWithArgs(
		func(ctx context.Context, r *http.Request, params CreateSubscriptionAddonParams) (CreateSubscriptionAddonRequest, error) {
			ns, err := h.resolveNamespace(ctx)
			if err != nil {
				return CreateSubscriptionAddonRequest{}, fmt.Errorf("failed to resolve namespace: %w", err)
			}

			var body api.CreateSubscriptionAddonJSONRequestBody

			if err := commonhttp.JSONRequestBodyDecoder(r, &body); err != nil {
				return CreateSubscriptionAddonRequest{}, err
			}

			addonInput, err := MapCreateSubscriptionAddonRequestToInput(body)
			if err != nil {
				return CreateSubscriptionAddonRequest{}, err
			}

			return CreateSubscriptionAddonRequest{
				SubscriptionID: models.NamespacedID{
					Namespace: ns,
					ID:        params.SubscriptionID,
				},
				AddonInput: addonInput,
			}, nil
		},
		func(ctx context.Context, req CreateSubscriptionAddonRequest) (CreateSubscriptionAddonResponse, error) {
			var def CreateSubscriptionAddonResponse

			// The add-on's own rate cards are serialized in the response, so reject a unit_config
			// add-on (the v1 shape cannot represent it) before it lands on the subscription. We do
			// NOT reject based on the subscription's plan
			addonToAdd, err := h.AddonService.GetAddon(ctx, addon.GetAddonInput{
				NamespacedID: models.NamespacedID{
					Namespace: req.SubscriptionID.Namespace,
					ID:        req.AddonInput.AddonID,
				},
			})
			if err != nil {
				return def, err
			}

			if addonToAdd.AsProductCatalogAddon().HasUnitConfig() {
				return def, productcatalog.ErrUnitConfigNotRepresentable
			}

			subsAdds, err := h.SubscriptionAddonService.List(ctx, req.SubscriptionID.Namespace, subscriptionaddon.ListSubscriptionAddonsInput{
				SubscriptionID: req.SubscriptionID.ID,
			})
			if err != nil {
				return def, err
			}

			var view subscription.SubscriptionView
			var add subscriptionaddon.SubscriptionAddon

			// If the addon is already present, we'll change the quantity instead as a convenience
			if sAdd, ok := lo.Find(subsAdds.Items, func(subAdd subscriptionaddon.SubscriptionAddon) bool {
				return subAdd.Addon.ID == req.AddonInput.AddonID
			}); ok {
				view, add, err = h.SubscriptionWorkflowService.ChangeAddonQuantity(ctx, req.SubscriptionID, subscriptionworkflow.ChangeAddonQuantityWorkflowInput{
					SubscriptionAddonID: sAdd.NamespacedID,
					Quantity:            req.AddonInput.InitialQuantity,
					Timing:              req.AddonInput.Timing,
				})
			} else {
				// Otherwise, we'll create it as per usual
				view, add, err = h.SubscriptionWorkflowService.AddAddon(ctx, req.SubscriptionID, req.AddonInput)
			}

			if err != nil {
				return def, err
			}

			return MapSubscriptionAddonToResponse(view, add)
		},
		commonhttp.JSONResponseEncoderWithStatus[CreateSubscriptionAddonResponse](http.StatusOK),
		httptransport.AppendOptions(
			h.Options,
			httptransport.WithOperationName("createSubscriptionAddon"),
		)...,
	)
}