-
Notifications
You must be signed in to change notification settings - Fork 1
/
generate_rate_limits_config.go
127 lines (109 loc) · 3.47 KB
/
generate_rate_limits_config.go
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
package genratelimit
import (
"bytes"
"fmt"
"sort"
ratelimit "github.com/SafetyCulture/protoc-gen-ratelimit/s12/protobuf/ratelimit"
gendoc "github.com/pseudomuto/protoc-gen-doc"
"gopkg.in/yaml.v3"
)
// YamlRateLimit is the rate limit being applied to a descriptor
type YamlRateLimit struct {
RequestsPerUnit uint32 `yaml:"requests_per_unit,omitempty"`
Unit string `yaml:"unit,omitempty"`
Unlimited bool `yaml:"unlimited,omitempty"`
}
// YamlDescriptor is the description of a rate limiting tuple
type YamlDescriptor struct {
Key string
Value string `yaml:"value,omitempty"`
RateLimit *YamlRateLimit `yaml:"rate_limit,omitempty"`
Descriptors []*YamlDescriptor `yaml:"descriptors,omitempty"`
}
// YamlRoot is the root of the YAML document
type YamlRoot struct {
Domain string
Descriptors []*YamlDescriptor
}
// GenerateRateLimitsConfig generates a YAML file containing the rate limits
func GenerateRateLimitsConfig(template *gendoc.Template, cfg Config) ([]byte, error) {
descriptors := cfg.Descriptors
descriptorCount := len(descriptors)
// Add the default limits in
limitsMap := map[string]*Limit{}
for _, def := range cfg.DefaultLimits {
key, err := formatKey(def.Key, "", descriptorCount)
if err != nil {
return nil, err
}
limitsMap[key] = &Limit{
Key: key,
Value: def.Value,
}
}
for _, file := range template.Files {
for _, service := range file.Services {
if opts, ok := service.Option("s12.protobuf.ratelimit.api_limit").(*ratelimit.ServiceOptionsRateLimits); ok {
if opts.Limits != nil && opts.Bucket != "" {
return nil, fmt.Errorf("%s %s cannot use bucket and limits together", file.Name, service.FullName)
}
if opts.Limits != nil {
for key, value := range opts.Limits {
limitKey, err := formatKey(key, service.FullName, descriptorCount)
if err != nil {
return nil, err
}
limitsMap[limitKey] = &Limit{
limitKey,
&YamlRateLimit{
uint32(value.RequestsPerUnit),
value.Unit,
value.Unlimited,
},
}
}
}
}
for _, method := range service.Methods {
if opts, ok := method.Option("s12.protobuf.ratelimit.limit").(*ratelimit.MethodOptionsRateLimits); ok {
if opts.Limits != nil && opts.Bucket != "" {
return nil, fmt.Errorf("%s %s %s cannot use bucket and limits together", file.Name, service.FullName, method.Name)
}
if opts.Limits != nil {
for key, value := range opts.Limits {
limitKey, err := formatKey(key, getDefaultMethodPath(service, method), descriptorCount)
if err != nil {
return nil, err
}
limitsMap[limitKey] = &Limit{
limitKey,
&YamlRateLimit{
uint32(value.RequestsPerUnit),
value.Unit,
value.Unlimited,
},
}
}
}
}
}
}
}
// Sort the limits so that the output is deterministic
// and empty/default values are last and not immediately matched
limitsArr := make(limits, 0, len(limitsMap))
for _, l := range limitsMap {
limitsArr = append(limitsArr, l)
}
sort.Sort(limitsArr)
root := YamlRoot{
Domain: cfg.Domain,
Descriptors: limitsArr.Descriptors(cfg.Descriptors),
}
var buf bytes.Buffer
buf.Write([]byte("---\n# This file is generated by protoc-gen-ratelimit. DO NOT EDIT.\n"))
yamlEncoder := yaml.NewEncoder(&buf)
yamlEncoder.SetIndent(2) // this is what you're looking for
err := yamlEncoder.Encode(&root)
return buf.Bytes(), err
}