Passed
Push — main ( f24a32...53c867 )
by Acho
03:11
created

handlers.*PhoneAPIKeyHandler.deletePhone   A

Complexity

Conditions 4

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 17
dl 0
loc 24
rs 9.55
c 0
b 0
f 0
nop 1
1
package handlers
2
3
import (
4
	"fmt"
5
6
	"github.com/NdoleStudio/httpsms/pkg/repositories"
7
	"github.com/NdoleStudio/httpsms/pkg/requests"
8
	"github.com/NdoleStudio/httpsms/pkg/services"
9
	"github.com/NdoleStudio/httpsms/pkg/telemetry"
10
	"github.com/NdoleStudio/httpsms/pkg/validators"
11
	"github.com/davecgh/go-spew/spew"
12
	"github.com/gofiber/fiber/v2"
13
	"github.com/google/uuid"
14
	"github.com/palantir/stacktrace"
15
)
16
17
// PhoneAPIKeyHandler handles phone API key http requests
18
type PhoneAPIKeyHandler struct {
19
	handler
20
	logger    telemetry.Logger
21
	tracer    telemetry.Tracer
22
	validator *validators.PhoneAPIKeyHandlerValidator
23
	service   *services.PhoneAPIKeyService
24
}
25
26
// NewPhoneAPIKeyHandler creates a new PhoneAPIKeyHandler
27
func NewPhoneAPIKeyHandler(
28
	logger telemetry.Logger,
29
	tracer telemetry.Tracer,
30
	validator *validators.PhoneAPIKeyHandlerValidator,
31
	service *services.PhoneAPIKeyService,
32
) *PhoneAPIKeyHandler {
33
	return &PhoneAPIKeyHandler{
34
		logger:    logger.WithService(fmt.Sprintf("%T", &PhoneAPIKeyHandler{})),
35
		tracer:    tracer,
36
		validator: validator,
37
		service:   service,
38
	}
39
}
40
41
// RegisterRoutes registers the routes for the PhoneAPIKeyHandler
42
func (h *PhoneAPIKeyHandler) RegisterRoutes(app *fiber.App, middlewares ...fiber.Handler) {
43
	router := app.Group("/v1/phone-api-keys/")
44
	router.Get("/", h.computeRoute(middlewares, h.index)...)
45
	router.Post("/", h.computeRoute(middlewares, h.store)...)
46
	router.Delete("/:phoneAPIKeyID", h.computeRoute(middlewares, h.delete)...)
47
	router.Delete("/:phoneAPIKeyID/phones/:phoneID", h.computeRoute(middlewares, h.deletePhone)...)
48
}
49
50
// @Summary      Get the phone API keys of a user
51
// @Description  Get list phone API keys which a user has registered on the httpSMS application
52
// @Security	 ApiKeyAuth
53
// @Tags         PhoneAPIKeys
54
// @Accept       json
55
// @Produce      json
56
// @Param        skip		query  int  	false	"number of phone api keys to skip"					minimum(0)
57
// @Param        query		query  string  	false 	"filter phone api keys with name containing query"
58
// @Param        limit		query  int  	false	"number of phone api keys to return"					minimum(1)	maximum(100)
59
// @Success      200 		{object}	responses.PhoneAPIKeysResponse
60
// @Failure      400		{object}	responses.BadRequest
61
// @Failure 	 401    	{object}	responses.Unauthorized
62
// @Failure      422		{object}	responses.UnprocessableEntity
63
// @Failure      500		{object}	responses.InternalServerError
64
// @Router       /api-keys [get]
65
func (h *PhoneAPIKeyHandler) index(c *fiber.Ctx) error {
66
	ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger)
67
	defer span.End()
68
69
	var request requests.PhoneAPIKeyIndex
70
	if err := c.QueryParser(&request); err != nil {
71
		msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request)
72
		ctxLogger.Warn(stacktrace.Propagate(err, msg))
73
		return h.responseBadRequest(c, err)
74
	}
