Skip to content

Commit 65893fb

Browse files
authored
Added update multiple accounts token API (#51)
* Added update multiple accounts token API * Fixes code smells
1 parent 8ee1b0c commit 65893fb

File tree

4 files changed

+242
-2
lines changed

4 files changed

+242
-2
lines changed

resources/postman/Switcher GitOps.postman_collection.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,42 @@
117117
},
118118
"response": []
119119
},
120+
{
121+
"name": "Update Tokens",
122+
"request": {
123+
"auth": {
124+
"type": "bearer",
125+
"bearer": [
126+
{
127+
"key": "token",
128+
"value": "{{gitopsToken}}",
129+
"type": "string"
130+
}
131+
]
132+
},
133+
"method": "PUT",
134+
"header": [],
135+
"body": {
136+
"mode": "raw",
137+
"raw": "{\r\n\t\"domainId\": \"{{domain_id}}\",\r\n\t\"token\": \"{{github_pat}}\",\r\n \"environments\": [\r\n \"default\",\r\n \"staging\"\r\n ]\r\n}",
138+
"options": {
139+
"raw": {
140+
"language": "json"
141+
}
142+
}
143+
},
144+
"url": {
145+
"raw": "{{url}}/account",
146+
"host": [
147+
"{{url}}"
148+
],
149+
"path": [
150+
"account"
151+
]
152+
}
153+
},
154+
"response": []
155+
},
120156
{
121157
"name": "Update (force sync)",
122158
"request": {

resources/swagger.yaml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,46 @@ paths:
171171
application/json:
172172
schema:
173173
$ref: '#/components/schemas/ErrorResponse'
174+
put:
175+
tags:
176+
- Account API
177+
summary: Update multiple accounts token
178+
description: Update multiple accounts token by domain ID and environment
179+
security:
180+
- bearerAuth: []
181+
parameters:
182+
- name: domainId
183+
in: path
184+
required: true
185+
description: Domain ID
186+
schema:
187+
type: string
188+
format: uuid
189+
requestBody:
190+
required: true
191+
content:
192+
application/json:
193+
schema:
194+
$ref: '#/components/schemas/AccountTokensRequest'
195+
responses:
196+
'200':
197+
description: Account tokens updated
198+
content:
199+
application/json:
200+
schema:
201+
$ref: '#/components/schemas/AccountTokensResponse'
202+
'400':
203+
description: Invalid request
204+
content:
205+
application/json:
206+
schema:
207+
$ref: '#/components/schemas/ErrorResponse'
208+
'404':
209+
description: Account not found
210+
content:
211+
application/json:
212+
schema:
213+
$ref: '#/components/schemas/ErrorResponse'
174214
/account/{domainId}/{environment}:
175215
get:
176216
tags:
@@ -361,6 +401,30 @@ components:
361401
forceprune:
362402
type: boolean
363403
description: Force delete elements from the API when true
404+
AccountTokensRequest:
405+
type: object
406+
properties:
407+
token:
408+
type: string
409+
description: Git token
410+
domainId:
411+
type: string
412+
format: uuid
413+
description: Domain ID
414+
environments:
415+
type: array
416+
items:
417+
type: string
418+
description: Environment names
419+
AccountTokensResponse:
420+
type: object
421+
properties:
422+
result:
423+
type: boolean
424+
description: Result status
425+
message:
426+
type: string
427+
description: Result message
364428
ErrorResponse:
365429
type: object
366430
properties:

src/controller/account.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,27 @@ import (
1212
"github.com/switcherapi/switcher-gitops/src/utils"
1313
)
1414

15+
const (
16+
InvalidRequestDesc = "Invalid request"
17+
)
18+
1519
type AccountController struct {
1620
coreHandler *core.CoreHandler
1721
accountRepository repository.AccountRepository
1822
routeAccountPath string
1923
}
2024

25+
type AccountTokensRequest struct {
26+
Token string `json:"token"`
27+
DomainId string `json:"domainId"`
28+
Environments []string `json:"environments"`
29+
}
30+
31+
type AccountTokensResponse struct {
32+
Result bool `json:"result"`
33+
Message string `json:"message"`
34+
}
35+
2136
type ErrorResponse struct {
2237
Error string `json:"error"`
2338
}
@@ -35,6 +50,8 @@ func (controller *AccountController) RegisterRoutes(r *mux.Router) http.Handler
3550
ValidateToken(http.HandlerFunc(controller.CreateAccountHandler))).Methods(http.MethodPost)
3651
r.NewRoute().Path(controller.routeAccountPath).Name("UpdateAccount").Handler(
3752
ValidateToken(http.HandlerFunc(controller.UpdateAccountHandler))).Methods(http.MethodPut)
53+
r.NewRoute().Path(controller.routeAccountPath + "/{domainId}").Name("UpdateAccountTokens").Handler(
54+
ValidateToken(http.HandlerFunc(controller.UpdateAccountTokensHandler))).Methods(http.MethodPut)
3855
r.NewRoute().Path(controller.routeAccountPath + "/{domainId}").Name("GelAllAccountsByDomainId").Handler(
3956
ValidateToken(http.HandlerFunc(controller.FetchAllAccountsByDomainIdHandler))).Methods(http.MethodGet)
4057
r.NewRoute().Path(controller.routeAccountPath + "/{domainId}/{enviroment}").Name("GetAccount").Handler(
@@ -49,7 +66,7 @@ func (controller *AccountController) CreateAccountHandler(w http.ResponseWriter,
4966
var accountRequest model.Account
5067
err := json.NewDecoder(r.Body).Decode(&accountRequest)
5168
if err != nil {
52-
utils.ResponseJSON(w, ErrorResponse{Error: "Invalid request"}, http.StatusBadRequest)
69+
utils.ResponseJSON(w, ErrorResponse{Error: InvalidRequestDesc}, http.StatusBadRequest)
5370
return
5471
}
5572

@@ -112,7 +129,7 @@ func (controller *AccountController) UpdateAccountHandler(w http.ResponseWriter,
112129
err := json.NewDecoder(r.Body).Decode(&accountRequest)
113130
if err != nil {
114131
utils.LogError("Error updating account: %s", err.Error())
115-
utils.ResponseJSON(w, ErrorResponse{Error: "Invalid request"}, http.StatusBadRequest)
132+
utils.ResponseJSON(w, ErrorResponse{Error: InvalidRequestDesc}, http.StatusBadRequest)
116133
return
117134
}
118135

@@ -132,6 +149,43 @@ func (controller *AccountController) UpdateAccountHandler(w http.ResponseWriter,
132149
utils.ResponseJSON(w, accountUpdated, http.StatusOK)
133150
}
134151

152+
func (controller *AccountController) UpdateAccountTokensHandler(w http.ResponseWriter, r *http.Request) {
153+
var accountTokensRequest AccountTokensRequest
154+
err := json.NewDecoder(r.Body).Decode(&accountTokensRequest)
155+
if err != nil {
156+
utils.LogError("Error updating account tokens: %s", err.Error())
157+
utils.ResponseJSON(w, ErrorResponse{Error: InvalidRequestDesc}, http.StatusBadRequest)
158+
return
159+
}
160+
161+
if accountTokensRequest.Token == "" {
162+
utils.LogError("Error updating account tokens: Token is required")
163+
utils.ResponseJSON(w, ErrorResponse{Error: "Token is required"}, http.StatusBadRequest)
164+
return
165+
}
166+
167+
// Encrypt token before saving
168+
accountTokensRequest.Token = utils.Encrypt(accountTokensRequest.Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
169+
170+
// Update account tokens
171+
for _, environment := range accountTokensRequest.Environments {
172+
account, err := controller.accountRepository.FetchByDomainIdEnvironment(accountTokensRequest.DomainId, environment)
173+
if err != nil {
174+
utils.LogError("Error fetching account: %s", err.Error())
175+
utils.ResponseJSON(w, ErrorResponse{Error: "Error fetching account"}, http.StatusNotFound)
176+
return
177+
}
178+
179+
account.Token = accountTokensRequest.Token
180+
controller.accountRepository.Update(account)
181+
}
182+
183+
utils.ResponseJSON(w, AccountTokensResponse{
184+
Result: true,
185+
Message: "Account tokens updated successfully",
186+
}, http.StatusOK)
187+
}
188+
135189
func (controller *AccountController) DeleteAccountHandler(w http.ResponseWriter, r *http.Request) {
136190
domainId := mux.Vars(r)["domainId"]
137191
enviroment := mux.Vars(r)["enviroment"]

src/controller/account_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,92 @@ func TestUpdateAccountHandler(t *testing.T) {
227227
})
228228
}
229229

230+
func TestUpdateAccountTokensHandler(t *testing.T) {
231+
token := generateToken("test", time.Minute)
232+
233+
t.Run("Should update account tokens", func(t *testing.T) {
234+
// Create accounts
235+
account1 := accountV1
236+
account1.Domain.ID = "123-controller-update-account-tokens"
237+
account1.Environment = "default"
238+
accountController.CreateAccountHandler(givenAccountRequest(account1))
239+
240+
account2 := accountV1
241+
account2.Domain.ID = "123-controller-update-account-tokens"
242+
account2.Environment = "staging"
243+
accountController.CreateAccountHandler(givenAccountRequest(account2))
244+
245+
// Test
246+
payload, _ := json.Marshal(AccountTokensRequest{
247+
DomainId: account1.Domain.ID,
248+
Environments: []string{account1.Environment, account2.Environment},
249+
Token: "new-token",
250+
})
251+
252+
req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath+"/"+account1.Domain.ID, bytes.NewBuffer(payload))
253+
response := executeRequest(req, r, token)
254+
255+
// Assert
256+
var accountTokensResponse AccountTokensResponse
257+
err := json.NewDecoder(response.Body).Decode(&accountTokensResponse)
258+
259+
assert.Equal(t, http.StatusOK, response.Code)
260+
assert.Nil(t, err)
261+
262+
accountRepository := repository.NewAccountRepositoryMongo(mongoDb)
263+
accountFromDb1 := accountRepository.FetchAllByDomainId(account1.Domain.ID)
264+
265+
decryptedToken1, _ := utils.Decrypt(accountFromDb1[0].Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
266+
decryptedToken2, _ := utils.Decrypt(accountFromDb1[1].Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY"))
267+
assert.Equal(t, "new-token", decryptedToken1)
268+
assert.Equal(t, "new-token", decryptedToken2)
269+
})
270+
271+
t.Run("Should not update account tokens - token is required", func(t *testing.T) {
272+
// Test
273+
payload, _ := json.Marshal(AccountTokensRequest{
274+
DomainId: "123-controller-update-account-tokens",
275+
Environments: []string{"default"},
276+
Token: "",
277+
})
278+
279+
req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath+"/123-controller-update-account-tokens", bytes.NewBuffer(payload))
280+
response := executeRequest(req, r, token)
281+
282+
// Assert
283+
assert.Equal(t, http.StatusBadRequest, response.Code)
284+
assert.Equal(t, "{\"error\":\"Token is required\"}", response.Body.String())
285+
})
286+
287+
t.Run("Should not update account tokens - invalid request", func(t *testing.T) {
288+
// Test
289+
payload := []byte("")
290+
req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath+"/invalid", bytes.NewBuffer(payload))
291+
response := executeRequest(req, r, token)
292+
293+
// Assert
294+
assert.Equal(t, http.StatusBadRequest, response.Code)
295+
assert.Equal(t, "{\"error\":\"Invalid request\"}", response.Body.String())
296+
})
297+
298+
t.Run("Should not update account tokens - not found", func(t *testing.T) {
299+
// Test
300+
payload, _ := json.Marshal(AccountTokensRequest{
301+
DomainId: "not-found",
302+
Environments: []string{"default"},
303+
Token: "new-token",
304+
})
305+
306+
req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath+"/not-found", bytes.NewBuffer(payload))
307+
response := executeRequest(req, r, token)
308+
309+
// Assert
310+
assert.Equal(t, http.StatusNotFound, response.Code)
311+
assert.Equal(t, "{\"error\":\"Error fetching account\"}", response.Body.String())
312+
})
313+
314+
}
315+
230316
func TestDeleteAccountHandler(t *testing.T) {
231317
token := generateToken("test", time.Minute)
232318

0 commit comments

Comments
 (0)