From b83505104a8bd523fdb273484b5293b274a12c3c Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Tue, 30 Aug 2022 10:05:11 -0800 Subject: [PATCH] align types with new db schema --- backend/app/api/docs/docs.go | 489 +++++++++++++++++- backend/app/api/docs/swagger.json | 489 +++++++++++++++++- backend/app/api/docs/swagger.yaml | 362 ++++++++++++- backend/app/api/main.go | 1 - backend/app/api/middleware.go | 2 +- backend/app/api/routes.go | 42 +- backend/app/api/seed.go | 11 +- backend/app/api/v1/main_test.go | 5 +- backend/app/api/v1/v1_ctrl_admin.go | 8 +- backend/app/api/v1/v1_ctrl_admin_test.go | 10 +- backend/app/api/v1/v1_ctrl_user.go | 33 +- backend/app/generator/main.go | 19 +- backend/config.template.yml | 1 + backend/go.sum | 4 + backend/internal/config/conf_seed.go | 1 + backend/internal/mapper/users_automapper.go | 27 - backend/internal/repo/repo_group.go | 29 ++ .../repo/{token_ent.go => repo_tokens.go} | 7 +- ...{token_ent_test.go => repo_tokens_test.go} | 0 .../repo/{users_ent.go => repo_users.go} | 62 +-- .../{users_ent_test.go => repo_users_test.go} | 3 +- backend/internal/repo/repos_all.go | 6 +- backend/internal/repo/token_interface.go | 20 - backend/internal/repo/users_interface.go | 27 - backend/internal/services/contexts.go | 8 +- backend/internal/services/contexts_test.go | 4 +- backend/internal/services/service_admin.go | 13 +- backend/internal/services/service_user.go | 31 +- backend/internal/types/users_types.go | 27 +- backend/internal/types/users_types_test.go | 13 - 30 files changed, 1491 insertions(+), 263 deletions(-) delete mode 100644 backend/internal/mapper/users_automapper.go create mode 100644 backend/internal/repo/repo_group.go rename backend/internal/repo/{token_ent.go => repo_tokens.go} (90%) rename backend/internal/repo/{token_ent_test.go => repo_tokens_test.go} (100%) rename backend/internal/repo/{users_ent.go => repo_users.go} (63%) rename backend/internal/repo/{users_ent_test.go => repo_users_test.go} (97%) delete mode 100644 backend/internal/repo/token_interface.go delete mode 100644 backend/internal/repo/users_interface.go diff --git a/backend/app/api/docs/docs.go b/backend/app/api/docs/docs.go index 9a371a6..a6d2d37 100644 --- a/backend/app/api/docs/docs.go +++ b/backend/app/api/docs/docs.go @@ -80,7 +80,7 @@ const docTemplate = `{ "item": { "type": "array", "items": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -126,7 +126,7 @@ const docTemplate = `{ "type": "object", "properties": { "item": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -171,7 +171,7 @@ const docTemplate = `{ "type": "object", "properties": { "item": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -223,7 +223,7 @@ const docTemplate = `{ "type": "object", "properties": { "item": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -337,6 +337,48 @@ const docTemplate = `{ } } }, + "/v1/users/register": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "User" + ], + "summary": "Get the current user", + "parameters": [ + { + "description": "User Data", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.UserRegistration" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/server.Result" + }, + { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/ent.User" + } + } + } + ] + } + } + } + } + }, "/v1/users/self": { "get": { "security": [ @@ -363,7 +405,7 @@ const docTemplate = `{ "type": "object", "properties": { "item": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -441,6 +483,418 @@ const docTemplate = `{ } }, "definitions": { + "ent.AuthTokens": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the AuthTokensQuery when eager-loading is set.", + "$ref": "#/definitions/ent.AuthTokensEdges" + }, + "expires_at": { + "description": "ExpiresAt holds the value of the \"expires_at\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "token": { + "description": "Token holds the value of the \"token\" field.", + "type": "array", + "items": { + "type": "integer" + } + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.AuthTokensEdges": { + "type": "object", + "properties": { + "user": { + "description": "User holds the value of the user edge.", + "$ref": "#/definitions/ent.User" + } + } + }, + "ent.Group": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "currency": { + "description": "Currency holds the value of the \"currency\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the GroupQuery when eager-loading is set.", + "$ref": "#/definitions/ent.GroupEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.GroupEdges": { + "type": "object", + "properties": { + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Item" + } + }, + "labels": { + "description": "Labels holds the value of the labels edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Label" + } + }, + "locations": { + "description": "Locations holds the value of the locations edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Location" + } + }, + "users": { + "description": "Users holds the value of the users edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.User" + } + } + } + }, + "ent.Item": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the ItemQuery when eager-loading is set.", + "$ref": "#/definitions/ent.ItemEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "manufacturer": { + "description": "Manufacturer holds the value of the \"manufacturer\" field.", + "type": "string" + }, + "model_number": { + "description": "ModelNumber holds the value of the \"model_number\" field.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "notes": { + "description": "Notes holds the value of the \"notes\" field.", + "type": "string" + }, + "purchase_from": { + "description": "PurchaseFrom holds the value of the \"purchase_from\" field.", + "type": "string" + }, + "purchase_price": { + "description": "PurchasePrice holds the value of the \"purchase_price\" field.", + "type": "number" + }, + "purchase_receipt_id": { + "description": "PurchaseReceiptID holds the value of the \"purchase_receipt_id\" field.", + "type": "string" + }, + "purchase_time": { + "description": "PurchaseTime holds the value of the \"purchase_time\" field.", + "type": "string" + }, + "serial_number": { + "description": "SerialNumber holds the value of the \"serial_number\" field.", + "type": "string" + }, + "sold_notes": { + "description": "SoldNotes holds the value of the \"sold_notes\" field.", + "type": "string" + }, + "sold_price": { + "description": "SoldPrice holds the value of the \"sold_price\" field.", + "type": "number" + }, + "sold_receipt_id": { + "description": "SoldReceiptID holds the value of the \"sold_receipt_id\" field.", + "type": "string" + }, + "sold_time": { + "description": "SoldTime holds the value of the \"sold_time\" field.", + "type": "string" + }, + "sold_to": { + "description": "SoldTo holds the value of the \"sold_to\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.ItemEdges": { + "type": "object", + "properties": { + "fields": { + "description": "Fields holds the value of the fields edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.ItemField" + } + }, + "group": { + "description": "Group holds the value of the group edge.", + "$ref": "#/definitions/ent.Group" + }, + "label": { + "description": "Label holds the value of the label edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Label" + } + }, + "location": { + "description": "Location holds the value of the location edge.", + "$ref": "#/definitions/ent.Location" + } + } + }, + "ent.ItemField": { + "type": "object", + "properties": { + "boolean_value": { + "description": "BooleanValue holds the value of the \"boolean_value\" field.", + "type": "boolean" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the ItemFieldQuery when eager-loading is set.", + "$ref": "#/definitions/ent.ItemFieldEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "number_value": { + "description": "NumberValue holds the value of the \"number_value\" field.", + "type": "integer" + }, + "text_value": { + "description": "TextValue holds the value of the \"text_value\" field.", + "type": "string" + }, + "time_value": { + "description": "TimeValue holds the value of the \"time_value\" field.", + "type": "string" + }, + "type": { + "description": "Type holds the value of the \"type\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.ItemFieldEdges": { + "type": "object", + "properties": { + "item": { + "description": "Item holds the value of the item edge.", + "$ref": "#/definitions/ent.Item" + } + } + }, + "ent.Label": { + "type": "object", + "properties": { + "color": { + "description": "Color holds the value of the \"color\" field.", + "type": "string" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the LabelQuery when eager-loading is set.", + "$ref": "#/definitions/ent.LabelEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.LabelEdges": { + "type": "object", + "properties": { + "group": { + "description": "Group holds the value of the group edge.", + "$ref": "#/definitions/ent.Group" + }, + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Item" + } + } + } + }, + "ent.Location": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the LocationQuery when eager-loading is set.", + "$ref": "#/definitions/ent.LocationEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.LocationEdges": { + "type": "object", + "properties": { + "group": { + "description": "Group holds the value of the group edge.", + "$ref": "#/definitions/ent.Group" + }, + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Item" + } + } + } + }, + "ent.User": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the UserQuery when eager-loading is set.", + "$ref": "#/definitions/ent.UserEdges" + }, + "email": { + "description": "Email holds the value of the \"email\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "is_superuser": { + "description": "IsSuperuser holds the value of the \"is_superuser\" field.", + "type": "boolean" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.UserEdges": { + "type": "object", + "properties": { + "auth_tokens": { + "description": "AuthTokens holds the value of the auth_tokens edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.AuthTokens" + } + }, + "group": { + "description": "Group holds the value of the group edge.", + "$ref": "#/definitions/ent.Group" + } + } + }, "server.Result": { "type": "object", "properties": { @@ -491,6 +945,9 @@ const docTemplate = `{ "email": { "type": "string" }, + "groupID": { + "type": "string" + }, "isSuperuser": { "type": "boolean" }, @@ -502,20 +959,28 @@ const docTemplate = `{ } } }, - "types.UserOut": { + "types.UserIn": { "type": "object", "properties": { "email": { "type": "string" }, - "id": { - "type": "string" - }, - "isSuperuser": { - "type": "boolean" - }, "name": { "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "types.UserRegistration": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/types.UserIn" } } }, diff --git a/backend/app/api/docs/swagger.json b/backend/app/api/docs/swagger.json index 51932ec..3124ca5 100644 --- a/backend/app/api/docs/swagger.json +++ b/backend/app/api/docs/swagger.json @@ -72,7 +72,7 @@ "item": { "type": "array", "items": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -118,7 +118,7 @@ "type": "object", "properties": { "item": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -163,7 +163,7 @@ "type": "object", "properties": { "item": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -215,7 +215,7 @@ "type": "object", "properties": { "item": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -329,6 +329,48 @@ } } }, + "/v1/users/register": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "User" + ], + "summary": "Get the current user", + "parameters": [ + { + "description": "User Data", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.UserRegistration" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/server.Result" + }, + { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/ent.User" + } + } + } + ] + } + } + } + } + }, "/v1/users/self": { "get": { "security": [ @@ -355,7 +397,7 @@ "type": "object", "properties": { "item": { - "$ref": "#/definitions/types.UserOut" + "$ref": "#/definitions/ent.User" } } } @@ -433,6 +475,418 @@ } }, "definitions": { + "ent.AuthTokens": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the AuthTokensQuery when eager-loading is set.", + "$ref": "#/definitions/ent.AuthTokensEdges" + }, + "expires_at": { + "description": "ExpiresAt holds the value of the \"expires_at\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "token": { + "description": "Token holds the value of the \"token\" field.", + "type": "array", + "items": { + "type": "integer" + } + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.AuthTokensEdges": { + "type": "object", + "properties": { + "user": { + "description": "User holds the value of the user edge.", + "$ref": "#/definitions/ent.User" + } + } + }, + "ent.Group": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "currency": { + "description": "Currency holds the value of the \"currency\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the GroupQuery when eager-loading is set.", + "$ref": "#/definitions/ent.GroupEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.GroupEdges": { + "type": "object", + "properties": { + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Item" + } + }, + "labels": { + "description": "Labels holds the value of the labels edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Label" + } + }, + "locations": { + "description": "Locations holds the value of the locations edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Location" + } + }, + "users": { + "description": "Users holds the value of the users edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.User" + } + } + } + }, + "ent.Item": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the ItemQuery when eager-loading is set.", + "$ref": "#/definitions/ent.ItemEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "manufacturer": { + "description": "Manufacturer holds the value of the \"manufacturer\" field.", + "type": "string" + }, + "model_number": { + "description": "ModelNumber holds the value of the \"model_number\" field.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "notes": { + "description": "Notes holds the value of the \"notes\" field.", + "type": "string" + }, + "purchase_from": { + "description": "PurchaseFrom holds the value of the \"purchase_from\" field.", + "type": "string" + }, + "purchase_price": { + "description": "PurchasePrice holds the value of the \"purchase_price\" field.", + "type": "number" + }, + "purchase_receipt_id": { + "description": "PurchaseReceiptID holds the value of the \"purchase_receipt_id\" field.", + "type": "string" + }, + "purchase_time": { + "description": "PurchaseTime holds the value of the \"purchase_time\" field.", + "type": "string" + }, + "serial_number": { + "description": "SerialNumber holds the value of the \"serial_number\" field.", + "type": "string" + }, + "sold_notes": { + "description": "SoldNotes holds the value of the \"sold_notes\" field.", + "type": "string" + }, + "sold_price": { + "description": "SoldPrice holds the value of the \"sold_price\" field.", + "type": "number" + }, + "sold_receipt_id": { + "description": "SoldReceiptID holds the value of the \"sold_receipt_id\" field.", + "type": "string" + }, + "sold_time": { + "description": "SoldTime holds the value of the \"sold_time\" field.", + "type": "string" + }, + "sold_to": { + "description": "SoldTo holds the value of the \"sold_to\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.ItemEdges": { + "type": "object", + "properties": { + "fields": { + "description": "Fields holds the value of the fields edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.ItemField" + } + }, + "group": { + "description": "Group holds the value of the group edge.", + "$ref": "#/definitions/ent.Group" + }, + "label": { + "description": "Label holds the value of the label edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Label" + } + }, + "location": { + "description": "Location holds the value of the location edge.", + "$ref": "#/definitions/ent.Location" + } + } + }, + "ent.ItemField": { + "type": "object", + "properties": { + "boolean_value": { + "description": "BooleanValue holds the value of the \"boolean_value\" field.", + "type": "boolean" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the ItemFieldQuery when eager-loading is set.", + "$ref": "#/definitions/ent.ItemFieldEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "number_value": { + "description": "NumberValue holds the value of the \"number_value\" field.", + "type": "integer" + }, + "text_value": { + "description": "TextValue holds the value of the \"text_value\" field.", + "type": "string" + }, + "time_value": { + "description": "TimeValue holds the value of the \"time_value\" field.", + "type": "string" + }, + "type": { + "description": "Type holds the value of the \"type\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.ItemFieldEdges": { + "type": "object", + "properties": { + "item": { + "description": "Item holds the value of the item edge.", + "$ref": "#/definitions/ent.Item" + } + } + }, + "ent.Label": { + "type": "object", + "properties": { + "color": { + "description": "Color holds the value of the \"color\" field.", + "type": "string" + }, + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the LabelQuery when eager-loading is set.", + "$ref": "#/definitions/ent.LabelEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.LabelEdges": { + "type": "object", + "properties": { + "group": { + "description": "Group holds the value of the group edge.", + "$ref": "#/definitions/ent.Group" + }, + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Item" + } + } + } + }, + "ent.Location": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "description": { + "description": "Description holds the value of the \"description\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the LocationQuery when eager-loading is set.", + "$ref": "#/definitions/ent.LocationEdges" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.LocationEdges": { + "type": "object", + "properties": { + "group": { + "description": "Group holds the value of the group edge.", + "$ref": "#/definitions/ent.Group" + }, + "items": { + "description": "Items holds the value of the items edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.Item" + } + } + } + }, + "ent.User": { + "type": "object", + "properties": { + "created_at": { + "description": "CreatedAt holds the value of the \"created_at\" field.", + "type": "string" + }, + "edges": { + "description": "Edges holds the relations/edges for other nodes in the graph.\nThe values are being populated by the UserQuery when eager-loading is set.", + "$ref": "#/definitions/ent.UserEdges" + }, + "email": { + "description": "Email holds the value of the \"email\" field.", + "type": "string" + }, + "id": { + "description": "ID of the ent.", + "type": "string" + }, + "is_superuser": { + "description": "IsSuperuser holds the value of the \"is_superuser\" field.", + "type": "boolean" + }, + "name": { + "description": "Name holds the value of the \"name\" field.", + "type": "string" + }, + "updated_at": { + "description": "UpdatedAt holds the value of the \"updated_at\" field.", + "type": "string" + } + } + }, + "ent.UserEdges": { + "type": "object", + "properties": { + "auth_tokens": { + "description": "AuthTokens holds the value of the auth_tokens edge.", + "type": "array", + "items": { + "$ref": "#/definitions/ent.AuthTokens" + } + }, + "group": { + "description": "Group holds the value of the group edge.", + "$ref": "#/definitions/ent.Group" + } + } + }, "server.Result": { "type": "object", "properties": { @@ -483,6 +937,9 @@ "email": { "type": "string" }, + "groupID": { + "type": "string" + }, "isSuperuser": { "type": "boolean" }, @@ -494,20 +951,28 @@ } } }, - "types.UserOut": { + "types.UserIn": { "type": "object", "properties": { "email": { "type": "string" }, - "id": { - "type": "string" - }, - "isSuperuser": { - "type": "boolean" - }, "name": { "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "types.UserRegistration": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/types.UserIn" } } }, diff --git a/backend/app/api/docs/swagger.yaml b/backend/app/api/docs/swagger.yaml index b268aa0..5845631 100644 --- a/backend/app/api/docs/swagger.yaml +++ b/backend/app/api/docs/swagger.yaml @@ -1,5 +1,316 @@ basePath: /api definitions: + ent.AuthTokens: + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + $ref: '#/definitions/ent.AuthTokensEdges' + description: |- + Edges holds the relations/edges for other nodes in the graph. + The values are being populated by the AuthTokensQuery when eager-loading is set. + expires_at: + description: ExpiresAt holds the value of the "expires_at" field. + type: string + id: + description: ID of the ent. + type: string + token: + description: Token holds the value of the "token" field. + items: + type: integer + type: array + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + type: object + ent.AuthTokensEdges: + properties: + user: + $ref: '#/definitions/ent.User' + description: User holds the value of the user edge. + type: object + ent.Group: + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + currency: + description: Currency holds the value of the "currency" field. + type: string + edges: + $ref: '#/definitions/ent.GroupEdges' + description: |- + Edges holds the relations/edges for other nodes in the graph. + The values are being populated by the GroupQuery when eager-loading is set. + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + type: object + ent.GroupEdges: + properties: + items: + description: Items holds the value of the items edge. + items: + $ref: '#/definitions/ent.Item' + type: array + labels: + description: Labels holds the value of the labels edge. + items: + $ref: '#/definitions/ent.Label' + type: array + locations: + description: Locations holds the value of the locations edge. + items: + $ref: '#/definitions/ent.Location' + type: array + users: + description: Users holds the value of the users edge. + items: + $ref: '#/definitions/ent.User' + type: array + type: object + ent.Item: + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + $ref: '#/definitions/ent.ItemEdges' + description: |- + Edges holds the relations/edges for other nodes in the graph. + The values are being populated by the ItemQuery when eager-loading is set. + id: + description: ID of the ent. + type: string + manufacturer: + description: Manufacturer holds the value of the "manufacturer" field. + type: string + model_number: + description: ModelNumber holds the value of the "model_number" field. + type: string + name: + description: Name holds the value of the "name" field. + type: string + notes: + description: Notes holds the value of the "notes" field. + type: string + purchase_from: + description: PurchaseFrom holds the value of the "purchase_from" field. + type: string + purchase_price: + description: PurchasePrice holds the value of the "purchase_price" field. + type: number + purchase_receipt_id: + description: PurchaseReceiptID holds the value of the "purchase_receipt_id" + field. + type: string + purchase_time: + description: PurchaseTime holds the value of the "purchase_time" field. + type: string + serial_number: + description: SerialNumber holds the value of the "serial_number" field. + type: string + sold_notes: + description: SoldNotes holds the value of the "sold_notes" field. + type: string + sold_price: + description: SoldPrice holds the value of the "sold_price" field. + type: number + sold_receipt_id: + description: SoldReceiptID holds the value of the "sold_receipt_id" field. + type: string + sold_time: + description: SoldTime holds the value of the "sold_time" field. + type: string + sold_to: + description: SoldTo holds the value of the "sold_to" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + type: object + ent.ItemEdges: + properties: + fields: + description: Fields holds the value of the fields edge. + items: + $ref: '#/definitions/ent.ItemField' + type: array + group: + $ref: '#/definitions/ent.Group' + description: Group holds the value of the group edge. + label: + description: Label holds the value of the label edge. + items: + $ref: '#/definitions/ent.Label' + type: array + location: + $ref: '#/definitions/ent.Location' + description: Location holds the value of the location edge. + type: object + ent.ItemField: + properties: + boolean_value: + description: BooleanValue holds the value of the "boolean_value" field. + type: boolean + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + $ref: '#/definitions/ent.ItemFieldEdges' + description: |- + Edges holds the relations/edges for other nodes in the graph. + The values are being populated by the ItemFieldQuery when eager-loading is set. + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + number_value: + description: NumberValue holds the value of the "number_value" field. + type: integer + text_value: + description: TextValue holds the value of the "text_value" field. + type: string + time_value: + description: TimeValue holds the value of the "time_value" field. + type: string + type: + description: Type holds the value of the "type" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + type: object + ent.ItemFieldEdges: + properties: + item: + $ref: '#/definitions/ent.Item' + description: Item holds the value of the item edge. + type: object + ent.Label: + properties: + color: + description: Color holds the value of the "color" field. + type: string + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + $ref: '#/definitions/ent.LabelEdges' + description: |- + Edges holds the relations/edges for other nodes in the graph. + The values are being populated by the LabelQuery when eager-loading is set. + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + type: object + ent.LabelEdges: + properties: + group: + $ref: '#/definitions/ent.Group' + description: Group holds the value of the group edge. + items: + description: Items holds the value of the items edge. + items: + $ref: '#/definitions/ent.Item' + type: array + type: object + ent.Location: + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + description: + description: Description holds the value of the "description" field. + type: string + edges: + $ref: '#/definitions/ent.LocationEdges' + description: |- + Edges holds the relations/edges for other nodes in the graph. + The values are being populated by the LocationQuery when eager-loading is set. + id: + description: ID of the ent. + type: string + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + type: object + ent.LocationEdges: + properties: + group: + $ref: '#/definitions/ent.Group' + description: Group holds the value of the group edge. + items: + description: Items holds the value of the items edge. + items: + $ref: '#/definitions/ent.Item' + type: array + type: object + ent.User: + properties: + created_at: + description: CreatedAt holds the value of the "created_at" field. + type: string + edges: + $ref: '#/definitions/ent.UserEdges' + description: |- + Edges holds the relations/edges for other nodes in the graph. + The values are being populated by the UserQuery when eager-loading is set. + email: + description: Email holds the value of the "email" field. + type: string + id: + description: ID of the ent. + type: string + is_superuser: + description: IsSuperuser holds the value of the "is_superuser" field. + type: boolean + name: + description: Name holds the value of the "name" field. + type: string + updated_at: + description: UpdatedAt holds the value of the "updated_at" field. + type: string + type: object + ent.UserEdges: + properties: + auth_tokens: + description: AuthTokens holds the value of the auth_tokens edge. + items: + $ref: '#/definitions/ent.AuthTokens' + type: array + group: + $ref: '#/definitions/ent.Group' + description: Group holds the value of the group edge. + type: object server.Result: properties: details: {} @@ -33,6 +344,8 @@ definitions: properties: email: type: string + groupID: + type: string isSuperuser: type: boolean name: @@ -40,16 +353,21 @@ definitions: password: type: string type: object - types.UserOut: + types.UserIn: properties: email: type: string - id: - type: string - isSuperuser: - type: boolean name: type: string + password: + type: string + type: object + types.UserRegistration: + properties: + groupName: + type: string + user: + $ref: '#/definitions/types.UserIn' type: object types.UserUpdate: properties: @@ -99,7 +417,7 @@ paths: - properties: item: items: - $ref: '#/definitions/types.UserOut' + $ref: '#/definitions/ent.User' type: array type: object security: @@ -125,7 +443,7 @@ paths: - $ref: '#/definitions/server.Result' - properties: item: - $ref: '#/definitions/types.UserOut' + $ref: '#/definitions/ent.User' type: object security: - Bearer: [] @@ -167,7 +485,7 @@ paths: - $ref: '#/definitions/server.Result' - properties: item: - $ref: '#/definitions/types.UserOut' + $ref: '#/definitions/ent.User' type: object security: - Bearer: [] @@ -197,7 +515,7 @@ paths: - $ref: '#/definitions/server.Result' - properties: item: - $ref: '#/definitions/types.UserOut' + $ref: '#/definitions/ent.User' type: object security: - Bearer: [] @@ -253,6 +571,30 @@ paths: summary: User Token Refresh tags: - Authentication + /v1/users/register: + post: + parameters: + - description: User Data + in: body + name: payload + required: true + schema: + $ref: '#/definitions/types.UserRegistration' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/server.Result' + - properties: + item: + $ref: '#/definitions/ent.User' + type: object + summary: Get the current user + tags: + - User /v1/users/self: get: produces: @@ -265,7 +607,7 @@ paths: - $ref: '#/definitions/server.Result' - properties: item: - $ref: '#/definitions/types.UserOut' + $ref: '#/definitions/ent.User' type: object security: - Bearer: [] diff --git a/backend/app/api/main.go b/backend/app/api/main.go index cfd59fb..29f0c22 100644 --- a/backend/app/api/main.go +++ b/backend/app/api/main.go @@ -97,7 +97,6 @@ func run(cfg *config.Config) error { routes := app.newRouter(app.repos) app.LogRoutes(routes) - app.EnsureAdministrator() app.SeedDatabase(app.repos) app.logger.Info("Starting HTTP Server", logger.Props{ diff --git a/backend/app/api/middleware.go b/backend/app/api/middleware.go index 3e2da68..d10ab21 100644 --- a/backend/app/api/middleware.go +++ b/backend/app/api/middleware.go @@ -64,7 +64,7 @@ func (a *app) mwAuthToken(next http.Handler) http.Handler { return } - r = r.WithContext(services.SetUserCtx(r.Context(), &usr, requestToken)) + r = r.WithContext(services.SetUserCtx(r.Context(), usr, requestToken)) next.ServeHTTP(w, r) }) diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index b784c26..0fed8b2 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -36,26 +36,30 @@ func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux { // ========================================================================= // API Version 1 - v1Base := v1.BaseUrlFunc(prefix) - v1Handlers := v1.NewControllerV1(a.logger, a.services) - r.Post(v1Base("/users/login"), v1Handlers.HandleAuthLogin()) - r.Group(func(r chi.Router) { - r.Use(a.mwAuthToken) - r.Get(v1Base("/users/self"), v1Handlers.HandleUserSelf()) - r.Put(v1Base("/users/self"), v1Handlers.HandleUserUpdate()) - r.Put(v1Base("/users/self/password"), v1Handlers.HandleUserUpdatePassword()) - r.Post(v1Base("/users/logout"), v1Handlers.HandleAuthLogout()) - r.Get(v1Base("/users/refresh"), v1Handlers.HandleAuthRefresh()) - }) - r.Group(func(r chi.Router) { - r.Use(a.mwAdminOnly) - r.Get(v1Base("/admin/users"), v1Handlers.HandleAdminUserGetAll()) - r.Post(v1Base("/admin/users"), v1Handlers.HandleAdminUserCreate()) - r.Get(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserGet()) - r.Put(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserUpdate()) - r.Delete(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserDelete()) - }) + v1Base := v1.BaseUrlFunc(prefix) + { + v1Handlers := v1.NewControllerV1(a.logger, a.services) + r.Post(v1Base("/users/register"), v1Handlers.HandleUserRegistration()) + r.Post(v1Base("/users/login"), v1Handlers.HandleAuthLogin()) + r.Group(func(r chi.Router) { + r.Use(a.mwAuthToken) + r.Get(v1Base("/users/self"), v1Handlers.HandleUserSelf()) + r.Put(v1Base("/users/self"), v1Handlers.HandleUserUpdate()) + r.Put(v1Base("/users/self/password"), v1Handlers.HandleUserUpdatePassword()) + r.Post(v1Base("/users/logout"), v1Handlers.HandleAuthLogout()) + r.Get(v1Base("/users/refresh"), v1Handlers.HandleAuthRefresh()) + }) + + r.Group(func(r chi.Router) { + r.Use(a.mwAdminOnly) + r.Get(v1Base("/admin/users"), v1Handlers.HandleAdminUserGetAll()) + r.Post(v1Base("/admin/users"), v1Handlers.HandleAdminUserCreate()) + r.Get(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserGet()) + r.Put(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserUpdate()) + r.Delete(v1Base("/admin/users/{id}"), v1Handlers.HandleAdminUserDelete()) + }) + } return r } diff --git a/backend/app/api/seed.go b/backend/app/api/seed.go index 7d66774..42c3600 100644 --- a/backend/app/api/seed.go +++ b/backend/app/api/seed.go @@ -11,6 +11,7 @@ import ( ) const ( + DefaultGroup = "Default" DefaultName = "Admin" DefaultEmail = "admin@admin.com" DefaultPassword = "admin" @@ -22,15 +23,13 @@ func (a *app) EnsureAdministrator() { superusers, err := a.repos.Users.GetSuperusers(context.Background()) if err != nil { - a.logger.Error(err, nil) + a.logger.Fatal(err, nil) } - if len(superusers) > 0 { return } pw, _ := hasher.HashPassword(DefaultPassword) - newSuperUser := types.UserCreate{ Name: DefaultName, Email: DefaultEmail, @@ -56,6 +55,11 @@ func (a *app) SeedDatabase(repos *repo.AllRepos) { return } + group, err := repos.Groups.Create(context.Background(), DefaultGroup) + if err != nil { + a.logger.Fatal(err, nil) + } + for _, user := range a.conf.Seed.Users { // Check if User Exists @@ -82,6 +86,7 @@ func (a *app) SeedDatabase(repos *repo.AllRepos) { Email: user.Email, IsSuperuser: user.IsSuperuser, Password: hashedPw, + GroupID: group.ID, }) if err != nil { diff --git a/backend/app/api/v1/main_test.go b/backend/app/api/v1/main_test.go index 3713dee..43c7132 100644 --- a/backend/app/api/v1/main_test.go +++ b/backend/app/api/v1/main_test.go @@ -4,13 +4,14 @@ import ( "context" "testing" + "github.com/hay-kot/content/backend/ent" "github.com/hay-kot/content/backend/internal/mocks" "github.com/hay-kot/content/backend/internal/mocks/factories" "github.com/hay-kot/content/backend/internal/types" ) var mockHandler = &V1Controller{} -var users = []types.UserOut{} +var users = []*ent.User{} func userPool() func() { create := []types.UserCreate{ @@ -20,7 +21,7 @@ func userPool() func() { factories.UserFactory(), } - userOut := []types.UserOut{} + userOut := []*ent.User{} for _, user := range create { usrOut, _ := mockHandler.svc.Admin.Create(context.Background(), user) diff --git a/backend/app/api/v1/v1_ctrl_admin.go b/backend/app/api/v1/v1_ctrl_admin.go index 4961c6b..a67a6f2 100644 --- a/backend/app/api/v1/v1_ctrl_admin.go +++ b/backend/app/api/v1/v1_ctrl_admin.go @@ -17,7 +17,7 @@ import ( // @Summary Gets all users from the database // @Tags Admin: Users // @Produce json -// @Success 200 {object} server.Result{item=[]types.UserOut} +// @Success 200 {object} server.Result{item=[]ent.User} // @Router /v1/admin/users [get] // @Security Bearer func (ctrl *V1Controller) HandleAdminUserGetAll() http.HandlerFunc { @@ -38,7 +38,7 @@ func (ctrl *V1Controller) HandleAdminUserGetAll() http.HandlerFunc { // @Tags Admin: Users // @Produce json // @Param id path string true "User ID" -// @Success 200 {object} server.Result{item=types.UserOut} +// @Success 200 {object} server.Result{item=ent.User} // @Router /v1/admin/users/{id} [get] // @Security Bearer func (ctrl *V1Controller) HandleAdminUserGet() http.HandlerFunc { @@ -71,7 +71,7 @@ func (ctrl *V1Controller) HandleAdminUserGet() http.HandlerFunc { // @Tags Admin: Users // @Produce json // @Param payload body types.UserCreate true "User Data" -// @Success 200 {object} server.Result{item=types.UserOut} +// @Success 200 {object} server.Result{item=ent.User} // @Router /v1/admin/users [POST] // @Security Bearer func (ctrl *V1Controller) HandleAdminUserCreate() http.HandlerFunc { @@ -129,7 +129,7 @@ func (ctrl *V1Controller) HandleAdminUserCreate() http.HandlerFunc { // @Param id path string true "User ID" // @Param payload body types.UserUpdate true "User Data" // @Produce json -// @Success 200 {object} server.Result{item=types.UserOut} +// @Success 200 {object} server.Result{item=ent.User} // @Router /v1/admin/users/{id} [PUT] // @Security Bearer func (ctrl *V1Controller) HandleAdminUserUpdate() http.HandlerFunc { diff --git a/backend/app/api/v1/v1_ctrl_admin_test.go b/backend/app/api/v1/v1_ctrl_admin_test.go index 6c0d8e9..b85cfec 100644 --- a/backend/app/api/v1/v1_ctrl_admin_test.go +++ b/backend/app/api/v1/v1_ctrl_admin_test.go @@ -9,9 +9,9 @@ import ( "net/http/httptest" "testing" + "github.com/hay-kot/content/backend/ent" "github.com/hay-kot/content/backend/internal/mocks/chimocker" "github.com/hay-kot/content/backend/internal/mocks/factories" - "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/server" "github.com/stretchr/testify/assert" ) @@ -23,11 +23,11 @@ const ( ) type usersResponse struct { - Users []types.UserOut `json:"item"` + Users []*ent.User `json:"item"` } type userResponse struct { - User types.UserOut `json:"item"` + User *ent.User `json:"item"` } func Test_HandleAdminUserGetAll_Success(t *testing.T) { @@ -37,7 +37,7 @@ func Test_HandleAdminUserGetAll_Success(t *testing.T) { mockHandler.HandleAdminUserGetAll()(r, req) response := usersResponse{ - Users: []types.UserOut{}, + Users: []*ent.User{}, } _ = json.Unmarshal(r.Body.Bytes(), &response) @@ -68,7 +68,7 @@ func Test_HandleAdminUserGet_Success(t *testing.T) { assert.Equal(t, http.StatusOK, res.Code) response := userResponse{ - User: types.UserOut{}, + User: &ent.User{}, } _ = json.Unmarshal(res.Body.Bytes(), &response) diff --git a/backend/app/api/v1/v1_ctrl_user.go b/backend/app/api/v1/v1_ctrl_user.go index 68c6be2..a3371c2 100644 --- a/backend/app/api/v1/v1_ctrl_user.go +++ b/backend/app/api/v1/v1_ctrl_user.go @@ -4,24 +4,53 @@ import ( "errors" "net/http" + "github.com/google/uuid" "github.com/hay-kot/content/backend/internal/services" "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/logger" "github.com/hay-kot/content/backend/pkgs/server" ) +// HandleUserSelf godoc +// @Summary Get the current user +// @Tags User +// @Produce json +// @Param payload body types.UserRegistration true "User Data" +// @Success 200 {object} server.Result{item=ent.User} +// @Router /v1/users/register [Post] +func (ctrl *V1Controller) HandleUserRegistration() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + regData := types.UserRegistration{} + + if err := server.Decode(r, ®Data); err != nil { + ctrl.log.Error(err, nil) + server.RespondError(w, http.StatusInternalServerError, err) + return + } + + usr, err := ctrl.svc.User.RegisterUser(r.Context(), regData) + if err != nil { + ctrl.log.Error(err, nil) + server.RespondError(w, http.StatusInternalServerError, err) + return + } + + _ = server.Respond(w, http.StatusOK, server.Wrap(usr)) + } +} + // HandleUserSelf godoc // @Summary Get the current user // @Tags User // @Produce json -// @Success 200 {object} server.Result{item=types.UserOut} +// @Success 200 {object} server.Result{item=ent.User} // @Router /v1/users/self [GET] // @Security Bearer func (ctrl *V1Controller) HandleUserSelf() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { token := services.UseTokenCtx(r.Context()) usr, err := ctrl.svc.User.GetSelf(r.Context(), token) - if usr.IsNull() || err != nil { + if usr.ID == uuid.Nil || err != nil { ctrl.log.Error(errors.New("no user within request context"), nil) server.RespondInternalServerError(w) return diff --git a/backend/app/generator/main.go b/backend/app/generator/main.go index 3cc45a6..8caf35c 100644 --- a/backend/app/generator/main.go +++ b/backend/app/generator/main.go @@ -4,7 +4,6 @@ import ( "time" "github.com/google/uuid" - "github.com/hay-kot/content/backend/ent" "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/automapper" "github.com/tkrajina/typescriptify-golang-structs/typescriptify" @@ -12,22 +11,7 @@ import ( // generateMappers serialized the config file into a list of automapper struct func generateMappers() []automapper.AutoMapper { - return []automapper.AutoMapper{ - { - Package: "mapper", - Prefix: "users", - Name: "User Out", - Schema: automapper.Schema{ - Type: types.UserOut{}, - Prefix: "types", - }, - Model: automapper.Model{ - Type: ent.User{}, - Prefix: "ent", - }, - Imports: []string{}, - }, - } + return []automapper.AutoMapper{} } func generateTypeScript() { @@ -43,7 +27,6 @@ func generateTypeScript() { types.ApiSummary{}, // User Types - types.UserOut{}, types.UserCreate{}, types.UserIn{}, types.UserUpdate{}, diff --git a/backend/config.template.yml b/backend/config.template.yml index 0dc2626..d80911b 100644 --- a/backend/config.template.yml +++ b/backend/config.template.yml @@ -20,6 +20,7 @@ mailer: from: example@email.com seed: enabled: true + group: Default users: - name: Admin email: admin@admin.com diff --git a/backend/go.sum b/backend/go.sum index cc23f5f..2ad1220 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -53,14 +53,18 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/backend/internal/config/conf_seed.go b/backend/internal/config/conf_seed.go index e076593..67a409a 100644 --- a/backend/internal/config/conf_seed.go +++ b/backend/internal/config/conf_seed.go @@ -10,4 +10,5 @@ type SeedUser struct { type Seed struct { Enabled bool `yaml:"enabled" conf:"default:false"` Users []SeedUser `yaml:"users"` + Group string `yaml:"group"` } diff --git a/backend/internal/mapper/users_automapper.go b/backend/internal/mapper/users_automapper.go deleted file mode 100644 index 78392f1..0000000 --- a/backend/internal/mapper/users_automapper.go +++ /dev/null @@ -1,27 +0,0 @@ -// Code generated by "/pkgs/automapper"; DO NOT EDIT. -package mapper - -import ( - "github.com/hay-kot/content/backend/ent" - "github.com/hay-kot/content/backend/internal/types" -) - -func UserOutFromModel(from ent.User) types.UserOut { - return types.UserOut{ - ID: from.ID, - Name: from.Name, - Email: from.Email, - Password: from.Password, - IsSuperuser: from.IsSuperuser, - } -} - -func UserOutToModel(from types.UserOut) ent.User { - return ent.User{ - ID: from.ID, - Name: from.Name, - Email: from.Email, - Password: from.Password, - IsSuperuser: from.IsSuperuser, - } -} diff --git a/backend/internal/repo/repo_group.go b/backend/internal/repo/repo_group.go new file mode 100644 index 0000000..85915f5 --- /dev/null +++ b/backend/internal/repo/repo_group.go @@ -0,0 +1,29 @@ +package repo + +import ( + "context" + + "github.com/google/uuid" + "github.com/hay-kot/content/backend/ent" +) + +type EntGroupRepository struct { + db *ent.Client +} + +func (r *EntGroupRepository) Create(ctx context.Context, name string) (*ent.Group, error) { + dbGroup, err := r.db.Group.Create().SetName(name).Save(ctx) + + if err != nil { + return dbGroup, err + } + return dbGroup, nil +} + +func (r *EntGroupRepository) GetOneId(ctx context.Context, id uuid.UUID) (*ent.Group, error) { + dbGroup, err := r.db.Group.Get(ctx, id) + if err != nil { + return dbGroup, err + } + return dbGroup, nil +} diff --git a/backend/internal/repo/token_ent.go b/backend/internal/repo/repo_tokens.go similarity index 90% rename from backend/internal/repo/token_ent.go rename to backend/internal/repo/repo_tokens.go index 7f3807d..a1e292c 100644 --- a/backend/internal/repo/token_ent.go +++ b/backend/internal/repo/repo_tokens.go @@ -6,7 +6,6 @@ import ( "github.com/hay-kot/content/backend/ent" "github.com/hay-kot/content/backend/ent/authtokens" - "github.com/hay-kot/content/backend/internal/mapper" "github.com/hay-kot/content/backend/internal/types" ) @@ -15,7 +14,7 @@ type EntTokenRepository struct { } // GetUserFromToken get's a user from a token -func (r *EntTokenRepository) GetUserFromToken(ctx context.Context, token []byte) (types.UserOut, error) { +func (r *EntTokenRepository) GetUserFromToken(ctx context.Context, token []byte) (*ent.User, error) { dbToken, err := r.db.AuthTokens.Query(). Where(authtokens.Token(token)). Where(authtokens.ExpiresAtGTE(time.Now())). @@ -23,10 +22,10 @@ func (r *EntTokenRepository) GetUserFromToken(ctx context.Context, token []byte) Only(ctx) if err != nil { - return types.UserOut{}, err + return nil, err } - return mapper.UserOutFromModel(*dbToken.Edges.User), nil + return dbToken.Edges.User, nil } // Creates a token for a user diff --git a/backend/internal/repo/token_ent_test.go b/backend/internal/repo/repo_tokens_test.go similarity index 100% rename from backend/internal/repo/token_ent_test.go rename to backend/internal/repo/repo_tokens_test.go diff --git a/backend/internal/repo/users_ent.go b/backend/internal/repo/repo_users.go similarity index 63% rename from backend/internal/repo/users_ent.go rename to backend/internal/repo/repo_users.go index 632c0fb..8a68231 100644 --- a/backend/internal/repo/users_ent.go +++ b/backend/internal/repo/repo_users.go @@ -13,66 +13,41 @@ type EntUserRepository struct { db *ent.Client } -func (e *EntUserRepository) toUserOut(usr *types.UserOut, entUsr *ent.User) { - usr.ID = entUsr.ID - usr.Password = entUsr.Password - usr.Name = entUsr.Name - usr.Email = entUsr.Email - usr.IsSuperuser = entUsr.IsSuperuser -} - -func (e *EntUserRepository) GetOneId(ctx context.Context, id uuid.UUID) (types.UserOut, error) { +func (e *EntUserRepository) GetOneId(ctx context.Context, id uuid.UUID) (*ent.User, error) { usr, err := e.db.User.Query().Where(user.ID(id)).Only(ctx) - usrOut := types.UserOut{} - if err != nil { - return usrOut, err + return usr, err } - e.toUserOut(&usrOut, usr) - - return usrOut, nil + return usr, nil } -func (e *EntUserRepository) GetOneEmail(ctx context.Context, email string) (types.UserOut, error) { +func (e *EntUserRepository) GetOneEmail(ctx context.Context, email string) (*ent.User, error) { usr, err := e.db.User.Query().Where(user.Email(email)).Only(ctx) - usrOut := types.UserOut{} - if err != nil { - return usrOut, err + return usr, err } - e.toUserOut(&usrOut, usr) - - return usrOut, nil + return usr, nil } -func (e *EntUserRepository) GetAll(ctx context.Context) ([]types.UserOut, error) { +func (e *EntUserRepository) GetAll(ctx context.Context) ([]*ent.User, error) { users, err := e.db.User.Query().All(ctx) if err != nil { return nil, err } - var usrs []types.UserOut - - for _, usr := range users { - usrOut := types.UserOut{} - e.toUserOut(&usrOut, usr) - usrs = append(usrs, usrOut) - } - - return usrs, nil + return users, nil } -func (e *EntUserRepository) Create(ctx context.Context, usr types.UserCreate) (types.UserOut, error) { +func (e *EntUserRepository) Create(ctx context.Context, usr types.UserCreate) (*ent.User, error) { err := usr.Validate() - usrOut := types.UserOut{} if err != nil { - return usrOut, err + return &ent.User{}, err } entUser, err := e.db.User. @@ -81,11 +56,10 @@ func (e *EntUserRepository) Create(ctx context.Context, usr types.UserCreate) (t SetEmail(usr.Email). SetPassword(usr.Password). SetIsSuperuser(usr.IsSuperuser). + SetGroupID(usr.GroupID). Save(ctx) - e.toUserOut(&usrOut, entUser) - - return usrOut, err + return entUser, err } func (e *EntUserRepository) Update(ctx context.Context, ID uuid.UUID, data types.UserUpdate) error { @@ -122,20 +96,12 @@ func (e *EntUserRepository) DeleteAll(ctx context.Context) error { return err } -func (e *EntUserRepository) GetSuperusers(ctx context.Context) ([]types.UserOut, error) { +func (e *EntUserRepository) GetSuperusers(ctx context.Context) ([]*ent.User, error) { users, err := e.db.User.Query().Where(user.IsSuperuser(true)).All(ctx) if err != nil { return nil, err } - var usrs []types.UserOut - - for _, usr := range users { - usrOut := types.UserOut{} - e.toUserOut(&usrOut, usr) - usrs = append(usrs, usrOut) - } - - return usrs, nil + return users, nil } diff --git a/backend/internal/repo/users_ent_test.go b/backend/internal/repo/repo_users_test.go similarity index 97% rename from backend/internal/repo/users_ent_test.go rename to backend/internal/repo/repo_users_test.go index 2bf9687..6740733 100644 --- a/backend/internal/repo/users_ent_test.go +++ b/backend/internal/repo/repo_users_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/hay-kot/content/backend/ent" "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/faker" "github.com/stretchr/testify/assert" @@ -66,7 +67,7 @@ func Test_EntUserRepo_GetAll(t *testing.T) { ctx := context.Background() - created := []types.UserOut{} + created := []*ent.User{} for _, usr := range toCreate { usrOut, _ := testRepos.Users.Create(ctx, usr) diff --git a/backend/internal/repo/repos_all.go b/backend/internal/repo/repos_all.go index 7703aa3..4a7b324 100644 --- a/backend/internal/repo/repos_all.go +++ b/backend/internal/repo/repos_all.go @@ -4,13 +4,15 @@ import "github.com/hay-kot/content/backend/ent" // AllRepos is a container for all the repository interfaces type AllRepos struct { - Users UserRepository - AuthTokens TokenRepository + Users *EntUserRepository + AuthTokens *EntTokenRepository + Groups *EntGroupRepository } func EntAllRepos(db *ent.Client) *AllRepos { return &AllRepos{ Users: &EntUserRepository{db}, AuthTokens: &EntTokenRepository{db}, + Groups: &EntGroupRepository{db}, } } diff --git a/backend/internal/repo/token_interface.go b/backend/internal/repo/token_interface.go deleted file mode 100644 index f610d9d..0000000 --- a/backend/internal/repo/token_interface.go +++ /dev/null @@ -1,20 +0,0 @@ -package repo - -import ( - "context" - - "github.com/hay-kot/content/backend/internal/types" -) - -type TokenRepository interface { - // GetUserFromToken get's a user from a token - GetUserFromToken(ctx context.Context, token []byte) (types.UserOut, error) - // Creates a token for a user - CreateToken(ctx context.Context, createToken types.UserAuthTokenCreate) (types.UserAuthToken, error) - // DeleteToken remove a single token from the database - equivalent to revoke or logout - DeleteToken(ctx context.Context, token []byte) error - // PurgeExpiredTokens removes all expired tokens from the database - PurgeExpiredTokens(ctx context.Context) (int, error) - // DeleteAll removes all tokens from the database - DeleteAll(ctx context.Context) (int, error) -} diff --git a/backend/internal/repo/users_interface.go b/backend/internal/repo/users_interface.go deleted file mode 100644 index 2e594ba..0000000 --- a/backend/internal/repo/users_interface.go +++ /dev/null @@ -1,27 +0,0 @@ -package repo - -import ( - "context" - - "github.com/google/uuid" - "github.com/hay-kot/content/backend/internal/types" -) - -type UserRepository interface { - // GetOneId returns a user by id - GetOneId(ctx context.Context, ID uuid.UUID) (types.UserOut, error) - // GetOneEmail returns a user by email - GetOneEmail(ctx context.Context, email string) (types.UserOut, error) - // GetAll returns all users - GetAll(ctx context.Context) ([]types.UserOut, error) - // Get Super Users - GetSuperusers(ctx context.Context) ([]types.UserOut, error) - // Create creates a new user - Create(ctx context.Context, user types.UserCreate) (types.UserOut, error) - // Update updates a user - Update(ctx context.Context, ID uuid.UUID, user types.UserUpdate) error - // Delete deletes a user - Delete(ctx context.Context, ID uuid.UUID) error - - DeleteAll(ctx context.Context) error -} diff --git a/backend/internal/services/contexts.go b/backend/internal/services/contexts.go index bc18fcc..6411f5c 100644 --- a/backend/internal/services/contexts.go +++ b/backend/internal/services/contexts.go @@ -3,7 +3,7 @@ package services import ( "context" - "github.com/hay-kot/content/backend/internal/types" + "github.com/hay-kot/content/backend/ent" ) type contextKeys struct { @@ -17,16 +17,16 @@ var ( // SetUserCtx is a helper function that sets the ContextUser and ContextUserToken // values within the context of a web request (or any context). -func SetUserCtx(ctx context.Context, user *types.UserOut, token string) context.Context { +func SetUserCtx(ctx context.Context, user *ent.User, token string) context.Context { ctx = context.WithValue(ctx, ContextUser, user) ctx = context.WithValue(ctx, ContextUserToken, token) return ctx } // UseUserCtx is a helper function that returns the user from the context. -func UseUserCtx(ctx context.Context) *types.UserOut { +func UseUserCtx(ctx context.Context) *ent.User { if val := ctx.Value(ContextUser); val != nil { - return val.(*types.UserOut) + return val.(*ent.User) } return nil } diff --git a/backend/internal/services/contexts_test.go b/backend/internal/services/contexts_test.go index cf5a862..251baad 100644 --- a/backend/internal/services/contexts_test.go +++ b/backend/internal/services/contexts_test.go @@ -5,12 +5,12 @@ import ( "testing" "github.com/google/uuid" - "github.com/hay-kot/content/backend/internal/types" + "github.com/hay-kot/content/backend/ent" "github.com/stretchr/testify/assert" ) func Test_SetAuthContext(t *testing.T) { - user := &types.UserOut{ + user := &ent.User{ ID: uuid.New(), } diff --git a/backend/internal/services/service_admin.go b/backend/internal/services/service_admin.go index 6fda029..8be4f68 100644 --- a/backend/internal/services/service_admin.go +++ b/backend/internal/services/service_admin.go @@ -4,6 +4,7 @@ import ( "context" "github.com/google/uuid" + "github.com/hay-kot/content/backend/ent" "github.com/hay-kot/content/backend/internal/repo" "github.com/hay-kot/content/backend/internal/types" ) @@ -12,27 +13,27 @@ type AdminService struct { repos *repo.AllRepos } -func (svc *AdminService) Create(ctx context.Context, usr types.UserCreate) (types.UserOut, error) { +func (svc *AdminService) Create(ctx context.Context, usr types.UserCreate) (*ent.User, error) { return svc.repos.Users.Create(ctx, usr) } -func (svc *AdminService) GetAll(ctx context.Context) ([]types.UserOut, error) { +func (svc *AdminService) GetAll(ctx context.Context) ([]*ent.User, error) { return svc.repos.Users.GetAll(ctx) } -func (svc *AdminService) GetByID(ctx context.Context, id uuid.UUID) (types.UserOut, error) { +func (svc *AdminService) GetByID(ctx context.Context, id uuid.UUID) (*ent.User, error) { return svc.repos.Users.GetOneId(ctx, id) } -func (svc *AdminService) GetByEmail(ctx context.Context, email string) (types.UserOut, error) { +func (svc *AdminService) GetByEmail(ctx context.Context, email string) (*ent.User, error) { return svc.repos.Users.GetOneEmail(ctx, email) } -func (svc *AdminService) UpdateProperties(ctx context.Context, ID uuid.UUID, data types.UserUpdate) (types.UserOut, error) { +func (svc *AdminService) UpdateProperties(ctx context.Context, ID uuid.UUID, data types.UserUpdate) (*ent.User, error) { err := svc.repos.Users.Update(ctx, ID, data) if err != nil { - return types.UserOut{}, err + return &ent.User{}, err } return svc.repos.Users.GetOneId(ctx, ID) diff --git a/backend/internal/services/service_user.go b/backend/internal/services/service_user.go index 0bb8d5f..de1cb27 100644 --- a/backend/internal/services/service_user.go +++ b/backend/internal/services/service_user.go @@ -6,6 +6,7 @@ import ( "time" "github.com/google/uuid" + "github.com/hay-kot/content/backend/ent" "github.com/hay-kot/content/backend/internal/repo" "github.com/hay-kot/content/backend/internal/types" "github.com/hay-kot/content/backend/pkgs/hasher" @@ -22,17 +23,41 @@ type UserService struct { repos *repo.AllRepos } +func (svc *UserService) RegisterUser(ctx context.Context, data types.UserRegistration) (*ent.User, error) { + group, err := svc.repos.Groups.Create(ctx, data.GroupName) + if err != nil { + return &ent.User{}, err + } + + hashed, _ := hasher.HashPassword(data.User.Password) + + usrCreate := types.UserCreate{ + Name: data.User.Name, + Email: data.User.Email, + Password: hashed, + IsSuperuser: false, + GroupID: group.ID, + } + + usr, err := svc.repos.Users.Create(ctx, usrCreate) + if err != nil { + return &ent.User{}, err + } + + return usr, nil +} + // GetSelf returns the user that is currently logged in based of the token provided within -func (svc *UserService) GetSelf(ctx context.Context, requestToken string) (types.UserOut, error) { +func (svc *UserService) GetSelf(ctx context.Context, requestToken string) (*ent.User, error) { hash := hasher.HashToken(requestToken) return svc.repos.AuthTokens.GetUserFromToken(ctx, hash) } -func (svc *UserService) UpdateSelf(ctx context.Context, ID uuid.UUID, data types.UserUpdate) (types.UserOut, error) { +func (svc *UserService) UpdateSelf(ctx context.Context, ID uuid.UUID, data types.UserUpdate) (*ent.User, error) { err := svc.repos.Users.Update(ctx, ID, data) if err != nil { - return types.UserOut{}, err + return &ent.User{}, err } return svc.repos.Users.GetOneId(ctx, ID) diff --git a/backend/internal/types/users_types.go b/backend/internal/types/users_types.go index db1c404..81cc932 100644 --- a/backend/internal/types/users_types.go +++ b/backend/internal/types/users_types.go @@ -23,10 +23,11 @@ type UserIn struct { // in the database. It should to create users from an API unless the user has // rights to create SuperUsers. For regular user in data use the UserIn struct. type UserCreate struct { - Name string `json:"name"` - Email string `json:"email"` - Password string `json:"password"` - IsSuperuser bool `json:"isSuperuser"` + Name string `json:"name"` + Email string `json:"email"` + Password string `json:"password"` + IsSuperuser bool `json:"isSuperuser"` + GroupID uuid.UUID `json:"groupID"` } func (u *UserCreate) Validate() error { @@ -39,20 +40,12 @@ func (u *UserCreate) Validate() error { return nil } -type UserOut struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - Password string `json:"-"` - IsSuperuser bool `json:"isSuperuser"` -} - -// IsNull is a proxy call for `usr.Id == uuid.Nil` -func (usr *UserOut) IsNull() bool { - return usr.ID == uuid.Nil -} - type UserUpdate struct { Name *string `json:"name"` Email *string `json:"email"` } + +type UserRegistration struct { + User UserIn `json:"user"` + GroupName string `json:"groupName"` +} diff --git a/backend/internal/types/users_types_test.go b/backend/internal/types/users_types_test.go index bc3b825..9f4a942 100644 --- a/backend/internal/types/users_types_test.go +++ b/backend/internal/types/users_types_test.go @@ -2,9 +2,6 @@ package types import ( "testing" - - "github.com/google/uuid" - "github.com/stretchr/testify/assert" ) func TestUserCreate_Validate(t *testing.T) { @@ -64,13 +61,3 @@ func TestUserCreate_Validate(t *testing.T) { }) } } - -func TestUserOut_IsNull(t *testing.T) { - nullUser := UserOut{} - - assert.True(t, nullUser.IsNull()) - - nullUser.ID = uuid.New() - - assert.False(t, nullUser.IsNull()) -}