75
76
	if errors := h.validator.ValidateIndex(ctx, request.Sanitize()); len(errors) != 0 {
77
		msg := fmt.Sprintf("validation errors [%s], while fetching phone API keys [%+#v]", spew.Sdump(errors), request)
78
		ctxLogger.Warn(stacktrace.NewError(msg))
79
		return h.responseUnprocessableEntity(c, errors, "validation errors while fetching phone API keys")
80
	}
81
82
	apiKeys, err := h.service.Index(ctx, h.userIDFomContext(c), request.ToIndexParams())
83
	if err != nil {
84
		msg := fmt.Sprintf("cannot index phone API keys with params [%+#v]", request)
85
		ctxLogger.Error(stacktrace.Propagate(err, msg))
86
		return h.responseInternalServerError(c)
87
	}
88
89
	return h.responseOK(c, fmt.Sprintf("fetched %d phone API %s", len(apiKeys), h.pluralize("key", len(apiKeys))), apiKeys)
90
}
91
92
// @Summary      Store phone API key
93
// @Description  Creates a new phone API key which can be used to log in to the httpSMS app on your Android phone
94
// @Security	 ApiKeyAuth
95
// @Tags         PhoneAPIKeys
96
// @Accept       json
97
// @Produce      json
98
// @Param        payload   	body 		requests.PhoneAPIKeyStoreRequest 	true 	"Payload of new phone API key."
99
// @Success      200 		{object}	responses.PhoneAPIKeyResponse
100
// @Failure      400		{object}	responses.BadRequest
101
// @Failure 	 401    	{object}	responses.Unauthorized
102
// @Failure      422		{object}	responses.UnprocessableEntity
103
// @Failure      500		{object}	responses.InternalServerError
104
// @Router       /api-keys [post]
105
func (h *PhoneAPIKeyHandler) store(c *fiber.Ctx) error {
106
	ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger)
107
	defer span.End()
108
109
	var request requests.PhoneAPIKeyStoreRequest
110
	if err := c.BodyParser(&request); err != nil {
111
		msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request)
112
		ctxLogger.Warn(stacktrace.Propagate(err, msg))
113
		return h.responseBadRequest(c, err)
114
	}
115
116
	if errors := h.validator.ValidateStore(ctx, request.Sanitize()); len(errors) != 0 {
117
		msg := fmt.Sprintf("validation errors [%s], while updating phones [%+#v]", spew.Sdump(errors), request)
118
		ctxLogger.Warn(stacktrace.NewError(msg))
119
		return h.responseUnprocessableEntity(c, errors, "validation errors while updating phones")
120
	}
121
122
	phoneAPIKey, err := h.service.Create(ctx, h.userFromContext(c), request.Name)
123
	if err != nil {
124
		msg := fmt.Sprintf("cannot update phones with params [%+#v]", request)
125
		ctxLogger.Error(stacktrace.Propagate(err, msg))
126
		return h.responseInternalServerError(c)
127
	}
128
129
	return h.responseOK(c, "phone API key created successfully", phoneAPIKey)
130
}
131
132
// @Summary      Delete a phone API key from the database.
133
// @Description  Delete a phone API Key from the database and cannot be used for authentication anymore.
134
// @Security	 ApiKeyAuth
135
// @Tags         PhoneAPIKeys
136
// @Accept       json
137
// @Produce      json
138
// @Param 		 phoneAPIKeyID 	path	string 							true 	"ID of the phone API key" 	default(32343a19-da5e-4b1b-a767-3298a73703ca)
139
// @Success      204  		{object} 	responses.NoContent
140
// @Failure      400  		{object}  	responses.BadRequest
141
// @Failure 	 401    	{object}	responses.Unauthorized
142
// @Failure 	 404		{object}	responses.NotFound
143
// @Failure      422  		{object} 	responses.UnprocessableEntity
144
// @Failure      500  		{object}  	responses.InternalServerError
145
// @Router       /api-keys/{phoneAPIKeyID} [delete]
146
func (h *PhoneAPIKeyHandler) delete(c *fiber.Ctx) error {
147
	ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger)
148
	defer span.End()
