Added PUT /account/{id}/fields
This commit is contained in:
parent
227132413a
commit
880a384f35
|
@ -34,12 +34,12 @@ func (d Db) AccountCreate(input AccountCreateInput) (CreatedAccount, error) {
|
||||||
|
|
||||||
_, err := d.DbPool.Exec(context.Background(), accountFieldsSQL, newFieldID, input.ID, field.Name, field.Values)
|
_, err := d.DbPool.Exec(context.Background(), accountFieldsSQL, newFieldID, input.ID, field.Name, field.Values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.HasPrefix(err.Error(), "ERROR: duplicate key") {
|
//if strings.HasPrefix(err.Error(), "ERROR: duplicate key") {
|
||||||
d.Log.Error("Database error when trying to account field", "err", err.Error())
|
d.Log.Error("Database error when trying to add account field", "err", err.Error(), "accountID", input.ID, "fieldName", field.Name, "fieldvalues", field.Values)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Log.Debug("Added account field", "accountId", input.ID, "fieldName", field.Name, "fieldValues", field.Values)
|
d.Log.Debug("Added account field", "accountID", input.ID, "fieldName", field.Name, "fieldValues", field.Values)
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreatedAccount{
|
return CreatedAccount{
|
||||||
|
@ -129,3 +129,51 @@ func (d Db) AccountGet(accountID string, APIKey string, Name string) (Account, e
|
||||||
|
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d Db) AccountUpdateFields(accountID string, fields []AccountCreateInputFields) (Account, error) {
|
||||||
|
// Begin database transaction
|
||||||
|
conn, err := d.DbPool.Acquire(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
d.Log.Error("Could not acquire database connection", "err", err.Error(), "accountID", accountID)
|
||||||
|
return Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := conn.Begin(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
d.Log.Error("Could not begin database transaction", "err", err.Error(), "accountID", accountID)
|
||||||
|
return Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rollback is safe to call even if the tx is already closed, so if
|
||||||
|
// the tx commits successfully, this is a no-op
|
||||||
|
defer tx.Rollback(context.Background())
|
||||||
|
|
||||||
|
_, err = tx.Exec(context.Background(), "DELETE FROM \"accountsFields\" WHERE \"accountId\" = $1;", accountID)
|
||||||
|
if err != nil {
|
||||||
|
d.Log.Error("Could not delete previous fields", "err", err.Error(), "accountID", accountID)
|
||||||
|
return Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accountFieldsSQL := "INSERT INTO \"accountsFields\" (id, \"accountId\", name, value) VALUES($1,$2,$3,$4);"
|
||||||
|
for _, field := range fields {
|
||||||
|
newFieldID, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
d.Log.Fatal("Could not create new Uuid", "err", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(context.Background(), accountFieldsSQL, newFieldID, accountID, field.Name, field.Values)
|
||||||
|
if err != nil {
|
||||||
|
d.Log.Error("Database error when trying to add account field", "err", err.Error(), "accountID", accountID, "fieldName", field.Name, "fieldvalues", field.Values)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Log.Debug("Added account field", "accountID", accountID, "fieldName", field.Name, "fieldValues", field.Values)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
d.Log.Error("Database error when tying to commit", "err", err.Error())
|
||||||
|
return Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.AccountGet(accountID, "", "")
|
||||||
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ var doc = `{
|
||||||
},
|
},
|
||||||
"/account/:id": {
|
"/account/:id": {
|
||||||
"delete": {
|
"delete": {
|
||||||
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
|
"description": "Requires Authorization-header with role \"admin\" or a matching account id\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
@ -171,6 +171,15 @@ var doc = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"415": {
|
"415": {
|
||||||
"description": "Unsupported Media Type",
|
"description": "Unsupported Media Type",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
@ -258,6 +267,86 @@ var doc = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/account/{id}/fields": {
|
||||||
|
"put": {
|
||||||
|
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "Update account fields",
|
||||||
|
"operationId": "account-update-fields",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Fields array with objects to be written to database",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/db.AccountCreateInputFields"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/db.Account"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"415": {
|
||||||
|
"description": "Unsupported Media Type",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/auth/api-key": {
|
"/auth/api-key": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Authenticate account by API Key",
|
"description": "Authenticate account by API Key",
|
||||||
|
|
|
@ -103,7 +103,7 @@
|
||||||
},
|
},
|
||||||
"/account/:id": {
|
"/account/:id": {
|
||||||
"delete": {
|
"delete": {
|
||||||
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
|
"description": "Requires Authorization-header with role \"admin\" or a matching account id\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
|
@ -155,6 +155,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"415": {
|
"415": {
|
||||||
"description": "Unsupported Media Type",
|
"description": "Unsupported Media Type",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
@ -242,6 +251,86 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/account/{id}/fields": {
|
||||||
|
"put": {
|
||||||
|
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"summary": "Update account fields",
|
||||||
|
"operationId": "account-update-fields",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Fields array with objects to be written to database",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/db.AccountCreateInputFields"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/db.Account"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"415": {
|
||||||
|
"description": "Unsupported Media Type",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/handlers.ResJSONError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/auth/api-key": {
|
"/auth/api-key": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Authenticate account by API Key",
|
"description": "Authenticate account by API Key",
|
||||||
|
|
|
@ -142,7 +142,7 @@ paths:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: |-
|
description: |-
|
||||||
Requires Authorization-header with role "admin".
|
Requires Authorization-header with role "admin" or a matching account id
|
||||||
Example: Authorization: bearer xxx
|
Example: Authorization: bearer xxx
|
||||||
Where "xxx" is a valid JWT token
|
Where "xxx" is a valid JWT token
|
||||||
operationId: account-del
|
operationId: account-del
|
||||||
|
@ -177,6 +177,12 @@ paths:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/handlers.ResJSONError'
|
$ref: '#/definitions/handlers.ResJSONError'
|
||||||
type: array
|
type: array
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/handlers.ResJSONError'
|
||||||
|
type: array
|
||||||
"415":
|
"415":
|
||||||
description: Unsupported Media Type
|
description: Unsupported Media Type
|
||||||
schema:
|
schema:
|
||||||
|
@ -237,6 +243,62 @@ paths:
|
||||||
$ref: '#/definitions/handlers.ResJSONError'
|
$ref: '#/definitions/handlers.ResJSONError'
|
||||||
type: array
|
type: array
|
||||||
summary: Get account by id
|
summary: Get account by id
|
||||||
|
/account/{id}/fields:
|
||||||
|
put:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: |-
|
||||||
|
Requires Authorization-header with role "admin".
|
||||||
|
Example: Authorization: bearer xxx
|
||||||
|
Where "xxx" is a valid JWT token
|
||||||
|
operationId: account-update-fields
|
||||||
|
parameters:
|
||||||
|
- description: Fields array with objects to be written to database
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/db.AccountCreateInputFields'
|
||||||
|
type: array
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/db.Account'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/handlers.ResJSONError'
|
||||||
|
type: array
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/handlers.ResJSONError'
|
||||||
|
type: array
|
||||||
|
"403":
|
||||||
|
description: Forbidden
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/handlers.ResJSONError'
|
||||||
|
type: array
|
||||||
|
"415":
|
||||||
|
description: Unsupported Media Type
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/handlers.ResJSONError'
|
||||||
|
type: array
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/handlers.ResJSONError'
|
||||||
|
type: array
|
||||||
|
summary: Update account fields
|
||||||
/auth/api-key:
|
/auth/api-key:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
// AccountDel godoc
|
// AccountDel godoc
|
||||||
// @Summary Delete an account
|
// @Summary Delete an account
|
||||||
// @Description Requires Authorization-header with role "admin".
|
// @Description Requires Authorization-header with role "admin" or a matching account id
|
||||||
// @Description Example: Authorization: bearer xxx
|
// @Description Example: Authorization: bearer xxx
|
||||||
// @Description Where "xxx" is a valid JWT token
|
// @Description Where "xxx" is a valid JWT token
|
||||||
// @ID account-del
|
// @ID account-del
|
||||||
|
|
52
src/handlers/put.go
Normal file
52
src/handlers/put.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gitlab.larvit.se/power-plan/auth/src/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccountUpdateFields godoc
|
||||||
|
// @Summary Update account fields
|
||||||
|
// @Description Requires Authorization-header with role "admin".
|
||||||
|
// @Description Example: Authorization: bearer xxx
|
||||||
|
// @Description Where "xxx" is a valid JWT token
|
||||||
|
// @ID account-update-fields
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body []db.AccountCreateInputFields true "Fields array with objects to be written to database"
|
||||||
|
// @Success 200 {object} db.Account
|
||||||
|
// @Failure 400 {object} []ResJSONError
|
||||||
|
// @Failure 401 {object} []ResJSONError
|
||||||
|
// @Failure 403 {object} []ResJSONError
|
||||||
|
// @Failure 415 {object} []ResJSONError
|
||||||
|
// @Failure 500 {object} []ResJSONError
|
||||||
|
// @Router /account/{id}/fields [put]
|
||||||
|
func (h Handlers) AccountUpdateFields(c *fiber.Ctx) error {
|
||||||
|
accountID := c.Params("accountID")
|
||||||
|
|
||||||
|
_, uuidErr := uuid.Parse(accountID)
|
||||||
|
if uuidErr != nil {
|
||||||
|
return c.Status(400).JSON([]ResJSONError{{Error: "Invalid uuid format"}})
|
||||||
|
}
|
||||||
|
|
||||||
|
authErr := h.RequireAdminRole(c)
|
||||||
|
if authErr != nil {
|
||||||
|
return c.Status(403).JSON([]ResJSONError{{Error: authErr.Error()}})
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldsInput := new([]db.AccountCreateInputFields)
|
||||||
|
|
||||||
|
if err := c.BodyParser(fieldsInput); err != nil {
|
||||||
|
return c.Status(400).JSON([]ResJSONError{
|
||||||
|
{Error: err.Error()},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedAccount, err := h.Db.AccountUpdateFields(accountID, *fieldsInput)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(500).JSON([]ResJSONError{{Error: "Internal server error"}})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(200).JSON(updatedAccount)
|
||||||
|
}
|
|
@ -97,6 +97,8 @@ func main() {
|
||||||
app.Post("/auth/api-key", handlers.AccountAuthAPIKey)
|
app.Post("/auth/api-key", handlers.AccountAuthAPIKey)
|
||||||
app.Post("/auth/password", handlers.AccountAuthPassword)
|
app.Post("/auth/password", handlers.AccountAuthPassword)
|
||||||
app.Post("/renew-token", handlers.RenewToken)
|
app.Post("/renew-token", handlers.RenewToken)
|
||||||
|
app.Put("/account/:accountID/fields", handlers.AccountUpdateFields)
|
||||||
|
// app.Put("")
|
||||||
|
|
||||||
log.Info("Trying to start web server", "WEB_BIND_HOST", os.Getenv("WEB_BIND_HOST"))
|
log.Info("Trying to start web server", "WEB_BIND_HOST", os.Getenv("WEB_BIND_HOST"))
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,32 @@ test('test-cases/01basic.js: Auth by username and password', async t => {
|
||||||
t.equal(userJWT.accountName, userName, 'The verified account name should match the created user');
|
t.equal(userJWT.accountName, userName, 'The verified account name should match the created user');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('test-cases/01basic.js: PUT /account/{id}/fields', async t => {
|
||||||
|
const res = await got.put(`${process.env.AUTH_URL}/account/${user.id}/fields`, {
|
||||||
|
headers: { 'Authorization': `bearer ${adminJWTString}`},
|
||||||
|
json: [
|
||||||
|
{
|
||||||
|
name: 'foo',
|
||||||
|
values: ['bar'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'role',
|
||||||
|
values: ['tomte'],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
responseType: 'json',
|
||||||
|
});
|
||||||
|
|
||||||
|
t.equal(user.id, res.body.id, 'The responded account id should be the same as the old one');
|
||||||
|
t.equal(Object.keys(res.body.fields).length, 2, 'There should only be two fields in total');
|
||||||
|
t.equal(JSON.stringify(res.body.fields.foo), '["bar"]', 'The foo field should have values ["bar"]');
|
||||||
|
t.equal(JSON.stringify(res.body.fields.role), '["tomte"]', 'The role field should have values ["tomte"]');
|
||||||
|
|
||||||
|
// Overload the previous user
|
||||||
|
user.fields = res.body.fields;
|
||||||
|
user.name = res.body.name;
|
||||||
|
});
|
||||||
|
|
||||||
test('test-cases/01basic.js: Remove an account', async t => {
|
test('test-cases/01basic.js: Remove an account', async t => {
|
||||||
try {
|
try {
|
||||||
// Random uuid that should not exist in the db. The chance of this existing is... small
|
// Random uuid that should not exist in the db. The chance of this existing is... small
|
||||||
|
|
Loading…
Reference in New Issue
Block a user