File size: 4,950 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
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package app

import (
	"context"
	"errors"
	"fmt"

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

// App represents an installed app
type App interface {
	GetAppBase() AppBase
	GetID() AppID
	GetType() AppType
	GetName() string
	GetDescription() *string
	GetStatus() AppStatus
	GetMetadata() models.Metadata
	GetListing() MarketplaceListing

	GetEventAppData() (EventAppData, error)

	UpdateAppConfig(ctx context.Context, input AppConfigUpdate) error

	// ValidateCapabilities validates if the app can run for the given capabilities
	ValidateCapabilities(capabilities ...CapabilityType) error

	// Customer data
	GetCustomerData(ctx context.Context, input GetAppInstanceCustomerDataInput) (CustomerData, error)
	UpsertCustomerData(ctx context.Context, input UpsertAppInstanceCustomerDataInput) error
	DeleteCustomerData(ctx context.Context, input DeleteAppInstanceCustomerDataInput) error
}

type GetAppInstanceCustomerDataInput struct {
	CustomerID customer.CustomerID
}

func (i GetAppInstanceCustomerDataInput) Validate() error {
	if err := i.CustomerID.Validate(); err != nil {
		return err
	}

	return nil
}

type UpsertAppInstanceCustomerDataInput struct {
	CustomerID customer.CustomerID
	Data       CustomerData
}

func (i UpsertAppInstanceCustomerDataInput) Validate() error {
	if err := i.CustomerID.Validate(); err != nil {
		return err
	}

	if err := i.Data.Validate(); err != nil {
		return err
	}

	return nil
}

type DeleteAppInstanceCustomerDataInput struct {
	CustomerID customer.CustomerID
}

func (i DeleteAppInstanceCustomerDataInput) Validate() error {
	if err := i.CustomerID.Validate(); err != nil {
		return err
	}

	return nil
}

// GetAppInput is the input for getting an installed app
type GetAppInput = AppID

type AppConfigUpdate interface {
	models.Validator
}

// UpdateAppInput is the input for setting an app as default for a type
type UpdateAppInput struct {
	AppID           AppID
	Name            string
	Description     *string
	Default         bool
	Metadata        *map[string]string
	AppConfigUpdate AppConfigUpdate
}

func (i UpdateAppInput) Validate() error {
	if err := i.AppID.Validate(); err != nil {
		return fmt.Errorf("error validating app ID: %w", err)
	}

	// Required fields
	if i.Name == "" {
		return errors.New("name is required")
	}

	if i.Metadata != nil {
		for k, v := range *i.Metadata {
			if k == "" {
				return errors.New("metadata key is required")
			}

			if v == "" {
				return errors.New("metadata value is required")
			}
		}
	}

	if i.AppConfigUpdate != nil {
		if err := i.AppConfigUpdate.Validate(); err != nil {
			return fmt.Errorf("error validating app entity update: %w", err)
		}
	}

	return nil
}

// CreateAppInput is the input for creating an app
type CreateAppInput struct {
	// AppID is optional. If not provided, a new AppID will be generated by the database
	ID          *AppID
	Namespace   string
	Name        string
	Description string
	Type        AppType
}

func (i CreateAppInput) Validate() error {
	if i.Namespace == "" {
		return errors.New("namespace is required")
	}

	if i.Name == "" {
		return errors.New("name is required")
	}

	return nil
}

// ListAppInput is the input for listing installed apps
type ListAppInput struct {
	Namespace string
	pagination.Page

	AppIDs         []AppID
	Type           *AppType
	IncludeDeleted bool
	// Only list apps that has data for the given customer
	CustomerID *customer.CustomerID
}

func (i ListAppInput) Validate() error {
	var errs []error

	if i.Namespace == "" {
		errs = append(errs, models.NewGenericValidationError(
			errors.New("namespace is required"),
		))
	}

	if i.CustomerID != nil {
		if err := i.CustomerID.Validate(); err != nil {
			errs = append(errs, models.NewGenericValidationError(
				fmt.Errorf("error validating customer id: %w", err),
			))
		}

		if i.CustomerID.Namespace != i.Namespace {
			errs = append(errs, models.NewGenericValidationError(
				fmt.Errorf("customer id namespace %s does not match app namespace %s", i.CustomerID.Namespace, i.Namespace),
			))
		}
	}

	if len(i.AppIDs) > 0 {
		for _, appID := range i.AppIDs {
			if appID.Namespace != i.Namespace {
				errs = append(errs, models.NewGenericValidationError(
					fmt.Errorf("app id namespace %s does not match app namespace %s", appID.Namespace, i.Namespace),
				))
			}

			if err := appID.Validate(); err != nil {
				errs = append(errs, models.NewGenericValidationError(
					fmt.Errorf("error validating app id: %w", err),
				))
			}
		}
	}

	return errors.Join(errs...)
}

// UpdateAppStatusInput is the input for updating an app status
type UpdateAppStatusInput struct {
	ID     AppID
	Status AppStatus
}

func (i UpdateAppStatusInput) Validate() error {
	if err := i.ID.Validate(); err != nil {
		return err
	}

	if i.Status == "" {
		return errors.New("status is required")
	}

	return nil
}