• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

michaelcoll / quiz-app / 6430878385

06 Oct 2023 11:10AM UTC coverage: 21.344%. First build
6430878385

Pull #262

github

michaelcoll
feat(health): disable log for health probe urls

Closes #261
Pull Request #262: feat(health): disable log for health probe urls

5 of 5 new or added lines in 1 file covered. (100.0%)

413 of 1935 relevant lines covered (21.34%)

1.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

21.74
/internal/back/presentation/baseApiController.go
1
/*
2
 * Copyright (c) 2022-2023 Michaël COLL.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package presentation
18

19
import (
20
        "fmt"
21
        "log"
22
        "net/http"
23
        "regexp"
24
        "strconv"
25

26
        "github.com/fatih/color"
27
        "github.com/gin-gonic/gin"
28

29
        "github.com/michaelcoll/quiz-app/internal/back/domain"
30
)
31

32
const apiPort = ":8080"
33

34
var rangeRxp = regexp.MustCompile(`(?P<Unit>.*)=(?P<Start>[0-9]+)-(?P<End>[0-9]*)`)
35

36
type ApiController struct {
37
        authService   *domain.AuthService
38
        classService  *domain.ClassService
39
        quizService   *domain.QuizService
40
        userService   *domain.UserService
41
        healthService *domain.HealthService
42
}
43

44
func NewApiController(
45
        authService *domain.AuthService,
46
        classService *domain.ClassService,
47
        quizService *domain.QuizService,
48
        userService *domain.UserService,
49
        healthService *domain.HealthService) ApiController {
×
50
        return ApiController{authService: authService, classService: classService,
×
51
                quizService: quizService, userService: userService, healthService: healthService}
×
52
}
×
53

54
var pathRoleMapping = map[*endPointDef]domain.Role{}
55

56
func (c *ApiController) Serve() {
×
57

×
58
        gin.SetMode(gin.ReleaseMode)
×
59

×
60
        router := gin.New()
×
61
        router.Use(
×
62
                gin.LoggerWithWriter(gin.DefaultWriter, "/health/started", "/health/ready", "/health/live"),
×
63
                gin.Recovery(),
×
64
        )
×
65
        router.Use(injectTokenIfPresent)
×
66

×
67
        addCommonMiddlewares(router)
×
68

×
69
        public := router.Group("/api/v1")
×
70
        private := router.Group("/api/v1")
×
71
        health := router.Group("/health")
×
72

×
73
        private.Use(validateAuthHeaderAndGetUser(c.authService))
×
74
        private.Use(enforceRoles)
×
75

×
76
        addPostEndpoint(public, "/login", domain.NoRole, c.login)
×
77

×
78
        addGetEndpoint(health, "/started", domain.NoRole, c.started)
×
79
        addGetEndpoint(health, "/ready", domain.NoRole, c.ready)
×
80
        addGetEndpoint(health, "/live", domain.NoRole, c.live)
×
81

×
82
        addGetEndpoint(private, "/quiz", domain.Student, c.quizList)
×
83
        addGetEndpoint(private, "/quiz/:sha1", domain.Student, c.quizBySha1)
×
84
        addPostEndpoint(private, "/quiz/:sha1/class/:uuid", domain.Teacher, c.createQuizClassVisibility)
×
85
        addDeleteEndpoint(private, "/quiz/:sha1/class/:uuid", domain.Teacher, c.deleteQuizClassVisibility)
×
86

×
87
        addGetEndpoint(private, "/user", domain.Teacher, c.userList)
×
88
        addGetEndpoint(private, "/user/me", domain.Student, c.me)
×
89
        addDeleteEndpoint(private, "/user/:id", domain.Admin, c.deactivateUser)
×
90
        addPostEndpoint(private, "/user/:id/activate", domain.Admin, c.activateUser)
×
91
        addPutEndpoint(private, "/user/:id/role/:roleName", domain.Admin, c.updateUserRole)
×
92
        addPutEndpoint(private, "/user/:id/class/:uuid", domain.Teacher, c.assignUserToClass)
×
93

×
94
        addGetEndpoint(private, "/session", domain.Student, c.sessionList)
×
95
        addPostEndpoint(private, "/session", domain.Student, c.startSession)
×
96
        addPostEndpoint(private, "/session/:uuid/answer", domain.Student, c.addSessionAnswer)
×
97

×
98
        addGetEndpoint(private, "/class", domain.Teacher, c.classList)
×
99
        addPostEndpoint(private, "/class", domain.Admin, c.classCreate)
×
100
        addPutEndpoint(private, "/class/:uuid", domain.Admin, c.classUpdate)
×
101
        addDeleteEndpoint(private, "/class/:uuid", domain.Admin, c.classDelete)
×
102

×
103
        addGetEndpoint(private, "/quiz-session", domain.Student, c.quizSessionList)
×
104
        addGetEndpoint(private, "/quiz-session/:uuid", domain.Student, c.quizSessionByUuid)
×
105

×
106
        // Listen and serve on 0.0.0.0:8080
×
107
        fmt.Printf("%s Listening API on http://0.0.0.0%s\n", color.GreenString("✓"), color.GreenString(apiPort))
×
108
        err := router.Run(apiPort)
×
109
        if err != nil {
×
110
                log.Fatalf("Error starting server : %v", err)
×
111
        }
×
112
}
113

114
func addGetEndpoint(routerGroup *gin.RouterGroup, path string, role domain.Role, handler gin.HandlerFunc) {
1✔
115
        appendEndpointDef(routerGroup, path, "GET", role)
1✔
116
        routerGroup.GET(path, handler)
1✔
117
}
1✔
118

119
func addPostEndpoint(routerGroup *gin.RouterGroup, path string, role domain.Role, handler gin.HandlerFunc) {
1✔
120
        appendEndpointDef(routerGroup, path, "POST", role)
1✔
121
        routerGroup.POST(path, handler)
1✔
122
}
1✔
123

124
func addPutEndpoint(routerGroup *gin.RouterGroup, path string, role domain.Role, handler gin.HandlerFunc) {
×
125
        appendEndpointDef(routerGroup, path, "PUT", role)
×
126
        routerGroup.PUT(path, handler)
×
127
}
×
128

129
func addDeleteEndpoint(routerGroup *gin.RouterGroup, path string, role domain.Role, handler gin.HandlerFunc) {
1✔
130
        appendEndpointDef(routerGroup, path, "DELETE", role)
1✔
131
        routerGroup.DELETE(path, handler)
1✔
132
}
1✔
133

134
func appendEndpointDef(routerGroup *gin.RouterGroup, path string, method string, role domain.Role) {
3✔
135
        if role != domain.NoRole {
6✔
136
                pathRoleMapping[&endPointDef{
3✔
137
                        regex:  toRegExPath(routerGroup, path),
3✔
138
                        method: method,
3✔
139
                }] = role
3✔
140
        }
3✔
141
}
142

143
func toRegExPath(routerGroup *gin.RouterGroup, path string) *regexp.Regexp {
4✔
144
        r := regexp.MustCompile(`:[0-9a-zA-Z]+`)
4✔
145
        replacedPath := r.ReplaceAllString(path, "[^/]+")
4✔
146

4✔
147
        return regexp.MustCompile(fmt.Sprintf("^%s%s$", routerGroup.BasePath(), replacedPath))
4✔
148
}
4✔
149

150
func extractRangeHeader(rangeHeader string, unit string) (uint16, uint16, error) {
×
151
        r := rangeRxp.FindStringSubmatch(rangeHeader)
×
152
        st := http.StatusRequestedRangeNotSatisfiable
×
153

×
154
        if len(r) < 4 {
×
155
                return 0, 0, Errorf(st, "Range is not valid, supported format : %s=0-25", unit)
×
156
        }
×
157

158
        if r[1] != unit {
×
159
                return 0, 0, Errorf(st, "Unit in range is not valid, supported unit : %s", unit)
×
160
        }
×
161

162
        start, errStart := strconv.ParseUint(r[2], 10, 16)
×
163
        end, errEnd := strconv.ParseUint(r[3], 10, 16)
×
164

×
165
        if len(r[3]) == 0 {
×
166
                end = 0
×
167
        }
×
168

169
        if errStart != nil {
×
170
                return 0, 0, Errorf(st, "Start range is not valid")
×
171
        }
×
172

173
        if len(r[3]) != 0 && errEnd != nil {
×
174
                return 0, 0, Errorf(st, "End range is not valid")
×
175
        }
×
176

177
        if end != 0 && start >= end {
×
178
                return 0, 0, Errorf(st, "Range is not valid, start > end")
×
179
        }
×
180

181
        return uint16(start), uint16(end), nil
×
182
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc