Skip to content

Commit a8f7221

Browse files
Merge pull request #5 from AntoineAugusti/useless-pointers-and-documentation
Do not pass pointers where not needed + documentation
2 parents 637a13c + 14baeeb commit a8f7221

File tree

11 files changed

+89
-69
lines changed

11 files changed

+89
-69
lines changed

db/bucket.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import (
66
"github.com/boltdb/bolt"
77
)
88

9+
// Get the name of the bucket
910
func GetBucketName() string {
1011
return "features"
1112
}
1213

14+
// Generate the default bucket if it does not exist yet
1315
func GenerateDefaultBucket(name string, db *bolt.DB) {
1416
_ = db.Update(func(tx *bolt.Tx) error {
1517
_, err := tx.CreateBucketIfNotExists([]byte(name))

helpers/helpers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"strconv"
55
)
66

7+
// Transform a uint32 to a byte slice
78
func Uint32ToBytes(u uint32) []byte {
89
return []byte(strconv.FormatUint(uint64(u), 10))
910
}
1011

12+
// Check if an int is in a slice
1113
func IntInSlice(a uint32, list []uint32) bool {
1214
for _, b := range list {
1315
if b == a {
@@ -17,6 +19,7 @@ func IntInSlice(a uint32, list []uint32) bool {
1719
return false
1820
}
1921

22+
// Check if a string is in a slice
2023
func StringInSlice(a string, list []string) bool {
2124
for _, b := range list {
2225
if b == a {

http/handlers.go

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,35 @@ package http
22

33
import (
44
"encoding/json"
5-
"fmt"
65
"net/http"
76

87
m "github.com/antoineaugusti/feature-flags/models"
98
services "github.com/antoineaugusti/feature-flags/services"
109
"github.com/gorilla/mux"
1110
)
1211

12+
// Handles incoming requests
1313
type APIHandler struct {
1414
FeatureService services.FeatureService
1515
}
1616

17+
// A simple structure to respond with error messages
1718
type APIMessage struct {
18-
code int
19-
Status string `json:"status"`
19+
// The HTTP status code
20+
code int
21+
// A status message
22+
Status string `json:"status"`
23+
// A human readable message
2024
Message string `json:"message"`
2125
}
2226

27+
// Describes the request when checking the access to a feature
2328
type AccessRequest struct {
2429
Groups []string `json:"groups"`
2530
User uint32 `json:"user"`
2631
}
2732

28-
func (handler *APIHandler) Welcome(w http.ResponseWriter, r *http.Request) {
29-
fmt.Fprint(w, "Hello World!\n")
30-
}
31-
32-
func (handler *APIHandler) FeatureIndex(w http.ResponseWriter, r *http.Request) {
33+
func (handler APIHandler) FeatureIndex(w http.ResponseWriter, r *http.Request) {
3334
features, err := handler.FeatureService.GetFeatures()
3435
if err != nil {
3536
panic(err)
@@ -42,11 +43,11 @@ func (handler *APIHandler) FeatureIndex(w http.ResponseWriter, r *http.Request)
4243
}
4344
}
4445

45-
func (handler *APIHandler) FeatureShow(w http.ResponseWriter, r *http.Request) {
46+
func (handler APIHandler) FeatureShow(w http.ResponseWriter, r *http.Request) {
4647
vars := mux.Vars(r)
4748

4849
// Check if the feature exists
49-
if !handler.FeatureExists(vars["featureKey"]) {
50+
if !handler.featureExists(vars["featureKey"]) {
5051
writeNotFound(w)
5152
return
5253
}
@@ -64,12 +65,12 @@ func (handler *APIHandler) FeatureShow(w http.ResponseWriter, r *http.Request) {
6465
}
6566
}
6667

67-
func (handler *APIHandler) FeatureAccess(w http.ResponseWriter, r *http.Request) {
68+
func (handler APIHandler) FeatureAccess(w http.ResponseWriter, r *http.Request) {
6869
var ar AccessRequest
6970
vars := mux.Vars(r)
7071

7172
// Check if the feature exists
72-
if !handler.FeatureExists(vars["featureKey"]) {
73+
if !handler.featureExists(vars["featureKey"]) {
7374
writeNotFound(w)
7475
return
7576
}
@@ -109,11 +110,11 @@ func (handler *APIHandler) FeatureAccess(w http.ResponseWriter, r *http.Request)
109110
}
110111
}
111112

112-
func (handler *APIHandler) FeatureRemove(w http.ResponseWriter, r *http.Request) {
113+
func (handler APIHandler) FeatureRemove(w http.ResponseWriter, r *http.Request) {
113114
vars := mux.Vars(r)
114115

115116
// Check if the feature exists
116-
if !handler.FeatureExists(vars["featureKey"]) {
117+
if !handler.featureExists(vars["featureKey"]) {
117118
writeNotFound(w)
118119
return
119120
}
@@ -127,7 +128,7 @@ func (handler *APIHandler) FeatureRemove(w http.ResponseWriter, r *http.Request)
127128
writeMessage(http.StatusOK, "feature_deleted", "The feature was successfully deleted", w)
128129
}
129130

130-
func (handler *APIHandler) FeatureCreate(w http.ResponseWriter, r *http.Request) {
131+
func (handler APIHandler) FeatureCreate(w http.ResponseWriter, r *http.Request) {
131132
var feature m.FeatureFlag
132133

133134
if err := json.NewDecoder(r.Body).Decode(&feature); err != nil {
@@ -153,11 +154,11 @@ func (handler *APIHandler) FeatureCreate(w http.ResponseWriter, r *http.Request)
153154
}
154155
}
155156

156-
func (handler *APIHandler) FeatureEdit(w http.ResponseWriter, r *http.Request) {
157+
func (handler APIHandler) FeatureEdit(w http.ResponseWriter, r *http.Request) {
157158
vars := mux.Vars(r)
158159

159160
// Check if the feature exists
160-
if !handler.FeatureExists(vars["featureKey"]) {
161+
if !handler.featureExists(vars["featureKey"]) {
161162
writeNotFound(w)
162163
return
163164
}
@@ -192,7 +193,7 @@ func (handler *APIHandler) FeatureEdit(w http.ResponseWriter, r *http.Request) {
192193
}
193194
}
194195

195-
func (handler *APIHandler) FeatureExists(featureKey string) bool {
196+
func (handler APIHandler) featureExists(featureKey string) bool {
196197
return handler.FeatureService.FeatureExists(featureKey)
197198
}
198199

http/handlers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var (
2828

2929
func onStart() {
3030
database = getTestDB()
31-
server = httptest.NewServer(NewRouter(&APIHandler{FeatureService: s.FeatureService{DB: database}}))
31+
server = httptest.NewServer(NewRouter(APIHandler{FeatureService: s.FeatureService{DB: database}}))
3232
base = fmt.Sprintf("%s/features", server.URL)
3333
}
3434

http/logger.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88
)
99

10+
// Serve and log an incoming request
1011
func Logger(inner http.Handler, name string) http.Handler {
1112
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1213
start := time.Now()
@@ -24,6 +25,7 @@ func Logger(inner http.Handler, name string) http.Handler {
2425
})
2526
}
2627

28+
// Extract the IP address from a request
2729
func getIPAddress(r *http.Request) string {
2830
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
2931

http/router.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import (
66
"github.com/gorilla/mux"
77
)
88

9-
func NewRouter(api *APIHandler) *mux.Router {
10-
9+
// Bind routes to handlers and create a router
10+
func NewRouter(api APIHandler) *mux.Router {
1111
router := mux.NewRouter().StrictSlash(true)
12+
1213
for _, route := range getRoutes(api) {
1314
var handler http.Handler
1415

http/routes.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,20 @@ package http
33
import "net/http"
44

55
type Route struct {
6-
Name string
7-
Method string
8-
Pattern string
6+
// A human readable name for the route
7+
Name string
8+
// The HTTP method
9+
Method string
10+
// The URL
11+
Pattern string
12+
// The handler for this endpoint
913
HandlerFunc http.HandlerFunc
1014
}
1115

1216
type Routes []Route
1317

14-
func getRoutes(api *APIHandler) Routes {
18+
func getRoutes(api APIHandler) Routes {
1519
return Routes{
16-
Route{
17-
"Welcome",
18-
"GET",
19-
"/",
20-
api.Welcome,
21-
},
2220
Route{
2321
"FeatureIndex",
2422
"GET",

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ func main() {
3232
api := h.APIHandler{FeatureService: s.FeatureService{DB: database}}
3333

3434
// Create and listen for the HTTP server
35-
router := h.NewRouter(&api)
35+
router := h.NewRouter(api)
3636
log.Fatal(http.ListenAndServe(*address, router))
3737
}

models/feature.go

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,26 @@ import (
88
helpers "github.com/antoineaugusti/feature-flags/helpers"
99
)
1010

11+
// Represents a feature flag
1112
type FeatureFlag struct {
12-
Key string `json:"key"`
13-
Enabled bool `json:"enabled"`
14-
Users []uint32 `json:"users"`
15-
Groups []string `json:"groups"`
16-
Percentage uint32 `json:"percentage"`
13+
// The key of a feature flag
14+
Key string `json:"key"`
15+
// Tell if a feature flag is enabled. If set to false,
16+
// the feature flag can still be partially enabled thanks to
17+
// the Users, Groups and Percentage properties
18+
Enabled bool `json:"enabled"`
19+
// Gives access to a feature to specific user IDs
20+
Users []uint32 `json:"users"`
21+
// Gives access to a feature to specific groups
22+
Groups []string `json:"groups"`
23+
// Gives access to a feature to a percentage of users
24+
Percentage uint32 `json:"percentage"`
1725
}
1826

1927
type FeatureFlags []FeatureFlag
2028

21-
func (f *FeatureFlag) Validate() error {
29+
// Self validate the properties of a feature flag
30+
func (f FeatureFlag) Validate() error {
2231
// Validate percentage
2332
if f.Percentage < 0 || f.Percentage > 100 {
2433
return fmt.Errorf("Percentage must be between 0 and 100")
@@ -35,46 +44,56 @@ func (f *FeatureFlag) Validate() error {
3544
return nil
3645
}
3746

38-
func (f *FeatureFlag) IsEnabled() bool {
47+
// Check if a feature flag is enabled
48+
func (f FeatureFlag) IsEnabled() bool {
3949
return f.Enabled || f.Percentage == 100
4050
}
4151

42-
func (f *FeatureFlag) IsPartiallyEnabled() bool {
52+
// Check if a feature flag is partially enabled
53+
func (f FeatureFlag) IsPartiallyEnabled() bool {
4354
return !f.IsEnabled() && (f.hasUsers() || f.hasGroups() || f.hasPercentage())
4455
}
4556

46-
func (f *FeatureFlag) GroupHasAccess(group string) bool {
57+
// Check if a group has access to a feature
58+
func (f FeatureFlag) GroupHasAccess(group string) bool {
4759
return f.IsEnabled() || (f.IsPartiallyEnabled() && f.groupInGroups(group))
4860
}
4961

50-
func (f *FeatureFlag) UserHasAccess(user uint32) bool {
62+
// Check if a user has access to a feature
63+
func (f FeatureFlag) UserHasAccess(user uint32) bool {
5164
// A user has access:
5265
// - if the feature is enabled
5366
// - if the feature is partially enabled and he has been given access explicity
5467
// - if the feature is partially enabled and he is in the allowed percentage
5568
return f.IsEnabled() || (f.IsPartiallyEnabled() && (f.userInUsers(user) || f.userIsAllowedByPercentage(user)))
5669
}
5770

58-
func (f *FeatureFlag) hasUsers() bool {
71+
// Tell if specific users have access to the feature
72+
func (f FeatureFlag) hasUsers() bool {
5973
return len(f.Users) > 0
6074
}
6175

62-
func (f *FeatureFlag) hasGroups() bool {
76+
// Tell if specific groups have access to the feature
77+
func (f FeatureFlag) hasGroups() bool {
6378
return len(f.Groups) > 0
6479
}
6580

66-
func (f *FeatureFlag) hasPercentage() bool {
81+
// Tell if a specific percentage of users has access to the feature
82+
func (f FeatureFlag) hasPercentage() bool {
6783
return f.Percentage > 0
6884
}
6985

70-
func (f *FeatureFlag) userIsAllowedByPercentage(user uint32) bool {
86+
// Check if a user has access to the feature thanks to the percentage value
87+
func (f FeatureFlag) userIsAllowedByPercentage(user uint32) bool {
7188
return crc32.ChecksumIEEE(helpers.Uint32ToBytes(user))%100 < f.Percentage
7289
}
7390

74-
func (f *FeatureFlag) userInUsers(user uint32) bool {
91+
// Check if a user is in the list of allowed users
92+
func (f FeatureFlag) userInUsers(user uint32) bool {
7593
return helpers.IntInSlice(user, f.Users)
7694
}
7795

78-
func (f *FeatureFlag) groupInGroups(group string) bool {
96+
// Check if a group is in the list of allowed groups
97+
func (f FeatureFlag) groupInGroups(group string) bool {
7998
return helpers.StringInSlice(group, f.Groups)
8099
}

repos/feature.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/boltdb/bolt"
1010
)
1111

12+
// Update a feature flag
1213
func PutFeature(tx *bolt.Tx, feature m.FeatureFlag) error {
1314
features := tx.Bucket([]byte(db.GetBucketName()))
1415

@@ -25,6 +26,7 @@ func PutFeature(tx *bolt.Tx, feature m.FeatureFlag) error {
2526
return nil
2627
}
2728

29+
// Get a list of feature flags
2830
func GetFeatures(tx *bolt.Tx) (m.FeatureFlags, error) {
2931
featuresBucket := tx.Bucket([]byte(db.GetBucketName()))
3032
cursor := featuresBucket.Cursor()
@@ -44,12 +46,14 @@ func GetFeatures(tx *bolt.Tx) (m.FeatureFlags, error) {
4446
return features, nil
4547
}
4648

49+
// Tell if a feature exists
4750
func FeatureExists(tx *bolt.Tx, featureKey string) bool {
4851
features := tx.Bucket([]byte(db.GetBucketName()))
4952
bytes := features.Get([]byte(featureKey))
5053
return bytes != nil
5154
}
5255

56+
// Get a feature flag thanks to its key
5357
func GetFeature(tx *bolt.Tx, featureKey string) (m.FeatureFlag, error) {
5458
features := tx.Bucket([]byte(db.GetBucketName()))
5559

@@ -68,6 +72,7 @@ func GetFeature(tx *bolt.Tx, featureKey string) (m.FeatureFlag, error) {
6872
return feature, nil
6973
}
7074

75+
// Delete a feature flag thanks to its key
7176
func RemoveFeature(tx *bolt.Tx, featureKey string) error {
7277
features := tx.Bucket([]byte(db.GetBucketName()))
7378
return features.Delete([]byte(featureKey))

0 commit comments

Comments
 (0)