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

mendersoftware / mender-server / 1927964654

15 Jul 2025 01:18PM UTC coverage: 65.511% (+0.06%) from 65.454%
1927964654

Pull #783

gitlab-ci

bahaa-ghazal
feat: Integrate requestsize middleware into useradm

Title: None
Signed-off-by: Bahaa Aldeen Ghazal <bahaa.ghazal@northern.tech>
Pull Request #783: feat: Implemented gin request body size limiter middleware

131 of 139 new or added lines in 17 files covered. (94.24%)

6 existing lines in 1 file now uncovered.

32173 of 49111 relevant lines covered (65.51%)

1.4 hits per line

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

91.09
/backend/services/iot-manager/api/http/router.go
1
// Copyright 2025 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package http
16

17
import (
18
        "context"
19
        "net/http"
20
        "time"
21

22
        "github.com/gin-gonic/gin"
23
        "github.com/gin-gonic/gin/binding"
24
        "github.com/pkg/errors"
25

26
        "github.com/mendersoftware/mender-server/pkg/identity"
27
        "github.com/mendersoftware/mender-server/pkg/log"
28
        "github.com/mendersoftware/mender-server/pkg/requestid"
29
        "github.com/mendersoftware/mender-server/pkg/requestsize"
30
        "github.com/mendersoftware/mender-server/pkg/rest.utils"
31
        "github.com/mendersoftware/mender-server/pkg/routing"
32

33
        "github.com/mendersoftware/mender-server/services/iot-manager/app"
34
        dconfig "github.com/mendersoftware/mender-server/services/iot-manager/config"
35
)
36

37
// API URL used by the HTTP router
38
const (
39
        APIURLInternal = "/api/internal/v1/iot-manager"
40

41
        APIURLAlive             = "/alive"
42
        APIURLHealth            = "/health"
43
        APIURLTenants           = "/tenants"
44
        APIURLTenant            = APIURLTenants + "/:tenant_id"
45
        APIURLTenantAuth        = APIURLTenant + "/auth"
46
        APIURLTenantDevices     = APIURLTenant + "/devices"
47
        APIURLTenantDevice      = APIURLTenantDevices + "/:device_id"
48
        APIURLTenantBulkDevices = APIURLTenant + "/bulk/devices"
49
        APIURLTenantBulkStatus  = APIURLTenantBulkDevices + "/status/:status"
50

51
        APIURLManagement = "/api/management/v1/iot-manager"
52

53
        APIURLIntegrations           = "/integrations"
54
        APIURLIntegration            = "/integrations/:id"
55
        APIURLIntegrationCredentials = APIURLIntegration + "/credentials"
56

57
        APIURLDevice                 = "/devices/:id"
58
        APIURLDeviceState            = APIURLDevice + "/state"
59
        APIURLDeviceStateIntegration = APIURLDevice + "/state/:integrationId"
60

61
        APIURLEvents = "/events"
62
)
63

64
const (
65
        defaultTimeout = time.Second * 10
66
)
67

68
type Config struct {
69
        Client         *http.Client
70
        MaxRequestSize int64
71
}
72

73
// NewConfig initializes a new empty config and optionally merges the
74
// configurations provided as argument.
75
func NewConfig(configs ...*Config) *Config {
3✔
76
        var config = new(Config)
3✔
77
        config.MaxRequestSize = dconfig.SettingMaxRequestSizeDefault
3✔
78
        for _, conf := range configs {
6✔
79
                if conf == nil {
4✔
80
                        continue
1✔
81
                }
82
                if conf.Client != nil {
5✔
83
                        config.Client = conf.Client
2✔
84
                }
2✔
85
                if conf.MaxRequestSize > 0 {
6✔
86
                        config.MaxRequestSize = conf.MaxRequestSize
3✔
87
                }
3✔
88
        }
89
        return config
3✔
90
}
91

92
func (conf *Config) SetClient(client *http.Client) *Config {
2✔
93
        conf.Client = client
2✔
94
        return conf
2✔
95
}
2✔
96
func (conf *Config) SetMaxRequestSize(size int64) *Config {
2✔
97
        conf.MaxRequestSize = size
2✔
98
        return conf
2✔
99
}
2✔
100