149
150
	phoneAPIKeyID := c.Params("phoneAPIKeyID")
151
	if errors := h.validator.ValidateUUID(phoneAPIKeyID, "phoneAPIKeyID"); len(errors) != 0 {
152
		msg := fmt.Sprintf("validation errors [%s], while deleting a phone API key with ID [%s]", spew.Sdump(errors), phoneAPIKeyID)
153
		ctxLogger.Warn(stacktrace.NewError(msg))
154
		return h.responseUnprocessableEntity(c, errors, "validation errors while storing event")
155
	}
156
157
	err := h.service.Delete(ctx, h.userIDFomContext(c), uuid.MustParse(phoneAPIKeyID))
158
	if stacktrace.GetCode(err) == repositories.ErrCodeNotFound {
159
		return h.responseNotFound(c, fmt.Sprintf("cannot find phone API key with ID [%s]", phoneAPIKeyID))
160
	}
161
162
	if err != nil {
163
		msg := fmt.Sprintf("cannot delete phone API key with ID [%s] for user with ID [%s]", phoneAPIKeyID, h.userIDFomContext(c))
164
		ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)))
165
		return h.responseInternalServerError(c)
166
	}
167
168
	return h.responseNoContent(c, "phone API key deleted successfully")
169
}
170
171
// @Summary      Remove the association of a phone from the phone API key.
172
// @Description  You will need to login again to the httpSMS app on your Android phone with a new phone API key.
173
// @Security	 ApiKeyAuth
174
// @Tags         PhoneAPIKeys
175
// @Accept       json
176
// @Produce      json
177
// @Param 		 phoneAPIKeyID 	path		string 							true 	"ID of the phone API key" 	default(32343a19-da5e-4b1b-a767-3298a73703ca)
178
// @Param 		 phoneID 		path		string 							true 	"ID of the phone" 			default(32343a19-da5e-4b1b-a767-3298a73703ca)
179
// @Success      204  			{object} 	responses.NoContent
180
// @Failure      400  			{object}  	responses.BadRequest
181
// @Failure 	 401    		{object}	responses.Unauthorized
182
// @Failure 	 404			{object}	responses.NotFound
183
// @Failure      422  			{object} 	responses.UnprocessableEntity
184
// @Failure      500  			{object}  	responses.InternalServerError
185
// @Router       /api-keys/{phoneAPIKeyID}/phones/{phoneID} [delete]
186
func (h *PhoneAPIKeyHandler) deletePhone(c *fiber.Ctx) error {
187
	ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger)
188
	defer span.End()
189
190
	phoneAPIKeyID := c.Params("phoneAPIKeyID")
191
	phoneID := c.Params("phoneID")
192
	if errors := h.mergeErrors(h.validator.ValidateUUID(phoneAPIKeyID, "phoneAPIKeyID"), h.validator.ValidateUUID(phoneID, "phoneID")); len(errors) != 0 {
193
		msg := fmt.Sprintf("validation errors [%s], while deleting a phone API key with ID [%s]", spew.Sdump(errors), phoneAPIKeyID)
194
		ctxLogger.Warn(stacktrace.NewError(msg))
195
		return h.responseUnprocessableEntity(c, errors, "validation errors while storing event")
196
	}
197
198
	err := h.service.RemovePhone(ctx, h.userIDFomContext(c), uuid.MustParse(phoneAPIKeyID), uuid.MustParse(phoneID))
199
	if stacktrace.GetCode(err) == repositories.ErrCodeNotFound {
200
		return h.responseNotFound(c, fmt.Sprintf("cannot find phone with ID [%s] which is associated with phone API key with ID [%s]", phoneID, phoneAPIKeyID))
201
	}
202
203
	if err != nil {
204
		msg := fmt.Sprintf("cannot remove phone with ID [%s] from phone API key with ID [%s] for user with ID [%s]", phoneID, phoneAPIKeyID, h.userIDFomContext(c))
205
		ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)))
206
		return h.responseInternalServerError(c)
207
	}
208
209
	return h.responseNoContent(c, "phone has been dissociated from phone API key successfully")
210
}
211