Skip to content

Commit 5d3ad21

Browse files
Add message counts for custom and free plan
ref DEV-2322 ref DEV-2284
2 parents 3bd5c37 + 0578684 commit 5d3ad21

File tree

22 files changed

+759
-243
lines changed

22 files changed

+759
-243
lines changed

pkg/lib/usage/globaldb_store.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,50 @@ func (s *GlobalDBStore) FetchUsageRecords(
205205

206206
return out, nil
207207
}
208+
209+
func (s *GlobalDBStore) FetchUsageRecordsInRange(
210+
ctx context.Context,
211+
appID string,
212+
recordName RecordName,
213+
period periodical.Type,
214+
fromStartTime time.Time,
215+
toEndTime time.Time,
216+
) ([]*UsageRecord, error) {
217+
q := s.SQLBuilder.Select(
218+
"id",
219+
"app_id",
220+
"name",
221+
"period",
222+
"start_time",
223+
"end_time",
224+
"count",
225+
"stripe_timestamp",
226+
).
227+
From(s.SQLBuilder.TableName("_portal_usage_record")).
228+
Where(
229+
"app_id = ? AND name = ? AND period = ? AND start_time >= ? AND start_time < ?",
230+
appID,
231+
string(recordName),
232+
string(period),
233+
fromStartTime,
234+
toEndTime,
235+
)
236+
237+
rows, err := s.SQLExecutor.QueryWith(ctx, q)
238+
if err != nil {
239+
return nil, err
240+
}
241+
defer rows.Close()
242+
243+
var out []*UsageRecord
244+
for rows.Next() {
245+
r, err := s.scan(rows)
246+
if err != nil {
247+
return nil, err
248+
}
249+
250+
out = append(out, r)
251+
}
252+
253+
return out, nil
254+
}

pkg/portal/deps.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ var DependencySet = wire.NewSet(
8484
wire.Bind(new(service.AppResourceManagerFactory), new(*appresource.ManagerFactory)),
8585
wire.Bind(new(service.SubscriptionConfigSourceStore), new(*configsource.Store)),
8686
wire.Bind(new(service.SubscriptionPlanStore), new(*plan.Store)),
87-
wire.Bind(new(service.UsageStore), new(*usage.GlobalDBStore)),
87+
wire.Bind(new(service.SubscriptionUsageStore), new(*usage.GlobalDBStore)),
88+
wire.Bind(new(service.UsageUsageStore), new(*usage.GlobalDBStore)),
8889
wire.Bind(new(service.OnboardServiceAdminAPIService), new(*service.AdminAPIService)),
8990

9091
loader.DependencySet,
@@ -112,6 +113,7 @@ var DependencySet = wire.NewSet(
112113
wire.Bind(new(graphql.TutorialService), new(*tutorial.Service)),
113114
wire.Bind(new(graphql.StripeService), new(*libstripe.Service)),
114115
wire.Bind(new(graphql.SubscriptionService), new(*service.SubscriptionService)),
116+
wire.Bind(new(graphql.UsageService), new(*service.UsageService)),
115117
wire.Bind(new(graphql.NFTService), new(*service.NFTService)),
116118
wire.Bind(new(graphql.DenoService), new(*hook.DenoClientImpl)),
117119
wire.Bind(new(graphql.AuditService), new(*service.AuditService)),

pkg/portal/graphql/app.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,27 @@ var nodeApp = node(
396396
return p.Source.(*model.App).Context.Config.FeatureConfig, nil
397397
},
398398
},
399+
"usage": &graphql.Field{
400+
Type: usage,
401+
Args: graphql.FieldConfigArgument{
402+
"date": &graphql.ArgumentConfig{
403+
Type: graphql.NewNonNull(graphql.DateTime),
404+
},
405+
},
406+
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
407+
ctx := p.Context
408+
gqlCtx := GQLContext(ctx)
409+
appID := p.Source.(*model.App).ID
410+
date := p.Args["date"].(time.Time)
411+
412+
usage, err := gqlCtx.UsageService.GetUsage(ctx, appID, date)
413+
if err != nil {
414+
return nil, err
415+
}
416+
417+
return usage, nil
418+
},
419+
},
399420
"planName": &graphql.Field{
400421
Type: graphql.NewNonNull(graphql.String),
401422
Resolve: func(p graphql.ResolveParams) (interface{}, error) {

pkg/portal/graphql/billing.go

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -18,54 +18,6 @@ var priceType = graphql.NewEnum(graphql.EnumConfig{
1818
},
1919
})
2020

21-
var usageType = graphql.NewEnum(graphql.EnumConfig{
22-
Name: "SubscriptionItemPriceUsageType",
23-
Values: graphql.EnumValueConfigMap{
24-
"NONE": &graphql.EnumValueConfig{
25-
Value: model.UsageTypeNone,
26-
},
27-
"SMS": &graphql.EnumValueConfig{
28-
Value: model.UsageTypeSMS,
29-
},
30-
"WHATSAPP": &graphql.EnumValueConfig{
31-
Value: model.UsageTypeWhatsapp,
32-
},
33-
"MAU": &graphql.EnumValueConfig{
34-
Value: model.UsageTypeMAU,
35-
},
36-
},
37-
})
38-
39-
var smsRegion = graphql.NewEnum(graphql.EnumConfig{
40-
Name: "SubscriptionItemPriceSMSRegion",
41-
Values: graphql.EnumValueConfigMap{
42-
"NONE": &graphql.EnumValueConfig{
43-
Value: model.SMSRegionNone,
44-
},
45-
"NORTH_AMERICA": &graphql.EnumValueConfig{
46-
Value: model.SMSRegionNorthAmerica,
47-
},
48-
"OTHER_REGIONS": &graphql.EnumValueConfig{
49-
Value: model.SMSRegionOtherRegions,
50-
},
51-
},
52-
})
53-
54-
var whatsappRegion = graphql.NewEnum(graphql.EnumConfig{
55-
Name: "SubscriptionItemPriceWhatsappRegion",
56-
Values: graphql.EnumValueConfigMap{
57-
"NONE": &graphql.EnumValueConfig{
58-
Value: model.WhatsappRegionNone,
59-
},
60-
"NORTH_AMERICA": &graphql.EnumValueConfig{
61-
Value: model.WhatsappRegionNorthAmerica,
62-
},
63-
"OTHER_REGIONS": &graphql.EnumValueConfig{
64-
Value: model.WhatsappRegionOtherRegions,
65-
},
66-
},
67-
})
68-
6921
var transformQuantityRound = graphql.NewEnum(graphql.EnumConfig{
7022
Name: "TransformQuantityRound",
7123
Values: graphql.EnumValueConfigMap{
@@ -133,7 +85,7 @@ var subscriptionUsage = graphql.NewObject(graphql.ObjectConfig{
13385
Type: graphql.NewNonNull(graphql.DateTime),
13486
},
13587
"items": &graphql.Field{
136-
Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(usageItem))),
88+
Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(subscriptionUsageItem))),
13789
},
13890
},
13991
})
@@ -149,7 +101,7 @@ var subscription = graphql.NewObject(graphql.ObjectConfig{
149101
},
150102
})
151103