101
// NewRouter returns the gin router
102
func NewRouter(
103
        app app.App,
104
        config ...*Config,
105
) *gin.Engine {
3✔
106
        conf := NewConfig(config...)
3✔
107
        handler := NewAPIHandler(app, conf)
3✔
108
        internal := (*InternalHandler)(handler)
3✔
109
        management := (*ManagementHandler)(handler)
3✔
110

3✔
111
        router := routing.NewGinRouter()
3✔
112

3✔
113
        router.Use(requestsize.Middleware(conf.MaxRequestSize))
3✔
114

3✔
115
        internalAPI := router.Group(APIURLInternal)
3✔
116
        internalAPI.GET(APIURLAlive, handler.Alive)
3✔
117
        internalAPI.GET(APIURLHealth, handler.Health)
3✔
118

3✔
119
        internalAPI.DELETE(APIURLTenant, internal.DeleteTenant)
3✔
120
        internalAPI.POST(APIURLTenantDevices, internal.ProvisionDevice)
3✔
121
        internalAPI.DELETE(APIURLTenantDevice, internal.DecommissionDevice)
3✔
122
        internalAPI.PUT(APIURLTenantBulkStatus, internal.BulkSetDeviceStatus)
3✔
123

3✔
124
        internalAPI.POST(APIURLTenantAuth, internal.PreauthorizeHandler)
3✔
125

3✔
126
        managementAPI := router.Group(APIURLManagement, identity.Middleware())
3✔
127
        managementAPI.GET(APIURLIntegrations, management.GetIntegrations)
3✔
128
        managementAPI.GET(APIURLIntegration, management.GetIntegrationById)
3✔
129
        managementAPI.POST(APIURLIntegrations, management.CreateIntegration)
3✔
130
        managementAPI.PUT(APIURLIntegrationCredentials, management.SetIntegrationCredentials)
3✔
131
        managementAPI.DELETE(APIURLIntegration, management.RemoveIntegration)
3✔
132

3✔
133
        managementAPI.GET(APIURLDeviceState, management.GetDeviceState)
3✔
134
        managementAPI.GET(APIURLDeviceStateIntegration, management.GetDeviceStateIntegration)
3✔
135
        managementAPI.PUT(APIURLDeviceStateIntegration, management.SetDeviceStateIntegration)
3✔
136

3✔
137
        managementAPI.GET(APIURLEvents, management.GetEvents)
3✔
138

3✔
139
        return router
3✔
140
}
3✔
141

142
type APIHandler struct {
143
        *http.Client
144
        app app.App
145
}
146

147
func NewAPIHandler(app app.App, config ...*Config) *APIHandler {
3✔
148
        conf := NewConfig(config...)
3✔
149
        if conf.Client == nil {
4✔
150
                conf.Client = new(http.Client)
1✔
151
        }
1✔
152
        return &APIHandler{
3✔
153
                Client: conf.Client,
3✔
154
                app:    app,
3✔
155
        }
3✔
156
}
157

158
// Alive responds to GET /alive
159
func (h *APIHandler) Alive(c *gin.Context) {
2✔
160
        c.Writer.WriteHeader(http.StatusNoContent)
2✔
161
}
2✔
162

163
// Health responds to GET /health
164
func (h *APIHandler) Health(c *gin.Context) {
2✔
165
        ctx := c.Request.Context()
2✔
166
        l := log.FromContext(ctx)
2✔
167
        ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
2✔
168
        defer cancel()
2✔
169

2✔
170
        err := h.app.HealthCheck(ctx)
2✔
171
        if err != nil {
3✔
172
                l.Error(errors.Wrap(err, "health check failed"))
1✔
173
                c.JSON(http.StatusServiceUnavailable, gin.H{
1✔
174
                        "error": err.Error(),
1✔
175
                })
1✔
176
                return
1✔
177
        }
1✔
178

179
        c.Writer.WriteHeader(http.StatusNoContent)
2✔
180
}
181

UNCOV
182
func (h *APIHandler) NoRoute(c *gin.Context) {
×
UNCOV
183
        c.JSON(http.StatusNotFound, rest.Error{
×
UNCOV
184
                Err:       "not found",
×
UNCOV
185
                RequestID: requestid.FromContext(c.Request.Context()),
×
UNCOV
186
        })
×
UNCOV
187
}
×
188

189
// Make gin-gonic use validatable structs instead of relying on go-playground
190
// validator interface.
191
type validateValidatableValidator struct{}
192

193
func (validateValidatableValidator) ValidateStruct(obj interface{}) error {
3✔
194
        if v, ok := obj.(interface{ Validate() error }); ok {
6✔
195
                return v.Validate()
3✔
196
        }
3✔
197
        return nil
3✔
198
}
199

200
func (validateValidatableValidator) Engine() interface{} {
×
201
        return nil
×
202
}
×
203

204
func init() {
3✔
205
        binding.Validator = validateValidatableValidator{}
3✔
206
}
3✔
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