152-
var usageItem = graphql.NewObject(graphql.ObjectConfig{
104+
var subscriptionUsageItem = graphql.NewObject(graphql.ObjectConfig{
153105
Name: "SubscriptionUsageItem",
154106
Fields: graphql.Fields{
155107
"type": &graphql.Field{

pkg/portal/graphql/context.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ type SubscriptionService interface {
152152
MarkCheckoutExpired(ctx context.Context, appID string, customerID string) error
153153
}
154154

155+
type UsageService interface {
156+
GetUsage(ctx context.Context,
157+
appID string,
158+
date time.Time,
159+
) (*model.Usage, error)
160+
}
161+
155162
type NFTService interface {
156163
ProbeNFTCollection(ctx context.Context, contractID web3.ContractID) (*apimodel.ProbeCollectionResult, error)
157164
GetContractMetadata(ctx context.Context, contracts []web3.ContractID) ([]apimodel.NFTCollection, error)
@@ -192,6 +199,7 @@ type Context struct {
192199
TutorialService TutorialService
193200
StripeService StripeService
194201
SubscriptionService SubscriptionService
202+
UsageService UsageService
195203
NFTService NFTService
196204
DenoService DenoService
197205
AuditService AuditService

pkg/portal/graphql/usage.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package graphql
2+
3+
import (
4+
"github.com/graphql-go/graphql"
5+
6+
"github.com/authgear/authgear-server/pkg/portal/model"
7+
)
8+
9+
var usage = graphql.NewObject(graphql.ObjectConfig{
10+
Name: "Usage",
11+
Fields: graphql.Fields{
12+
"items": &graphql.Field{
13+
Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(usageItem))),
14+
},
15+
},
16+
})
17+
18+
var usageType = graphql.NewEnum(graphql.EnumConfig{
19+
Name: "UsageType",
20+
Values: graphql.EnumValueConfigMap{
21+
"NONE": &graphql.EnumValueConfig{
22+
Value: model.UsageTypeNone,
23+
},
24+
"SMS": &graphql.EnumValueConfig{
25+
Value: model.UsageTypeSMS,
26+
},
27+
"WHATSAPP": &graphql.EnumValueConfig{
28+
Value: model.UsageTypeWhatsapp,
29+
},
30+
"MAU": &graphql.EnumValueConfig{
31+
Value: model.UsageTypeMAU,
32+
},
33+
},
34+
})
35+
36+
var smsRegion = graphql.NewEnum(graphql.EnumConfig{
37+
Name: "UsageSMSRegion",
38+
Values: graphql.EnumValueConfigMap{
39+
"NONE": &graphql.EnumValueConfig{
40+
Value: model.SMSRegionNone,
41+
},
42+
"NORTH_AMERICA": &graphql.EnumValueConfig{
43+
Value: model.SMSRegionNorthAmerica,
44+
},
45+
"OTHER_REGIONS": &graphql.EnumValueConfig{
46+
Value: model.SMSRegionOtherRegions,
47+
},
48+
},
49+
})
50+
51+
var whatsappRegion = graphql.NewEnum(graphql.EnumConfig{
52+
Name: "UsageWhatsappRegion",
53+
Values: graphql.EnumValueConfigMap{
54+
"NONE": &graphql.EnumValueConfig{
55+
Value: model.WhatsappRegionNone,
56+
},
57+
"NORTH_AMERICA": &graphql.EnumValueConfig{
58+
Value: model.WhatsappRegionNorthAmerica,
59+
},
60+
"OTHER_REGIONS": &graphql.EnumValueConfig{
61+
Value: model.WhatsappRegionOtherRegions,
62+
},
63+
},
64+
})
65+
66+
var usageItem = graphql.NewObject(graphql.ObjectConfig{
67+
Name: "UsageItem",
68+
Fields: graphql.Fields{
69+
"usageType": &graphql.Field{
70+
Type: graphql.NewNonNull(usageType),
71+
},
72+
"smsRegion": &graphql.Field{
73+
Type: graphql.NewNonNull(smsRegion),
74+
},
75+
"whatsappRegion": &graphql.Field{
76+
Type: graphql.NewNonNull(whatsappRegion),
77+
},
78+
"quantity": &graphql.Field{
79+
Type: graphql.NewNonNull(graphql.Int),
80+
},
81+
},
82+
})

pkg/portal/model/usage.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package model
2+
3+
type Usage struct {
4+
Items []UsageItem `json:"items"`
5+
}
6+
7+
type UsageItem struct {
8+
UsageType UsageType `json:"usageType"`
9+
SMSRegion SMSRegion `json:"smsRegion"`
10+
WhatsappRegion WhatsappRegion `json:"whatsappRegion"`
11+
Quantity int `json:"quantity"`
12+
}

pkg/portal/service/deps.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ var DependencySet = wire.NewSet(
4646
wire.Struct(new(CollaboratorService), "*"),
4747
wire.Struct(new(SystemConfigProvider), "*"),
4848
wire.Struct(new(SubscriptionService), "*"),
49+
wire.Struct(new(UsageService), "*"),
4950
wire.Struct(new(NFTService), "*"),
5051
wire.Struct(new(AuditService), "*"),
5152
wire.Struct(new(OnboardService), "*"),

pkg/portal/service/subscription.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type SubscriptionPlanStore interface {
3636
GetPlan(ctx context.Context, name string) (*model.Plan, error)
3737
}
3838

39-
type UsageStore interface {
39+
type SubscriptionUsageStore interface {
4040
FetchUploadedUsageRecords(
4141
ctx context.Context,
4242
appID string,
@@ -60,7 +60,7 @@ type SubscriptionService struct {
6060
GlobalDatabase *globaldb.Handle
6161
ConfigSourceStore SubscriptionConfigSourceStore
6262
PlanStore SubscriptionPlanStore
63-
UsageStore UsageStore
63+
UsageStore SubscriptionUsageStore
6464
Clock clock.Clock
6565
AppConfig *portalconfig.AppConfig
6666
}
@@ -673,14 +673,6 @@ func (s *SubscriptionService) getSubscriptionUsage(
673673
return incompleteSubscriptionUsage, nil
674674
}
675675

676-
func sumUsageRecord(records []*usage.UsageRecord) int {
677-
sum := 0
678-
for _, record := range records {
679-
sum += record.Count
680-
}
681-
return sum
682-
}
683-
684676
func findPlan(planName string, subscriptionPlans []*model.SubscriptionPlan) (*model.SubscriptionPlan, bool) {
685677
// The first step is to find the plan.
686678
var targetPlan *model.SubscriptionPlan

0 commit comments

Comments
 (0)