├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── crud_users.http ├── docker-compose.yml ├── docs ├── docs.go ├── swagger.json └── swagger.yaml ├── go.mod ├── go.sum ├── init_dependencies.go ├── main.go └── src ├── configuration ├── database │ └── mongodb │ │ └── mongodb_connection.go ├── logger │ └── logger.go ├── rest_err │ └── rest_err.go └── validation │ └── validate_user.go ├── controller ├── create_user.go ├── create_user_test.go ├── delete_user.go ├── delete_user_test.go ├── find_user.go ├── find_user_test.go ├── login_user.go ├── login_user_test.go ├── model │ ├── request │ │ ├── user_login.go │ │ └── user_request.go │ └── response │ │ └── user_response.go ├── routes │ └── routes.go ├── update_user.go ├── update_user_test.go └── user_controller.go ├── model ├── repository │ ├── create_user_repository.go │ ├── create_user_repository_test.go │ ├── delete_user_repository.go │ ├── delete_user_repository_test.go │ ├── entity │ │ ├── converter │ │ │ ├── convert_domain_to_entity.go │ │ │ └── convert_entity_to_domain.go │ │ └── user_entity.go │ ├── find_user_repository.go │ ├── find_user_repository_test.go │ ├── update_user_repository.go │ ├── update_user_repository_test.go │ └── user_repository.go ├── service │ ├── create_user.go │ ├── create_user_test.go │ ├── delete_user.go │ ├── delete_user_test.go │ ├── find_user.go │ ├── find_user_test.go │ ├── login_user.go │ ├── login_user_test.go │ ├── update_user.go │ ├── update_user_test.go │ └── user_interface.go ├── user_domain.go ├── user_domain_interface.go ├── user_domain_password.go └── user_token_domain.go ├── tests ├── connection │ └── open_connection.go ├── create_user_test.go ├── delete_user_test.go ├── find_user_test.go ├── login_user_test.go ├── mocks │ ├── user_domain_interface_mock.go │ ├── user_repository_mock.go │ └── user_service_mock.go └── update_user_test.go └── view └── convert_domain_to_response.go /.env: -------------------------------------------------------------------------------- 1 | TEST=TEST 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19 AS BUILDER 2 | 3 | 4 | WORKDIR /app 5 | COPY src src 6 | COPY docs docs 7 | COPY go.mod go.mod 8 | COPY go.sum go.sum 9 | COPY init_dependencies.go init_dependencies.go 10 | COPY main.go main.go 11 | 12 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on \ 13 | GOOS=linux go build -o meuprimeirocrudgo . 14 | 15 | FROM golang:1.19-alpine3.15 as runner 16 | 17 | RUN adduser -D huncoding 18 | 19 | COPY --from=BUILDER /app/meuprimeirocrudgo /app/meuprimeirocrudgo 20 | 21 | RUN chown -R huncoding:huncoding /app 22 | RUN chmod +x /app/meuprimeirocrudgo 23 | 24 | EXPOSE 8080 25 | 26 | USER huncoding 27 | 28 | CMD ["./meuprimeirocrudgo"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MeuPrimeiroCRUD em Go 2 | 3 | This is a comprehensive guide for the "MeuPrimeiroCRUD em Go" project in Go, an example application that implements the basic CRUD (Create, Read, Update, Delete) operations for users. The project includes a Dockerfile to facilitate running it in containers. 4 | 5 | ## Information 6 | 7 | - **Title**: MeuPrimeiroCRUD em Go | HunCoding 8 | - **Version**: 1.0 9 | - **Host**: localhost:8080 10 | 11 | ## Prerequisites 12 | 13 | Before getting started, make sure you have the following prerequisites installed on your system: 14 | 15 | - [Go](https://golang.org/dl/): The Go programming language. 16 | - [Docker](https://www.docker.com/get-started): Docker is required if you wish to run the application in a container. 17 | 18 | ## Installation 19 | 20 | Follow the steps below to install the project in your development environment: 21 | 22 | 1. **Clone the repository:** 23 | 24 | ``` 25 | git clone https://github.com/HunCoding/meu-primeiro-crud-go.git 26 | ``` 27 | 28 | 2. **Navigate to the project directory:** 29 | 30 | ``` 31 | cd meu-primeiro-crud-go 32 | ``` 33 | 34 | 3. **Build the application using Docker Compose:** 35 | 36 | ``` 37 | docker compose up 38 | ``` 39 | 40 | ## Running the Application 41 | 42 | After installation, you can run the MeuPrimeiroCRUD em Go application with the following command (if you want to run it directly with Golang): 43 | 44 | ``` 45 | docker container run --name meuprimeirocrudgo -p 27017:27017 -d mongo 46 | 47 | go run main.go 48 | ``` 49 | 50 | The application will be accessible at `http://localhost:8080`. 51 | 52 | ## Testing the Application 53 | 54 | If you prefer, after running the project, visit: http://localhost:8080/swagger/index.html# to see and test all the route contracts. 55 | 56 | The MeuPrimeiroCRUD em Go application offers REST endpoints for creating, listing, updating, and deleting users. You can use tools like [curl](https://curl.se/) or [Postman](https://www.postman.com/) to test the endpoints. Here are some `curl` command examples for testing the endpoints: 57 | 58 | - **Create a user:** 59 | 60 | ``` 61 | curl -X POST -H "Content-Type: application/json" -d '{"name": "João", "email": "joao@example.com", "age": 30, "password": "password$#@$#323"}' http://localhost:8080/createUser 62 | ``` 63 | 64 | - **Update a user:** 65 | 66 | ``` 67 | curl -X PUT -H "Content-Type: application/json" -d '{"name": "João Silva"}' http://localhost:8080/updateUser/{userId} 68 | ``` 69 | 70 | - **Delete a user:** 71 | 72 | ``` 73 | curl -X DELETE http://localhost:8080/deleteUser/{userID} 74 | ``` 75 | 76 | Remember to adjust the commands according to your needs and requirements. 77 | 78 | ## Data Models 79 | 80 | ### request.UserLogin 81 | Structure containing the necessary fields for user login. 82 | 83 | - `email` (string, required): The user's email (must be a valid email address). 84 | - `password` (string, required): The user's password (must be at least 6 characters and contain at least one of the characters: !@#$%*). 85 | 86 | ### request.UserRequest 87 | Structure containing the required fields for creating a new user. 88 | 89 | - `age` (integer, required): The user's age (must be between 1 and 140). 90 | - `email` (string, required): The user's email (must be a valid email address). 91 | - `name` (string, required): The user's name (must be at least 4 characters and at most 100 characters). 92 | - `password` (string, required): The user's password (must be at least 6 characters and contain at least one of the characters: !@#$%*). 93 | 94 | ### request.UserUpdateRequest 95 | Structure containing fields to update user information. 96 | 97 | - `age` (integer, required): The user's age (must be between 1 and 140). 98 | - `name` (string, required): The user's name (must be at least 4 characters and at most 100 characters). 99 | 100 | ### response.UserResponse 101 | Response structure containing user information. 102 | 103 | - `age` (integer): The user's age. 104 | - `email` (string): The user's email. 105 | - `id` (string): The user's unique ID. 106 | - `name` (string): The user's name. 107 | 108 | ### rest_err.Causes 109 | Structure representing the causes of an error. 110 | 111 | - `field` (string): The field associated with the error cause. 112 | - `message` (string): Error message describing the cause. 113 | 114 | ### rest_err.RestErr 115 | Structure describing why an error occurred. 116 | 117 | - `causes` (array of rest_err.Causes): Error causes. 118 | - `code` (integer): Error code. 119 | - `error` (string): Error description. 120 | - `message` (string): Error message. 121 | 122 | ## Endpoints 123 | 124 | ### Note 125 | 126 | - For authentication, you should include the access token in the `Authorization` header as "Bearer " for protected endpoints. 127 | 128 | The API offers the following endpoints: 129 | 130 | 1. **POST /createUser** 131 | - Description: Create a new user with the provided user information. 132 | - Parameters: 133 | - `userRequest` (body, required): User information for registration. 134 | - Responses: 135 | - 200: OK (User created successfully) 136 | - 400: Bad Request (Request error) 137 | - 500: Internal Server Error (Internal server error) 138 | 139 | 2. **DELETE /deleteUser/{userId}** 140 | - Description: Delete a user based on the provided ID parameter. 141 | - Parameters: 142 | - `userId` (path, required): ID of the user to be deleted. 143 | - Responses: 144 | - 200: OK (User deleted successfully) 145 | - 400: Bad Request (Request error) 146 | - 500: Internal Server Error (Internal server error) 147 | 148 | 3. **GET /getUserByEmail/{userEmail}** 149 | - Description: Retrieve user details based on the email provided as a parameter. 150 | - Parameters: 151 | - `userEmail` (path, required): Email of the user to be retrieved. 152 | - Responses: 153 | - 200: User information retrieved successfully 154 | - 400: Error: Invalid user ID 155 | - 404: User not found 156 | 157 | 4. **GET /getUserById/{userId}** 158 | - Description: Retrieve user details based on the user ID provided as a parameter. 159 | - Parameters: 160 | - `userId` (path, required): ID of the user to be retrieved. 161 | - Responses: 162 | - 200: User information retrieved successfully 163 | - 400: Error: Invalid user ID 164 | - 404: User not found 165 | 166 | 5. **POST /login** 167 | - Description: Allow a user to log in and receive an authentication token. 168 | - Parameters: 169 | - `userLogin` (body, required): User login credentials. 170 | - Responses: 171 | - 200: Login successful, authentication token provided 172 | - 403: Error: Invalid login credentials 173 | 174 | 6. **PUT /updateUser/{userId}** 175 | - Description: Update user details based on the ID provided as a parameter. 176 | - Parameters: 177 | - `userId` (path, required): ID of the user to be updated. 178 | - `userRequest` (body, required): User information for update. 179 | - Responses: 180 | - 200: OK (User updated successfully) 181 | - 400: Bad Request (Request error) 182 | - 500: Internal Server Error (Internal server error) 183 | 184 | ## Contributing 185 | 186 | If you wish to contribute to the MeuPrimeiroCRUD em Go project in Go, feel free to submit pull requests or report issues on the [official repository](https://github.com/HunCoding/meu-primeiro-crud-go.git). 187 | 188 | ## License 189 | 190 | This project is distributed under the MIT license. Please refer to the LICENSE file for more details. 191 | 192 | --- 193 | 194 | We hope this Swagger documentation has been helpful in understanding and interacting with the API of the MeuPrimeiroCRUD em Go project in Go. If you have any questions or need additional support, please don't hesitate to reach out. Enjoy using the API! -------------------------------------------------------------------------------- /crud_users.http: -------------------------------------------------------------------------------- 1 | // Buscar usuário no banco de dados por email 2 | GET http://localhost:8080/getUserByEmail/huncoding@gmail.com 3 | Accept: application/json 4 | Authorization: 5 | ### 6 | 7 | // Buscar usuário no banco de dados por email 8 | GET http://localhost:8080/getUserById/647b3fc9c5be50a5f2e48008 9 | Accept: application/json 10 | Authorization: 11 | ### 12 | 13 | // Criar um usuario dentro do banco de dados 14 | POST http://localhost:8080/createUser 15 | Content-Type: application/json 16 | 17 | { 18 | "email": "huncoding@gmail.com", 19 | "age": 20, 20 | "password": "huncoding#!@!dwdw", 21 | "name": "Huncoding" 22 | } 23 | ### 24 | 25 | // Atualiza um usuario já criado dentro do banco de dados 26 | PUT http://localhost:8080/updateUser/6423852a15cd25e0b80f8535 27 | Content-Type: application/json 28 | 29 | { 30 | "email": "otavio20313131@test.com", 31 | "age": 90 32 | } 33 | ### 34 | 35 | // Apaga um usuário do banco de dados dado um userId 36 | DELETE http://localhost:8080/deleteUser/6423852a15cd25e0b80f8535 37 | Accept: application/json 38 | ### 39 | 40 | // Realiza o login do usuário com email e senha 41 | POST http://localhost:8080/login 42 | Content-Type: application/json 43 | 44 | { 45 | "email": "huncoding@gmail.com", 46 | "password": "huncoding#!@!dwdw" 47 | } 48 | ### 49 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | meuprimeirocrudgo: 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | environment: 8 | - MONGODB_URL=mongodb://mongodb:27017 9 | - MONGODB_USER_DB=users 10 | - JWT_SECRET_KEY= 11 | ports: 12 | - "8080:8080" 13 | depends_on: 14 | - mongodb 15 | networks: 16 | - meu_crud_network 17 | 18 | mongodb: 19 | image: mongo 20 | ports: 21 | - "27017:27017" 22 | networks: 23 | - meu_crud_network 24 | 25 | networks: 26 | meu_crud_network: -------------------------------------------------------------------------------- /docs/docs.go: -------------------------------------------------------------------------------- 1 | // Package docs Code generated by swaggo/swag. DO NOT EDIT 2 | package docs 3 | 4 | import "github.com/swaggo/swag" 5 | 6 | const docTemplate = `{ 7 | "schemes": {{ marshal .Schemes }}, 8 | "swagger": "2.0", 9 | "info": { 10 | "description": "{{escape .Description}}", 11 | "title": "{{.Title}}", 12 | "contact": {}, 13 | "version": "{{.Version}}" 14 | }, 15 | "host": "{{.Host}}", 16 | "basePath": "{{.BasePath}}", 17 | "paths": { 18 | "/createUser": { 19 | "post": { 20 | "description": "Create a new user with the provided user information", 21 | "consumes": [ 22 | "application/json" 23 | ], 24 | "produces": [ 25 | "application/json" 26 | ], 27 | "tags": [ 28 | "Users" 29 | ], 30 | "summary": "Create a new user", 31 | "parameters": [ 32 | { 33 | "description": "User information for registration", 34 | "name": "userRequest", 35 | "in": "body", 36 | "required": true, 37 | "schema": { 38 | "$ref": "#/definitions/request.UserRequest" 39 | } 40 | } 41 | ], 42 | "responses": { 43 | "200": { 44 | "description": "OK", 45 | "schema": { 46 | "$ref": "#/definitions/response.UserResponse" 47 | } 48 | }, 49 | "400": { 50 | "description": "Bad Request", 51 | "schema": { 52 | "$ref": "#/definitions/rest_err.RestErr" 53 | } 54 | }, 55 | "500": { 56 | "description": "Internal Server Error", 57 | "schema": { 58 | "$ref": "#/definitions/rest_err.RestErr" 59 | } 60 | } 61 | } 62 | } 63 | }, 64 | "/deleteUser/{userId}": { 65 | "delete": { 66 | "description": "Deletes a user based on the ID provided as a parameter.", 67 | "consumes": [ 68 | "application/json" 69 | ], 70 | "produces": [ 71 | "application/json" 72 | ], 73 | "tags": [ 74 | "Users" 75 | ], 76 | "summary": "Delete User", 77 | "parameters": [ 78 | { 79 | "type": "string", 80 | "description": "ID of the user to be deleted", 81 | "name": "userId", 82 | "in": "path", 83 | "required": true 84 | }, 85 | { 86 | "type": "string", 87 | "default": "Bearer \u003cAdd access token here\u003e", 88 | "description": "Insert your access token", 89 | "name": "Authorization", 90 | "in": "header", 91 | "required": true 92 | } 93 | ], 94 | "responses": { 95 | "200": { 96 | "description": "OK" 97 | }, 98 | "400": { 99 | "description": "Bad Request", 100 | "schema": { 101 | "$ref": "#/definitions/rest_err.RestErr" 102 | } 103 | }, 104 | "500": { 105 | "description": "Internal Server Error", 106 | "schema": { 107 | "$ref": "#/definitions/rest_err.RestErr" 108 | } 109 | } 110 | } 111 | } 112 | }, 113 | "/getUserByEmail/{userEmail}": { 114 | "get": { 115 | "description": "Retrieves user details based on the email provided as a parameter.", 116 | "consumes": [ 117 | "application/json" 118 | ], 119 | "produces": [ 120 | "application/json" 121 | ], 122 | "tags": [ 123 | "Users" 124 | ], 125 | "summary": "Find User by Email", 126 | "parameters": [ 127 | { 128 | "type": "string", 129 | "description": "Email of the user to be retrieved", 130 | "name": "userEmail", 131 | "in": "path", 132 | "required": true 133 | }, 134 | { 135 | "type": "string", 136 | "default": "Bearer \u003cAdd access token here\u003e", 137 | "description": "Insert your access token", 138 | "name": "Authorization", 139 | "in": "header", 140 | "required": true 141 | } 142 | ], 143 | "responses": { 144 | "200": { 145 | "description": "User information retrieved successfully", 146 | "schema": { 147 | "$ref": "#/definitions/response.UserResponse" 148 | } 149 | }, 150 | "400": { 151 | "description": "Error: Invalid user ID", 152 | "schema": { 153 | "$ref": "#/definitions/rest_err.RestErr" 154 | } 155 | }, 156 | "404": { 157 | "description": "User not found", 158 | "schema": { 159 | "$ref": "#/definitions/rest_err.RestErr" 160 | } 161 | } 162 | } 163 | } 164 | }, 165 | "/getUserById/{userId}": { 166 | "get": { 167 | "description": "Retrieves user details based on the user ID provided as a parameter.", 168 | "consumes": [ 169 | "application/json" 170 | ], 171 | "produces": [ 172 | "application/json" 173 | ], 174 | "tags": [ 175 | "Users" 176 | ], 177 | "summary": "Find User by ID", 178 | "parameters": [ 179 | { 180 | "type": "string", 181 | "description": "ID of the user to be retrieved", 182 | "name": "userId", 183 | "in": "path", 184 | "required": true 185 | }, 186 | { 187 | "type": "string", 188 | "default": "Bearer \u003cAdd access token here\u003e", 189 | "description": "Insert your access token", 190 | "name": "Authorization", 191 | "in": "header", 192 | "required": true 193 | } 194 | ], 195 | "responses": { 196 | "200": { 197 | "description": "User information retrieved successfully", 198 | "schema": { 199 | "$ref": "#/definitions/response.UserResponse" 200 | } 201 | }, 202 | "400": { 203 | "description": "Error: Invalid user ID", 204 | "schema": { 205 | "$ref": "#/definitions/rest_err.RestErr" 206 | } 207 | }, 208 | "404": { 209 | "description": "User not found", 210 | "schema": { 211 | "$ref": "#/definitions/rest_err.RestErr" 212 | } 213 | } 214 | } 215 | } 216 | }, 217 | "/login": { 218 | "post": { 219 | "description": "Allows a user to log in and receive an authentication token.", 220 | "consumes": [ 221 | "application/json" 222 | ], 223 | "produces": [ 224 | "application/json" 225 | ], 226 | "tags": [ 227 | "Authentication" 228 | ], 229 | "summary": "User Login", 230 | "parameters": [ 231 | { 232 | "description": "User login credentials", 233 | "name": "userLogin", 234 | "in": "body", 235 | "required": true, 236 | "schema": { 237 | "$ref": "#/definitions/request.UserLogin" 238 | } 239 | } 240 | ], 241 | "responses": { 242 | "200": { 243 | "description": "Login successful, authentication token provided", 244 | "schema": { 245 | "$ref": "#/definitions/response.UserResponse" 246 | }, 247 | "headers": { 248 | "Authorization": { 249 | "type": "string", 250 | "description": "Authentication token" 251 | } 252 | } 253 | }, 254 | "403": { 255 | "description": "Error: Invalid login credentials", 256 | "schema": { 257 | "$ref": "#/definitions/rest_err.RestErr" 258 | } 259 | } 260 | } 261 | } 262 | }, 263 | "/updateUser/{userId}": { 264 | "put": { 265 | "description": "Updates user details based on the ID provided as a parameter.", 266 | "consumes": [ 267 | "application/json" 268 | ], 269 | "produces": [ 270 | "application/json" 271 | ], 272 | "tags": [ 273 | "Users" 274 | ], 275 | "summary": "Update User", 276 | "parameters": [ 277 | { 278 | "type": "string", 279 | "description": "ID of the user to be updated", 280 | "name": "userId", 281 | "in": "path", 282 | "required": true 283 | }, 284 | { 285 | "description": "User information for update", 286 | "name": "userRequest", 287 | "in": "body", 288 | "required": true, 289 | "schema": { 290 | "$ref": "#/definitions/request.UserUpdateRequest" 291 | } 292 | }, 293 | { 294 | "type": "string", 295 | "default": "Bearer \u003cAdd access token here\u003e", 296 | "description": "Insert your access token", 297 | "name": "Authorization", 298 | "in": "header", 299 | "required": true 300 | } 301 | ], 302 | "responses": { 303 | "200": { 304 | "description": "OK" 305 | }, 306 | "400": { 307 | "description": "Bad Request", 308 | "schema": { 309 | "$ref": "#/definitions/rest_err.RestErr" 310 | } 311 | }, 312 | "500": { 313 | "description": "Internal Server Error", 314 | "schema": { 315 | "$ref": "#/definitions/rest_err.RestErr" 316 | } 317 | } 318 | } 319 | } 320 | } 321 | }, 322 | "definitions": { 323 | "request.UserLogin": { 324 | "description": "Structure containing the necessary fields for user login.", 325 | "type": "object", 326 | "required": [ 327 | "email", 328 | "password" 329 | ], 330 | "properties": { 331 | "email": { 332 | "description": "User's email (required and must be a valid email address).", 333 | "type": "string", 334 | "example": "test@test.com" 335 | }, 336 | "password": { 337 | "description": "User's password (required, minimum of 6 characters, and must contain at least one of the characters: !@#$%*).", 338 | "type": "string", 339 | "minLength": 6, 340 | "example": "password#@#@!2121" 341 | } 342 | } 343 | }, 344 | "request.UserRequest": { 345 | "description": "Structure containing the required fields for creating a new user.", 346 | "type": "object", 347 | "required": [ 348 | "age", 349 | "email", 350 | "name", 351 | "password" 352 | ], 353 | "properties": { 354 | "age": { 355 | "description": "User's age (required, must be between 1 and 140).\n@json\n@jsonTag age\n@jsonExample 30", 356 | "type": "integer", 357 | "maximum": 140, 358 | "minimum": 1, 359 | "example": 30 360 | }, 361 | "email": { 362 | "description": "User's email (required and must be a valid email address).\nExample: user@example.com\n@json\n@jsonTag email\n@jsonExample user@example.com\n@binding required,email", 363 | "type": "string", 364 | "example": "test@test.com" 365 | }, 366 | "name": { 367 | "description": "User's name (required, minimum of 4 characters, maximum of 100 characters).\nExample: John Doe\n@json\n@jsonTag name\n@jsonExample John Doe\n@binding required,min=4,max=100", 368 | "type": "string", 369 | "maxLength": 100, 370 | "minLength": 4, 371 | "example": "John Doe" 372 | }, 373 | "password": { 374 | "description": "User's password (required, minimum of 6 characters, and must contain at least one of the characters: !@#$%*).\n@json\n@jsonTag password\n@jsonExample P@ssw0rd!\n@binding required,min=6,containsany=!@#$%*", 375 | "type": "string", 376 | "minLength": 6, 377 | "example": "password#@#@!2121" 378 | } 379 | } 380 | }, 381 | "request.UserUpdateRequest": { 382 | "type": "object", 383 | "required": [ 384 | "age", 385 | "name" 386 | ], 387 | "properties": { 388 | "age": { 389 | "description": "User's age (required, must be between 1 and 140).\n@json\n@jsonTag age\n@jsonExample 30\n@binding required,min=1,max=140", 390 | "type": "integer", 391 | "maximum": 140, 392 | "minimum": 1, 393 | "example": 30 394 | }, 395 | "name": { 396 | "description": "User's name (required, minimum of 4 characters, maximum of 100 characters).\nExample: John Doe\n@json\n@jsonTag name\n@jsonExample John Doe\n@binding required,min=4,max=100", 397 | "type": "string", 398 | "maxLength": 100, 399 | "minLength": 4, 400 | "example": "John Doe" 401 | } 402 | } 403 | }, 404 | "response.UserResponse": { 405 | "type": "object", 406 | "properties": { 407 | "age": { 408 | "type": "integer", 409 | "example": 30 410 | }, 411 | "email": { 412 | "type": "string", 413 | "example": "test@test.com" 414 | }, 415 | "id": { 416 | "type": "string", 417 | "example": "82bdd399-321b-41d8-8b40-9a0116db9e92" 418 | }, 419 | "name": { 420 | "type": "string", 421 | "example": "John Doe" 422 | } 423 | } 424 | }, 425 | "rest_err.Causes": { 426 | "description": "Structure representing the causes of an error.", 427 | "type": "object", 428 | "properties": { 429 | "field": { 430 | "description": "Field associated with the error cause.\n@json\n@jsonTag field", 431 | "type": "string", 432 | "example": "name" 433 | }, 434 | "message": { 435 | "description": "Error message describing the cause.\n@json\n@jsonTag message", 436 | "type": "string", 437 | "example": "name is required" 438 | } 439 | } 440 | }, 441 | "rest_err.RestErr": { 442 | "description": "Structure for describing why the error occurred", 443 | "type": "object", 444 | "properties": { 445 | "causes": { 446 | "description": "Error causes.", 447 | "type": "array", 448 | "items": { 449 | "$ref": "#/definitions/rest_err.Causes" 450 | } 451 | }, 452 | "code": { 453 | "description": "Error code.", 454 | "type": "integer", 455 | "example": 500 456 | }, 457 | "error": { 458 | "description": "Error description.", 459 | "type": "string", 460 | "example": "internal_server_error" 461 | }, 462 | "message": { 463 | "description": "Error message.", 464 | "type": "string", 465 | "example": "error trying to process request" 466 | } 467 | } 468 | } 469 | } 470 | }` 471 | 472 | // SwaggerInfo holds exported Swagger Info so clients can modify it 473 | var SwaggerInfo = &swag.Spec{ 474 | Version: "1.0", 475 | Host: "localhost:8080", 476 | BasePath: "/", 477 | Schemes: []string{"http"}, 478 | Title: "Meu Primeiro CRUD em Go | HunCoding", 479 | Description: "API for crud operations on users", 480 | InfoInstanceName: "swagger", 481 | SwaggerTemplate: docTemplate, 482 | LeftDelim: "{{", 483 | RightDelim: "}}", 484 | } 485 | 486 | func init() { 487 | swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) 488 | } 489 | -------------------------------------------------------------------------------- /docs/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemes": [ 3 | "http" 4 | ], 5 | "swagger": "2.0", 6 | "info": { 7 | "description": "API for crud operations on users", 8 | "title": "Meu Primeiro CRUD em Go | HunCoding", 9 | "contact": {}, 10 | "version": "1.0" 11 | }, 12 | "host": "localhost:8080", 13 | "basePath": "/", 14 | "paths": { 15 | "/createUser": { 16 | "post": { 17 | "description": "Create a new user with the provided user information", 18 | "consumes": [ 19 | "application/json" 20 | ], 21 | "produces": [ 22 | "application/json" 23 | ], 24 | "tags": [ 25 | "Users" 26 | ], 27 | "summary": "Create a new user", 28 | "parameters": [ 29 | { 30 | "description": "User information for registration", 31 | "name": "userRequest", 32 | "in": "body", 33 | "required": true, 34 | "schema": { 35 | "$ref": "#/definitions/request.UserRequest" 36 | } 37 | } 38 | ], 39 | "responses": { 40 | "200": { 41 | "description": "OK", 42 | "schema": { 43 | "$ref": "#/definitions/response.UserResponse" 44 | } 45 | }, 46 | "400": { 47 | "description": "Bad Request", 48 | "schema": { 49 | "$ref": "#/definitions/rest_err.RestErr" 50 | } 51 | }, 52 | "500": { 53 | "description": "Internal Server Error", 54 | "schema": { 55 | "$ref": "#/definitions/rest_err.RestErr" 56 | } 57 | } 58 | } 59 | } 60 | }, 61 | "/deleteUser/{userId}": { 62 | "delete": { 63 | "description": "Deletes a user based on the ID provided as a parameter.", 64 | "consumes": [ 65 | "application/json" 66 | ], 67 | "produces": [ 68 | "application/json" 69 | ], 70 | "tags": [ 71 | "Users" 72 | ], 73 | "summary": "Delete User", 74 | "parameters": [ 75 | { 76 | "type": "string", 77 | "description": "ID of the user to be deleted", 78 | "name": "userId", 79 | "in": "path", 80 | "required": true 81 | }, 82 | { 83 | "type": "string", 84 | "default": "Bearer \u003cAdd access token here\u003e", 85 | "description": "Insert your access token", 86 | "name": "Authorization", 87 | "in": "header", 88 | "required": true 89 | } 90 | ], 91 | "responses": { 92 | "200": { 93 | "description": "OK" 94 | }, 95 | "400": { 96 | "description": "Bad Request", 97 | "schema": { 98 | "$ref": "#/definitions/rest_err.RestErr" 99 | } 100 | }, 101 | "500": { 102 | "description": "Internal Server Error", 103 | "schema": { 104 | "$ref": "#/definitions/rest_err.RestErr" 105 | } 106 | } 107 | } 108 | } 109 | }, 110 | "/getUserByEmail/{userEmail}": { 111 | "get": { 112 | "description": "Retrieves user details based on the email provided as a parameter.", 113 | "consumes": [ 114 | "application/json" 115 | ], 116 | "produces": [ 117 | "application/json" 118 | ], 119 | "tags": [ 120 | "Users" 121 | ], 122 | "summary": "Find User by Email", 123 | "parameters": [ 124 | { 125 | "type": "string", 126 | "description": "Email of the user to be retrieved", 127 | "name": "userEmail", 128 | "in": "path", 129 | "required": true 130 | }, 131 | { 132 | "type": "string", 133 | "default": "Bearer \u003cAdd access token here\u003e", 134 | "description": "Insert your access token", 135 | "name": "Authorization", 136 | "in": "header", 137 | "required": true 138 | } 139 | ], 140 | "responses": { 141 | "200": { 142 | "description": "User information retrieved successfully", 143 | "schema": { 144 | "$ref": "#/definitions/response.UserResponse" 145 | } 146 | }, 147 | "400": { 148 | "description": "Error: Invalid user ID", 149 | "schema": { 150 | "$ref": "#/definitions/rest_err.RestErr" 151 | } 152 | }, 153 | "404": { 154 | "description": "User not found", 155 | "schema": { 156 | "$ref": "#/definitions/rest_err.RestErr" 157 | } 158 | } 159 | } 160 | } 161 | }, 162 | "/getUserById/{userId}": { 163 | "get": { 164 | "description": "Retrieves user details based on the user ID provided as a parameter.", 165 | "consumes": [ 166 | "application/json" 167 | ], 168 | "produces": [ 169 | "application/json" 170 | ], 171 | "tags": [ 172 | "Users" 173 | ], 174 | "summary": "Find User by ID", 175 | "parameters": [ 176 | { 177 | "type": "string", 178 | "description": "ID of the user to be retrieved", 179 | "name": "userId", 180 | "in": "path", 181 | "required": true 182 | }, 183 | { 184 | "type": "string", 185 | "default": "Bearer \u003cAdd access token here\u003e", 186 | "description": "Insert your access token", 187 | "name": "Authorization", 188 | "in": "header", 189 | "required": true 190 | } 191 | ], 192 | "responses": { 193 | "200": { 194 | "description": "User information retrieved successfully", 195 | "schema": { 196 | "$ref": "#/definitions/response.UserResponse" 197 | } 198 | }, 199 | "400": { 200 | "description": "Error: Invalid user ID", 201 | "schema": { 202 | "$ref": "#/definitions/rest_err.RestErr" 203 | } 204 | }, 205 | "404": { 206 | "description": "User not found", 207 | "schema": { 208 | "$ref": "#/definitions/rest_err.RestErr" 209 | } 210 | } 211 | } 212 | } 213 | }, 214 | "/login": { 215 | "post": { 216 | "description": "Allows a user to log in and receive an authentication token.", 217 | "consumes": [ 218 | "application/json" 219 | ], 220 | "produces": [ 221 | "application/json" 222 | ], 223 | "tags": [ 224 | "Authentication" 225 | ], 226 | "summary": "User Login", 227 | "parameters": [ 228 | { 229 | "description": "User login credentials", 230 | "name": "userLogin", 231 | "in": "body", 232 | "required": true, 233 | "schema": { 234 | "$ref": "#/definitions/request.UserLogin" 235 | } 236 | } 237 | ], 238 | "responses": { 239 | "200": { 240 | "description": "Login successful, authentication token provided", 241 | "schema": { 242 | "$ref": "#/definitions/response.UserResponse" 243 | }, 244 | "headers": { 245 | "Authorization": { 246 | "type": "string", 247 | "description": "Authentication token" 248 | } 249 | } 250 | }, 251 | "403": { 252 | "description": "Error: Invalid login credentials", 253 | "schema": { 254 | "$ref": "#/definitions/rest_err.RestErr" 255 | } 256 | } 257 | } 258 | } 259 | }, 260 | "/updateUser/{userId}": { 261 | "put": { 262 | "description": "Updates user details based on the ID provided as a parameter.", 263 | "consumes": [ 264 | "application/json" 265 | ], 266 | "produces": [ 267 | "application/json" 268 | ], 269 | "tags": [ 270 | "Users" 271 | ], 272 | "summary": "Update User", 273 | "parameters": [ 274 | { 275 | "type": "string", 276 | "description": "ID of the user to be updated", 277 | "name": "userId", 278 | "in": "path", 279 | "required": true 280 | }, 281 | { 282 | "description": "User information for update", 283 | "name": "userRequest", 284 | "in": "body", 285 | "required": true, 286 | "schema": { 287 | "$ref": "#/definitions/request.UserUpdateRequest" 288 | } 289 | }, 290 | { 291 | "type": "string", 292 | "default": "Bearer \u003cAdd access token here\u003e", 293 | "description": "Insert your access token", 294 | "name": "Authorization", 295 | "in": "header", 296 | "required": true 297 | } 298 | ], 299 | "responses": { 300 | "200": { 301 | "description": "OK" 302 | }, 303 | "400": { 304 | "description": "Bad Request", 305 | "schema": { 306 | "$ref": "#/definitions/rest_err.RestErr" 307 | } 308 | }, 309 | "500": { 310 | "description": "Internal Server Error", 311 | "schema": { 312 | "$ref": "#/definitions/rest_err.RestErr" 313 | } 314 | } 315 | } 316 | } 317 | } 318 | }, 319 | "definitions": { 320 | "request.UserLogin": { 321 | "description": "Structure containing the necessary fields for user login.", 322 | "type": "object", 323 | "required": [ 324 | "email", 325 | "password" 326 | ], 327 | "properties": { 328 | "email": { 329 | "description": "User's email (required and must be a valid email address).", 330 | "type": "string", 331 | "example": "test@test.com" 332 | }, 333 | "password": { 334 | "description": "User's password (required, minimum of 6 characters, and must contain at least one of the characters: !@#$%*).", 335 | "type": "string", 336 | "minLength": 6, 337 | "example": "password#@#@!2121" 338 | } 339 | } 340 | }, 341 | "request.UserRequest": { 342 | "description": "Structure containing the required fields for creating a new user.", 343 | "type": "object", 344 | "required": [ 345 | "age", 346 | "email", 347 | "name", 348 | "password" 349 | ], 350 | "properties": { 351 | "age": { 352 | "description": "User's age (required, must be between 1 and 140).\n@json\n@jsonTag age\n@jsonExample 30", 353 | "type": "integer", 354 | "maximum": 140, 355 | "minimum": 1, 356 | "example": 30 357 | }, 358 | "email": { 359 | "description": "User's email (required and must be a valid email address).\nExample: user@example.com\n@json\n@jsonTag email\n@jsonExample user@example.com\n@binding required,email", 360 | "type": "string", 361 | "example": "test@test.com" 362 | }, 363 | "name": { 364 | "description": "User's name (required, minimum of 4 characters, maximum of 100 characters).\nExample: John Doe\n@json\n@jsonTag name\n@jsonExample John Doe\n@binding required,min=4,max=100", 365 | "type": "string", 366 | "maxLength": 100, 367 | "minLength": 4, 368 | "example": "John Doe" 369 | }, 370 | "password": { 371 | "description": "User's password (required, minimum of 6 characters, and must contain at least one of the characters: !@#$%*).\n@json\n@jsonTag password\n@jsonExample P@ssw0rd!\n@binding required,min=6,containsany=!@#$%*", 372 | "type": "string", 373 | "minLength": 6, 374 | "example": "password#@#@!2121" 375 | } 376 | } 377 | }, 378 | "request.UserUpdateRequest": { 379 | "type": "object", 380 | "required": [ 381 | "age", 382 | "name" 383 | ], 384 | "properties": { 385 | "age": { 386 | "description": "User's age (required, must be between 1 and 140).\n@json\n@jsonTag age\n@jsonExample 30\n@binding required,min=1,max=140", 387 | "type": "integer", 388 | "maximum": 140, 389 | "minimum": 1, 390 | "example": 30 391 | }, 392 | "name": { 393 | "description": "User's name (required, minimum of 4 characters, maximum of 100 characters).\nExample: John Doe\n@json\n@jsonTag name\n@jsonExample John Doe\n@binding required,min=4,max=100", 394 | "type": "string", 395 | "maxLength": 100, 396 | "minLength": 4, 397 | "example": "John Doe" 398 | } 399 | } 400 | }, 401 | "response.UserResponse": { 402 | "type": "object", 403 | "properties": { 404 | "age": { 405 | "type": "integer", 406 | "example": 30 407 | }, 408 | "email": { 409 | "type": "string", 410 | "example": "test@test.com" 411 | }, 412 | "id": { 413 | "type": "string", 414 | "example": "82bdd399-321b-41d8-8b40-9a0116db9e92" 415 | }, 416 | "name": { 417 | "type": "string", 418 | "example": "John Doe" 419 | } 420 | } 421 | }, 422 | "rest_err.Causes": { 423 | "description": "Structure representing the causes of an error.", 424 | "type": "object", 425 | "properties": { 426 | "field": { 427 | "description": "Field associated with the error cause.\n@json\n@jsonTag field", 428 | "type": "string", 429 | "example": "name" 430 | }, 431 | "message": { 432 | "description": "Error message describing the cause.\n@json\n@jsonTag message", 433 | "type": "string", 434 | "example": "name is required" 435 | } 436 | } 437 | }, 438 | "rest_err.RestErr": { 439 | "description": "Structure for describing why the error occurred", 440 | "type": "object", 441 | "properties": { 442 | "causes": { 443 | "description": "Error causes.", 444 | "type": "array", 445 | "items": { 446 | "$ref": "#/definitions/rest_err.Causes" 447 | } 448 | }, 449 | "code": { 450 | "description": "Error code.", 451 | "type": "integer", 452 | "example": 500 453 | }, 454 | "error": { 455 | "description": "Error description.", 456 | "type": "string", 457 | "example": "internal_server_error" 458 | }, 459 | "message": { 460 | "description": "Error message.", 461 | "type": "string", 462 | "example": "error trying to process request" 463 | } 464 | } 465 | } 466 | } 467 | } -------------------------------------------------------------------------------- /docs/swagger.yaml: -------------------------------------------------------------------------------- 1 | basePath: / 2 | definitions: 3 | request.UserLogin: 4 | description: Structure containing the necessary fields for user login. 5 | properties: 6 | email: 7 | description: User's email (required and must be a valid email address). 8 | example: test@test.com 9 | type: string 10 | password: 11 | description: 'User''s password (required, minimum of 6 characters, and must 12 | contain at least one of the characters: !@#$%*).' 13 | example: password#@#@!2121 14 | minLength: 6 15 | type: string 16 | required: 17 | - email 18 | - password 19 | type: object 20 | request.UserRequest: 21 | description: Structure containing the required fields for creating a new user. 22 | properties: 23 | age: 24 | description: |- 25 | User's age (required, must be between 1 and 140). 26 | @json 27 | @jsonTag age 28 | @jsonExample 30 29 | example: 30 30 | maximum: 140 31 | minimum: 1 32 | type: integer 33 | email: 34 | description: |- 35 | User's email (required and must be a valid email address). 36 | Example: user@example.com 37 | @json 38 | @jsonTag email 39 | @jsonExample user@example.com 40 | @binding required,email 41 | example: test@test.com 42 | type: string 43 | name: 44 | description: |- 45 | User's name (required, minimum of 4 characters, maximum of 100 characters). 46 | Example: John Doe 47 | @json 48 | @jsonTag name 49 | @jsonExample John Doe 50 | @binding required,min=4,max=100 51 | example: John Doe 52 | maxLength: 100 53 | minLength: 4 54 | type: string 55 | password: 56 | description: |- 57 | User's password (required, minimum of 6 characters, and must contain at least one of the characters: !@#$%*). 58 | @json 59 | @jsonTag password 60 | @jsonExample P@ssw0rd! 61 | @binding required,min=6,containsany=!@#$%* 62 | example: password#@#@!2121 63 | minLength: 6 64 | type: string 65 | required: 66 | - age 67 | - email 68 | - name 69 | - password 70 | type: object 71 | request.UserUpdateRequest: 72 | properties: 73 | age: 74 | description: |- 75 | User's age (required, must be between 1 and 140). 76 | @json 77 | @jsonTag age 78 | @jsonExample 30 79 | @binding required,min=1,max=140 80 | example: 30 81 | maximum: 140 82 | minimum: 1 83 | type: integer 84 | name: 85 | description: |- 86 | User's name (required, minimum of 4 characters, maximum of 100 characters). 87 | Example: John Doe 88 | @json 89 | @jsonTag name 90 | @jsonExample John Doe 91 | @binding required,min=4,max=100 92 | example: John Doe 93 | maxLength: 100 94 | minLength: 4 95 | type: string 96 | required: 97 | - age 98 | - name 99 | type: object 100 | response.UserResponse: 101 | properties: 102 | age: 103 | example: 30 104 | type: integer 105 | email: 106 | example: test@test.com 107 | type: string 108 | id: 109 | example: 82bdd399-321b-41d8-8b40-9a0116db9e92 110 | type: string 111 | name: 112 | example: John Doe 113 | type: string 114 | type: object 115 | rest_err.Causes: 116 | description: Structure representing the causes of an error. 117 | properties: 118 | field: 119 | description: |- 120 | Field associated with the error cause. 121 | @json 122 | @jsonTag field 123 | example: name 124 | type: string 125 | message: 126 | description: |- 127 | Error message describing the cause. 128 | @json 129 | @jsonTag message 130 | example: name is required 131 | type: string 132 | type: object 133 | rest_err.RestErr: 134 | description: Structure for describing why the error occurred 135 | properties: 136 | causes: 137 | description: Error causes. 138 | items: 139 | $ref: '#/definitions/rest_err.Causes' 140 | type: array 141 | code: 142 | description: Error code. 143 | example: 500 144 | type: integer 145 | error: 146 | description: Error description. 147 | example: internal_server_error 148 | type: string 149 | message: 150 | description: Error message. 151 | example: error trying to process request 152 | type: string 153 | type: object 154 | host: localhost:8080 155 | info: 156 | contact: {} 157 | description: API for crud operations on users 158 | title: Meu Primeiro CRUD em Go | HunCoding 159 | version: "1.0" 160 | paths: 161 | /createUser: 162 | post: 163 | consumes: 164 | - application/json 165 | description: Create a new user with the provided user information 166 | parameters: 167 | - description: User information for registration 168 | in: body 169 | name: userRequest 170 | required: true 171 | schema: 172 | $ref: '#/definitions/request.UserRequest' 173 | produces: 174 | - application/json 175 | responses: 176 | "200": 177 | description: OK 178 | schema: 179 | $ref: '#/definitions/response.UserResponse' 180 | "400": 181 | description: Bad Request 182 | schema: 183 | $ref: '#/definitions/rest_err.RestErr' 184 | "500": 185 | description: Internal Server Error 186 | schema: 187 | $ref: '#/definitions/rest_err.RestErr' 188 | summary: Create a new user 189 | tags: 190 | - Users 191 | /deleteUser/{userId}: 192 | delete: 193 | consumes: 194 | - application/json 195 | description: Deletes a user based on the ID provided as a parameter. 196 | parameters: 197 | - description: ID of the user to be deleted 198 | in: path 199 | name: userId 200 | required: true 201 | type: string 202 | - default: Bearer 203 | description: Insert your access token 204 | in: header 205 | name: Authorization 206 | required: true 207 | type: string 208 | produces: 209 | - application/json 210 | responses: 211 | "200": 212 | description: OK 213 | "400": 214 | description: Bad Request 215 | schema: 216 | $ref: '#/definitions/rest_err.RestErr' 217 | "500": 218 | description: Internal Server Error 219 | schema: 220 | $ref: '#/definitions/rest_err.RestErr' 221 | summary: Delete User 222 | tags: 223 | - Users 224 | /getUserByEmail/{userEmail}: 225 | get: 226 | consumes: 227 | - application/json 228 | description: Retrieves user details based on the email provided as a parameter. 229 | parameters: 230 | - description: Email of the user to be retrieved 231 | in: path 232 | name: userEmail 233 | required: true 234 | type: string 235 | - default: Bearer 236 | description: Insert your access token 237 | in: header 238 | name: Authorization 239 | required: true 240 | type: string 241 | produces: 242 | - application/json 243 | responses: 244 | "200": 245 | description: User information retrieved successfully 246 | schema: 247 | $ref: '#/definitions/response.UserResponse' 248 | "400": 249 | description: 'Error: Invalid user ID' 250 | schema: 251 | $ref: '#/definitions/rest_err.RestErr' 252 | "404": 253 | description: User not found 254 | schema: 255 | $ref: '#/definitions/rest_err.RestErr' 256 | summary: Find User by Email 257 | tags: 258 | - Users 259 | /getUserById/{userId}: 260 | get: 261 | consumes: 262 | - application/json 263 | description: Retrieves user details based on the user ID provided as a parameter. 264 | parameters: 265 | - description: ID of the user to be retrieved 266 | in: path 267 | name: userId 268 | required: true 269 | type: string 270 | - default: Bearer 271 | description: Insert your access token 272 | in: header 273 | name: Authorization 274 | required: true 275 | type: string 276 | produces: 277 | - application/json 278 | responses: 279 | "200": 280 | description: User information retrieved successfully 281 | schema: 282 | $ref: '#/definitions/response.UserResponse' 283 | "400": 284 | description: 'Error: Invalid user ID' 285 | schema: 286 | $ref: '#/definitions/rest_err.RestErr' 287 | "404": 288 | description: User not found 289 | schema: 290 | $ref: '#/definitions/rest_err.RestErr' 291 | summary: Find User by ID 292 | tags: 293 | - Users 294 | /login: 295 | post: 296 | consumes: 297 | - application/json 298 | description: Allows a user to log in and receive an authentication token. 299 | parameters: 300 | - description: User login credentials 301 | in: body 302 | name: userLogin 303 | required: true 304 | schema: 305 | $ref: '#/definitions/request.UserLogin' 306 | produces: 307 | - application/json 308 | responses: 309 | "200": 310 | description: Login successful, authentication token provided 311 | headers: 312 | Authorization: 313 | description: Authentication token 314 | type: string 315 | schema: 316 | $ref: '#/definitions/response.UserResponse' 317 | "403": 318 | description: 'Error: Invalid login credentials' 319 | schema: 320 | $ref: '#/definitions/rest_err.RestErr' 321 | summary: User Login 322 | tags: 323 | - Authentication 324 | /updateUser/{userId}: 325 | put: 326 | consumes: 327 | - application/json 328 | description: Updates user details based on the ID provided as a parameter. 329 | parameters: 330 | - description: ID of the user to be updated 331 | in: path 332 | name: userId 333 | required: true 334 | type: string 335 | - description: User information for update 336 | in: body 337 | name: userRequest 338 | required: true 339 | schema: 340 | $ref: '#/definitions/request.UserUpdateRequest' 341 | - default: Bearer 342 | description: Insert your access token 343 | in: header 344 | name: Authorization 345 | required: true 346 | type: string 347 | produces: 348 | - application/json 349 | responses: 350 | "200": 351 | description: OK 352 | "400": 353 | description: Bad Request 354 | schema: 355 | $ref: '#/definitions/rest_err.RestErr' 356 | "500": 357 | description: Internal Server Error 358 | schema: 359 | $ref: '#/definitions/rest_err.RestErr' 360 | summary: Update User 361 | tags: 362 | - Users 363 | schemes: 364 | - http 365 | swagger: "2.0" 366 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/HunCoding/meu-primeiro-crud-go 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.9.1 7 | github.com/go-playground/locales v0.14.1 8 | github.com/go-playground/universal-translator v0.18.1 9 | github.com/go-playground/validator/v10 v10.14.0 10 | github.com/golang-jwt/jwt v3.2.2+incompatible 11 | github.com/golang/mock v1.6.0 12 | github.com/joho/godotenv v1.4.0 13 | github.com/ory/dockertest v3.3.5+incompatible 14 | github.com/stretchr/testify v1.8.3 15 | github.com/swaggo/files v1.0.1 16 | github.com/swaggo/gin-swagger v1.6.0 17 | github.com/swaggo/swag v1.16.2 18 | go.mongodb.org/mongo-driver v1.11.1 19 | go.uber.org/mock v0.1.0 20 | go.uber.org/zap v1.24.0 21 | ) 22 | 23 | require ( 24 | github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect 25 | github.com/KyleBanks/depth v1.2.1 // indirect 26 | github.com/Microsoft/go-winio v0.6.1 // indirect 27 | github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect 28 | github.com/PuerkitoBio/purell v1.1.1 // indirect 29 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect 30 | github.com/bytedance/sonic v1.9.1 // indirect 31 | github.com/cenkalti/backoff v2.2.1+incompatible // indirect 32 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 33 | github.com/containerd/continuity v0.4.2 // indirect 34 | github.com/davecgh/go-spew v1.1.1 // indirect 35 | github.com/docker/go-connections v0.4.0 // indirect 36 | github.com/docker/go-units v0.5.0 // indirect 37 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 38 | github.com/gin-contrib/sse v0.1.0 // indirect 39 | github.com/go-openapi/jsonpointer v0.19.5 // indirect 40 | github.com/go-openapi/jsonreference v0.19.6 // indirect 41 | github.com/go-openapi/spec v0.20.4 // indirect 42 | github.com/go-openapi/swag v0.19.15 // indirect 43 | github.com/goccy/go-json v0.10.2 // indirect 44 | github.com/golang/snappy v0.0.1 // indirect 45 | github.com/google/go-cmp v0.5.5 // indirect 46 | github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect 47 | github.com/josharian/intern v1.0.0 // indirect 48 | github.com/json-iterator/go v1.1.12 // indirect 49 | github.com/klauspost/compress v1.13.6 // indirect 50 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 51 | github.com/leodido/go-urn v1.2.4 // indirect 52 | github.com/lib/pq v1.10.9 // indirect 53 | github.com/mailru/easyjson v0.7.6 // indirect 54 | github.com/mattn/go-isatty v0.0.19 // indirect 55 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 56 | github.com/modern-go/reflect2 v1.0.2 // indirect 57 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect 58 | github.com/opencontainers/go-digest v1.0.0 // indirect 59 | github.com/opencontainers/image-spec v1.0.2 // indirect 60 | github.com/opencontainers/runc v1.1.9 // indirect 61 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 62 | github.com/pkg/errors v0.9.1 // indirect 63 | github.com/pmezard/go-difflib v1.0.0 // indirect 64 | github.com/sirupsen/logrus v1.9.3 // indirect 65 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 66 | github.com/ugorji/go/codec v1.2.11 // indirect 67 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 68 | github.com/xdg-go/scram v1.1.1 // indirect 69 | github.com/xdg-go/stringprep v1.0.3 // indirect 70 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect 71 | go.uber.org/atomic v1.10.0 // indirect 72 | go.uber.org/multierr v1.8.0 // indirect 73 | golang.org/x/arch v0.3.0 // indirect 74 | golang.org/x/crypto v0.9.0 // indirect 75 | golang.org/x/mod v0.9.0 // indirect 76 | golang.org/x/net v0.10.0 // indirect 77 | golang.org/x/sync v0.1.0 // indirect 78 | golang.org/x/sys v0.8.0 // indirect 79 | golang.org/x/text v0.9.0 // indirect 80 | golang.org/x/tools v0.7.0 // indirect 81 | google.golang.org/protobuf v1.30.0 // indirect 82 | gopkg.in/yaml.v2 v2.4.0 // indirect 83 | gopkg.in/yaml.v3 v3.0.1 // indirect 84 | gotest.tools v2.2.0+incompatible // indirect 85 | ) 86 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= 2 | github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 3 | github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= 4 | github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 5 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 6 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 7 | github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= 8 | github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= 9 | github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= 10 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 11 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= 12 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 13 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 14 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 15 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 16 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 17 | github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= 18 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 19 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 20 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 21 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 22 | github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= 23 | github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= 24 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 25 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 27 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 28 | github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 29 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 30 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 31 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 32 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 33 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 34 | github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= 35 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 36 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 37 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 38 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 39 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 40 | github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= 41 | github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 42 | github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= 43 | github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= 44 | github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= 45 | github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= 46 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 47 | github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= 48 | github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 49 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 50 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 51 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 52 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 53 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 54 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 55 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 56 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 57 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 58 | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 59 | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 60 | github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= 61 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 62 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 63 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 64 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 65 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 66 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 67 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 68 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 69 | github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= 70 | github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= 71 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= 72 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 73 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 74 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 75 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 76 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 77 | github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= 78 | github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 79 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 80 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 81 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 82 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 83 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 84 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 85 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 86 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 87 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 88 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 89 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 90 | github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= 91 | github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 92 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 93 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 94 | github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= 95 | github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 96 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 97 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 98 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 99 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 100 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 101 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 102 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 103 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= 104 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 105 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 106 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 107 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 108 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 109 | github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= 110 | github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 111 | github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= 112 | github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= 113 | github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= 114 | github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= 115 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 116 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 117 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 118 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 119 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 120 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 121 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 122 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 123 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 124 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 125 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 126 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 127 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 128 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 129 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 130 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 131 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 132 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 133 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 134 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 135 | github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= 136 | github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= 137 | github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= 138 | github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= 139 | github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= 140 | github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= 141 | github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= 142 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= 143 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 144 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 145 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 146 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 147 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 148 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 149 | github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= 150 | github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= 151 | github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= 152 | github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= 153 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= 154 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 155 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 156 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 157 | go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8= 158 | go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= 159 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 160 | go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= 161 | go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 162 | go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= 163 | go.uber.org/mock v0.1.0 h1:NEVjcPIj/L96qilPi+2M5l9zUkQksRaqNKphz3pStHI= 164 | go.uber.org/mock v0.1.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= 165 | go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= 166 | go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= 167 | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= 168 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 169 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 170 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 171 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 172 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 173 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 174 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 175 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 176 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 177 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 178 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 179 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 180 | golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= 181 | golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 182 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 183 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 184 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 185 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 186 | golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= 187 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 188 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 189 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 190 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 191 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 192 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 193 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 194 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 195 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 196 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 197 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 198 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 199 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 200 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 201 | golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 202 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 203 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 204 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 205 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 206 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 207 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 208 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 209 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 210 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 211 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 212 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 213 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 214 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 215 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 216 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 217 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 218 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 219 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 220 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 221 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 222 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 223 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 224 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 225 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 226 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 227 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 228 | golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= 229 | golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 230 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 231 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 232 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 233 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 234 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 235 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 236 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 237 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 238 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 239 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 240 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 241 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 242 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 243 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 244 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 245 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 246 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 247 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 248 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 249 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 250 | gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= 251 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 252 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 253 | -------------------------------------------------------------------------------- /init_dependencies.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/service" 7 | "go.mongodb.org/mongo-driver/mongo" 8 | ) 9 | 10 | func initDependencies( 11 | database *mongo.Database, 12 | ) controller.UserControllerInterface { 13 | repo := repository.NewUserRepository(database) 14 | service := service.NewUserDomainService(repo) 15 | return controller.NewUserControllerInterface(service) 16 | } 17 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | _ "github.com/HunCoding/meu-primeiro-crud-go/docs" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/database/mongodb" 9 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 10 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/routes" 11 | "github.com/gin-gonic/gin" 12 | "github.com/joho/godotenv" 13 | ) 14 | 15 | // @title Meu Primeiro CRUD em Go | HunCoding 16 | // @version 1.0 17 | // @description API for crud operations on users 18 | // @host localhost:8080 19 | // @BasePath / 20 | // @schemes http 21 | // @license MIT 22 | func main() { 23 | logger.Info("About to start user application") 24 | 25 | godotenv.Load() 26 | 27 | database, err := mongodb.NewMongoDBConnection(context.Background()) 28 | if err != nil { 29 | log.Fatalf( 30 | "Error trying to connect to database, error=%s \n", 31 | err.Error()) 32 | return 33 | } 34 | 35 | userController := initDependencies(database) 36 | 37 | router := gin.Default() 38 | gin.SetMode(gin.ReleaseMode) 39 | routes.InitRoutes(&router.RouterGroup, userController) 40 | 41 | if err := router.Run(":8080"); err != nil { 42 | log.Fatal(err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/configuration/database/mongodb/mongodb_connection.go: -------------------------------------------------------------------------------- 1 | package mongodb 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "go.mongodb.org/mongo-driver/mongo" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | ) 10 | 11 | var ( 12 | MONGODB_URL = "MONGODB_URL" 13 | MONGODB_USER_DB = "MONGODB_USER_DB" 14 | ) 15 | 16 | func NewMongoDBConnection( 17 | ctx context.Context, 18 | ) (*mongo.Database, error) { 19 | mongodb_uri := os.Getenv(MONGODB_URL) 20 | mongodb_database := os.Getenv(MONGODB_USER_DB) 21 | 22 | client, err := mongo.Connect( 23 | ctx, 24 | options.Client().ApplyURI(mongodb_uri)) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | if err := client.Ping(ctx, nil); err != nil { 30 | return nil, err 31 | } 32 | 33 | return client.Database(mongodb_database), nil 34 | } 35 | -------------------------------------------------------------------------------- /src/configuration/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "go.uber.org/zap" 8 | "go.uber.org/zap/zapcore" 9 | ) 10 | 11 | var ( 12 | log *zap.Logger 13 | 14 | LOG_OUTPUT = "LOG_OUTPUT" 15 | LOG_LEVEL = "LOG_LEVEL" 16 | ) 17 | 18 | func init() { 19 | logConfig := zap.Config{ 20 | OutputPaths: []string{getOutputLogs()}, 21 | Level: zap.NewAtomicLevelAt(getLevelLogs()), 22 | Encoding: "json", 23 | EncoderConfig: zapcore.EncoderConfig{ 24 | LevelKey: "level", 25 | TimeKey: "time", 26 | MessageKey: "message", 27 | EncodeTime: zapcore.ISO8601TimeEncoder, 28 | EncodeLevel: zapcore.LowercaseLevelEncoder, 29 | EncodeCaller: zapcore.ShortCallerEncoder, 30 | }, 31 | } 32 | 33 | log, _ = logConfig.Build() 34 | } 35 | 36 | func Info(message string, tags ...zap.Field) { 37 | log.Info(message, tags...) 38 | log.Sync() 39 | } 40 | 41 | func Error(message string, err error, tags ...zap.Field) { 42 | tags = append(tags, zap.NamedError("error", err)) 43 | log.Info(message, tags...) 44 | log.Sync() 45 | } 46 | 47 | func getOutputLogs() string { 48 | output := strings.ToLower(strings.TrimSpace(os.Getenv(LOG_OUTPUT))) 49 | if output == "" { 50 | return "stdout" 51 | } 52 | 53 | return output 54 | } 55 | 56 | func getLevelLogs() zapcore.Level { 57 | switch strings.ToLower(strings.TrimSpace(os.Getenv(LOG_LEVEL))) { 58 | case "info": 59 | return zapcore.InfoLevel 60 | case "error": 61 | return zapcore.ErrorLevel 62 | case "debug": 63 | return zapcore.DebugLevel 64 | default: 65 | return zapcore.InfoLevel 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/configuration/rest_err/rest_err.go: -------------------------------------------------------------------------------- 1 | package rest_err 2 | 3 | import "net/http" 4 | 5 | // RestErr represents the error object. 6 | // @Summary Error information 7 | // @Description Structure for describing why the error occurred 8 | type RestErr struct { 9 | // Error message. 10 | Message string `json:"message" example:"error trying to process request"` 11 | 12 | // Error description. 13 | Err string `json:"error" example:"internal_server_error"` 14 | 15 | // Error code. 16 | Code int `json:"code" example:"500"` 17 | 18 | // Error causes. 19 | Causes []Causes `json:"causes"` 20 | } 21 | 22 | // Causes represents the error causes. 23 | // @Summary Error Causes 24 | // @Description Structure representing the causes of an error. 25 | type Causes struct { 26 | // Field associated with the error cause. 27 | // @json 28 | // @jsonTag field 29 | Field string `json:"field" example:"name"` 30 | 31 | // Error message describing the cause. 32 | // @json 33 | // @jsonTag message 34 | Message string `json:"message" example:"name is required"` 35 | } 36 | 37 | func (r *RestErr) Error() string { 38 | return r.Message 39 | } 40 | 41 | func NewBadRequestError(message string) *RestErr { 42 | return &RestErr{ 43 | Message: message, 44 | Err: "bad_request", 45 | Code: http.StatusBadRequest, 46 | } 47 | } 48 | 49 | func NewUnauthorizedRequestError(message string) *RestErr { 50 | return &RestErr{ 51 | Message: message, 52 | Err: "unauthorized", 53 | Code: http.StatusUnauthorized, 54 | } 55 | } 56 | 57 | func NewBadRequestValidationError(message string, causes []Causes) *RestErr { 58 | return &RestErr{ 59 | Message: message, 60 | Err: "bad_request", 61 | Code: http.StatusBadRequest, 62 | Causes: causes, 63 | } 64 | } 65 | 66 | func NewInternalServerError(message string) *RestErr { 67 | return &RestErr{ 68 | Message: message, 69 | Err: "internal_server_error", 70 | Code: http.StatusInternalServerError, 71 | } 72 | } 73 | 74 | func NewNotFoundError(message string) *RestErr { 75 | return &RestErr{ 76 | Message: message, 77 | Err: "not_found", 78 | Code: http.StatusNotFound, 79 | } 80 | } 81 | 82 | func NewForbiddenError(message string) *RestErr { 83 | return &RestErr{ 84 | Message: message, 85 | Err: "forbidden", 86 | Code: http.StatusForbidden, 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/configuration/validation/validate_user.go: -------------------------------------------------------------------------------- 1 | package validation 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 8 | "github.com/gin-gonic/gin/binding" 9 | "github.com/go-playground/locales/en" 10 | ut "github.com/go-playground/universal-translator" 11 | "github.com/go-playground/validator/v10" 12 | 13 | en_translation "github.com/go-playground/validator/v10/translations/en" 14 | ) 15 | 16 | var ( 17 | Validate = validator.New() 18 | transl ut.Translator 19 | ) 20 | 21 | func init() { 22 | if val, ok := binding.Validator.Engine().(*validator.Validate); ok { 23 | en := en.New() 24 | unt := ut.New(en, en) 25 | transl, _ = unt.GetTranslator("en") 26 | en_translation.RegisterDefaultTranslations(val, transl) 27 | } 28 | } 29 | 30 | func ValidateUserError( 31 | validation_err error, 32 | ) *rest_err.RestErr { 33 | 34 | var jsonErr *json.UnmarshalTypeError 35 | var jsonValidationError validator.ValidationErrors 36 | 37 | if errors.As(validation_err, &jsonErr) { 38 | return rest_err.NewBadRequestError("Invalid field type") 39 | } else if errors.As(validation_err, &jsonValidationError) { 40 | errorsCauses := []rest_err.Causes{} 41 | 42 | for _, e := range validation_err.(validator.ValidationErrors) { 43 | cause := rest_err.Causes{ 44 | Message: e.Translate(transl), 45 | Field: e.Field(), 46 | } 47 | 48 | errorsCauses = append(errorsCauses, cause) 49 | } 50 | 51 | return rest_err.NewBadRequestValidationError("Some fields are invalid", errorsCauses) 52 | } else { 53 | return rest_err.NewBadRequestError("Error trying to convert fields") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/controller/create_user.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/validation" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 9 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 10 | "github.com/HunCoding/meu-primeiro-crud-go/src/view" 11 | "github.com/gin-gonic/gin" 12 | "go.uber.org/zap" 13 | ) 14 | 15 | // CreateUser Creates a new user 16 | // @Summary Create a new user 17 | // @Description Create a new user with the provided user information 18 | // @Tags Users 19 | // @Accept json 20 | // @Produce json 21 | // @Param userRequest body request.UserRequest true "User information for registration" 22 | // @Success 200 {object} response.UserResponse 23 | // @Failure 400 {object} rest_err.RestErr 24 | // @Failure 500 {object} rest_err.RestErr 25 | // @Router /createUser [post] 26 | func (uc *userControllerInterface) CreateUser(c *gin.Context) { 27 | logger.Info("Init CreateUser controller", 28 | zap.String("journey", "createUser"), 29 | ) 30 | var userRequest request.UserRequest 31 | 32 | if err := c.ShouldBindJSON(&userRequest); err != nil { 33 | logger.Error("Error trying to validate user info", err, 34 | zap.String("journey", "createUser")) 35 | errRest := validation.ValidateUserError(err) 36 | 37 | c.JSON(errRest.Code, errRest) 38 | return 39 | } 40 | 41 | domain := model.NewUserDomain( 42 | userRequest.Email, 43 | userRequest.Password, 44 | userRequest.Name, 45 | userRequest.Age, 46 | ) 47 | domainResult, err := uc.service.CreateUserServices(domain) 48 | if err != nil { 49 | logger.Error( 50 | "Error trying to call CreateUser service", 51 | err, 52 | zap.String("journey", "createUser")) 53 | c.JSON(err.Code, err) 54 | return 55 | } 56 | 57 | logger.Info( 58 | "CreateUser controller executed successfully", 59 | zap.String("userId", domainResult.GetID()), 60 | zap.String("journey", "createUser")) 61 | 62 | c.JSON(http.StatusOK, view.ConvertDomainToResponse( 63 | domainResult, 64 | )) 65 | } 66 | -------------------------------------------------------------------------------- /src/controller/create_user_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 9 | "github.com/gin-gonic/gin" 10 | "github.com/golang/mock/gomock" 11 | "github.com/stretchr/testify/assert" 12 | "io" 13 | "net/http" 14 | "net/http/httptest" 15 | "net/url" 16 | "strings" 17 | "testing" 18 | ) 19 | 20 | func TestUserControllerInterface_CreateUser(t *testing.T) { 21 | ctrl := gomock.NewController(t) 22 | defer ctrl.Finish() 23 | 24 | service := mocks.NewMockUserDomainService(ctrl) 25 | controller := NewUserControllerInterface(service) 26 | 27 | t.Run("validation_got_error", func(t *testing.T) { 28 | recorder := httptest.NewRecorder() 29 | context := GetTestGinContext(recorder) 30 | 31 | userRequest := request.UserRequest{ 32 | Email: "ERROR@_EMAIL", 33 | Password: "teste@", 34 | Name: "test", 35 | Age: 0, 36 | } 37 | 38 | b, _ := json.Marshal(userRequest) 39 | stringReader := io.NopCloser(strings.NewReader(string(b))) 40 | 41 | MakeRequest(context, []gin.Param{}, url.Values{}, "POST", stringReader) 42 | controller.CreateUser(context) 43 | 44 | assert.EqualValues(t, http.StatusBadRequest, recorder.Code) 45 | }) 46 | 47 | t.Run("object_is_valid_but_service_returns_error", func(t *testing.T) { 48 | recorder := httptest.NewRecorder() 49 | context := GetTestGinContext(recorder) 50 | 51 | userRequest := request.UserRequest{ 52 | Email: "test@test.com", 53 | Password: "teste@#@123", 54 | Name: "Test User", 55 | Age: 10, 56 | } 57 | 58 | domain := model.NewUserDomain( 59 | userRequest.Email, 60 | userRequest.Password, 61 | userRequest.Name, 62 | userRequest.Age, 63 | ) 64 | 65 | b, _ := json.Marshal(userRequest) 66 | stringReader := io.NopCloser(strings.NewReader(string(b))) 67 | 68 | service.EXPECT().CreateUserServices(domain).Return( 69 | nil, rest_err.NewInternalServerError("error test")) 70 | 71 | MakeRequest(context, []gin.Param{}, url.Values{}, "POST", stringReader) 72 | controller.CreateUser(context) 73 | 74 | assert.EqualValues(t, http.StatusInternalServerError, recorder.Code) 75 | }) 76 | 77 | t.Run("object_is_valid_and_service_returns_success", func(t *testing.T) { 78 | recorder := httptest.NewRecorder() 79 | context := GetTestGinContext(recorder) 80 | 81 | userRequest := request.UserRequest{ 82 | Email: "test@test.com", 83 | Password: "teste@#@123", 84 | Name: "Test User", 85 | Age: 10, 86 | } 87 | 88 | domain := model.NewUserDomain( 89 | userRequest.Email, 90 | userRequest.Password, 91 | userRequest.Name, 92 | userRequest.Age, 93 | ) 94 | 95 | b, _ := json.Marshal(userRequest) 96 | stringReader := io.NopCloser(strings.NewReader(string(b))) 97 | 98 | service.EXPECT().CreateUserServices(domain).Return( 99 | domain, nil) 100 | 101 | MakeRequest(context, []gin.Param{}, url.Values{}, "POST", stringReader) 102 | controller.CreateUser(context) 103 | 104 | assert.EqualValues(t, http.StatusOK, recorder.Code) 105 | }) 106 | } 107 | -------------------------------------------------------------------------------- /src/controller/delete_user.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 8 | "github.com/gin-gonic/gin" 9 | "go.mongodb.org/mongo-driver/bson/primitive" 10 | "go.uber.org/zap" 11 | ) 12 | 13 | // DeleteUser deletes a user with the specified ID. 14 | // @Summary Delete User 15 | // @Description Deletes a user based on the ID provided as a parameter. 16 | // @Tags Users 17 | // @Accept json 18 | // @Produce json 19 | // @Param userId path string true "ID of the user to be deleted" 20 | // @Success 200 21 | // @Param Authorization header string true "Insert your access token" default(Bearer ) 22 | // @Failure 400 {object} rest_err.RestErr 23 | // @Failure 500 {object} rest_err.RestErr 24 | // @Router /deleteUser/{userId} [delete] 25 | func (uc *userControllerInterface) DeleteUser(c *gin.Context) { 26 | logger.Info("Init deleteUser controller", 27 | zap.String("journey", "deleteUser"), 28 | ) 29 | 30 | userId := c.Param("userId") 31 | if _, err := primitive.ObjectIDFromHex(userId); err != nil { 32 | errRest := rest_err.NewBadRequestError("Invalid userId, must be a hex value") 33 | c.JSON(errRest.Code, errRest) 34 | return 35 | } 36 | 37 | err := uc.service.DeleteUser(userId) 38 | if err != nil { 39 | logger.Error( 40 | "Error trying to call deleteUser service", 41 | err, 42 | zap.String("journey", "deleteUser")) 43 | c.JSON(err.Code, err) 44 | return 45 | } 46 | 47 | logger.Info( 48 | "deleteUser controller executed successfully", 49 | zap.String("userId", userId), 50 | zap.String("journey", "deleteUser")) 51 | 52 | c.Status(http.StatusOK) 53 | } 54 | -------------------------------------------------------------------------------- /src/controller/delete_user_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 6 | "github.com/gin-gonic/gin" 7 | "github.com/golang/mock/gomock" 8 | "github.com/stretchr/testify/assert" 9 | "go.mongodb.org/mongo-driver/bson/primitive" 10 | "net/http" 11 | "net/http/httptest" 12 | "net/url" 13 | "testing" 14 | ) 15 | 16 | func TestUserControllerInterface_DeleteUser(t *testing.T) { 17 | ctrl := gomock.NewController(t) 18 | defer ctrl.Finish() 19 | 20 | service := mocks.NewMockUserDomainService(ctrl) 21 | controller := NewUserControllerInterface(service) 22 | 23 | t.Run("userId_is_invalid_returns_error", func(t *testing.T) { 24 | recorder := httptest.NewRecorder() 25 | context := GetTestGinContext(recorder) 26 | 27 | param := []gin.Param{ 28 | { 29 | Key: "userId", 30 | Value: "teste", 31 | }, 32 | } 33 | 34 | MakeRequest(context, param, url.Values{}, "DELETE", nil) 35 | controller.DeleteUser(context) 36 | 37 | assert.EqualValues(t, http.StatusBadRequest, recorder.Code) 38 | }) 39 | 40 | t.Run("id_is_valid_service_returns_error", func(t *testing.T) { 41 | recorder := httptest.NewRecorder() 42 | context := GetTestGinContext(recorder) 43 | id := primitive.NewObjectID().Hex() 44 | 45 | param := []gin.Param{ 46 | { 47 | Key: "userId", 48 | Value: id, 49 | }, 50 | } 51 | 52 | service.EXPECT().DeleteUser(id).Return( 53 | rest_err.NewInternalServerError("error test")) 54 | 55 | MakeRequest(context, param, url.Values{}, "DELETE", nil) 56 | controller.DeleteUser(context) 57 | 58 | assert.EqualValues(t, http.StatusInternalServerError, recorder.Code) 59 | }) 60 | 61 | t.Run("id_is_valid_service_returns_success", func(t *testing.T) { 62 | recorder := httptest.NewRecorder() 63 | context := GetTestGinContext(recorder) 64 | id := primitive.NewObjectID().Hex() 65 | 66 | param := []gin.Param{ 67 | { 68 | Key: "userId", 69 | Value: id, 70 | }, 71 | } 72 | 73 | service.EXPECT().DeleteUser(id).Return(nil) 74 | 75 | MakeRequest(context, param, url.Values{}, "DELETE", nil) 76 | controller.DeleteUser(context) 77 | 78 | assert.EqualValues(t, http.StatusOK, recorder.Code) 79 | }) 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/controller/find_user.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | "net/mail" 6 | 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 9 | "github.com/HunCoding/meu-primeiro-crud-go/src/view" 10 | "github.com/gin-gonic/gin" 11 | "go.mongodb.org/mongo-driver/bson/primitive" 12 | "go.uber.org/zap" 13 | ) 14 | 15 | // FindUserByID retrieves user information based on the provided user ID. 16 | // @Summary Find User by ID 17 | // @Description Retrieves user details based on the user ID provided as a parameter. 18 | // @Tags Users 19 | // @Accept json 20 | // @Produce json 21 | // @Param userId path string true "ID of the user to be retrieved" 22 | // @Param Authorization header string true "Insert your access token" default(Bearer ) 23 | // @Success 200 {object} response.UserResponse "User information retrieved successfully" 24 | // @Failure 400 {object} rest_err.RestErr "Error: Invalid user ID" 25 | // @Failure 404 {object} rest_err.RestErr "User not found" 26 | // @Router /getUserById/{userId} [get] 27 | func (uc *userControllerInterface) FindUserByID(c *gin.Context) { 28 | logger.Info("Init findUserByID controller", 29 | zap.String("journey", "findUserByID"), 30 | ) 31 | 32 | userId := c.Param("userId") 33 | 34 | if _, err := primitive.ObjectIDFromHex(userId); err != nil { 35 | logger.Error("Error trying to validate userId", 36 | err, 37 | zap.String("journey", "findUserByID"), 38 | ) 39 | errorMessage := rest_err.NewBadRequestError( 40 | "UserID is not a valid id", 41 | ) 42 | 43 | c.JSON(errorMessage.Code, errorMessage) 44 | return 45 | } 46 | 47 | userDomain, err := uc.service.FindUserByIDServices(userId) 48 | if err != nil { 49 | logger.Error("Error trying to call findUserByID services", 50 | err, 51 | zap.String("journey", "findUserByID"), 52 | ) 53 | c.JSON(err.Code, err) 54 | return 55 | } 56 | 57 | logger.Info("FindUserByID controller executed successfully", 58 | zap.String("journey", "findUserByID"), 59 | ) 60 | c.JSON(http.StatusOK, view.ConvertDomainToResponse( 61 | userDomain, 62 | )) 63 | } 64 | 65 | // FindUserByEmail retrieves user information based on the provided email. 66 | // @Summary Find User by Email 67 | // @Description Retrieves user details based on the email provided as a parameter. 68 | // @Tags Users 69 | // @Accept json 70 | // @Produce json 71 | // @Param userEmail path string true "Email of the user to be retrieved" 72 | // @Param Authorization header string true "Insert your access token" default(Bearer ) 73 | // @Success 200 {object} response.UserResponse "User information retrieved successfully" 74 | // @Failure 400 {object} rest_err.RestErr "Error: Invalid user ID" 75 | // @Failure 404 {object} rest_err.RestErr "User not found" 76 | // @Router /getUserByEmail/{userEmail} [get] 77 | func (uc *userControllerInterface) FindUserByEmail(c *gin.Context) { 78 | logger.Info("Init findUserByEmail controller", 79 | zap.String("journey", "findUserByEmail"), 80 | ) 81 | 82 | userEmail := c.Param("userEmail") 83 | 84 | if _, err := mail.ParseAddress(userEmail); err != nil { 85 | logger.Error("Error trying to validate userEmail", 86 | err, 87 | zap.String("journey", "findUserByEmail"), 88 | ) 89 | errorMessage := rest_err.NewBadRequestError( 90 | "UserEmail is not a valid email", 91 | ) 92 | 93 | c.JSON(errorMessage.Code, errorMessage) 94 | return 95 | } 96 | 97 | userDomain, err := uc.service.FindUserByEmailServices(userEmail) 98 | if err != nil { 99 | logger.Error("Error trying to call findUserByEmail services", 100 | err, 101 | zap.String("journey", "findUserByEmail"), 102 | ) 103 | c.JSON(err.Code, err) 104 | return 105 | } 106 | 107 | logger.Info("findUserByEmail controller executed successfully", 108 | zap.String("journey", "findUserByEmail"), 109 | ) 110 | c.JSON(http.StatusOK, view.ConvertDomainToResponse( 111 | userDomain, 112 | )) 113 | } 114 | -------------------------------------------------------------------------------- /src/controller/find_user_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 7 | "github.com/gin-gonic/gin" 8 | "github.com/golang/mock/gomock" 9 | "github.com/stretchr/testify/assert" 10 | "go.mongodb.org/mongo-driver/bson/primitive" 11 | "io" 12 | "net/http" 13 | "net/http/httptest" 14 | "net/url" 15 | "testing" 16 | ) 17 | 18 | func TestUserControllerInterface_FindUserByEmail(t *testing.T) { 19 | ctrl := gomock.NewController(t) 20 | defer ctrl.Finish() 21 | 22 | service := mocks.NewMockUserDomainService(ctrl) 23 | controller := NewUserControllerInterface(service) 24 | 25 | t.Run("email_is_invalid_returns_error", func(t *testing.T) { 26 | recorder := httptest.NewRecorder() 27 | context := GetTestGinContext(recorder) 28 | 29 | param := []gin.Param{ 30 | { 31 | Key: "userEmail", 32 | Value: "TEST_ERROR", 33 | }, 34 | } 35 | 36 | MakeRequest(context, param, url.Values{}, "GET", nil) 37 | controller.FindUserByEmail(context) 38 | 39 | assert.EqualValues(t, http.StatusBadRequest, recorder.Code) 40 | }) 41 | 42 | t.Run("email_is_valid_service_returns_error", func(t *testing.T) { 43 | recorder := httptest.NewRecorder() 44 | context := GetTestGinContext(recorder) 45 | 46 | param := []gin.Param{ 47 | { 48 | Key: "userEmail", 49 | Value: "test@test.com", 50 | }, 51 | } 52 | 53 | service.EXPECT().FindUserByEmailServices("test@test.com").Return( 54 | nil, rest_err.NewInternalServerError("error test")) 55 | 56 | MakeRequest(context, param, url.Values{}, "GET", nil) 57 | controller.FindUserByEmail(context) 58 | 59 | assert.EqualValues(t, http.StatusInternalServerError, recorder.Code) 60 | }) 61 | 62 | t.Run("email_is_valid_service_returns_success", func(t *testing.T) { 63 | recorder := httptest.NewRecorder() 64 | context := GetTestGinContext(recorder) 65 | 66 | param := []gin.Param{ 67 | { 68 | Key: "userEmail", 69 | Value: "test@test.com", 70 | }, 71 | } 72 | 73 | service.EXPECT().FindUserByEmailServices("test@test.com").Return( 74 | model.NewUserDomain( 75 | "test@test.com", 76 | "test", 77 | "test", 78 | 20), nil) 79 | 80 | MakeRequest(context, param, url.Values{}, "GET", nil) 81 | controller.FindUserByEmail(context) 82 | 83 | assert.EqualValues(t, http.StatusOK, recorder.Code) 84 | }) 85 | 86 | } 87 | 88 | func TestUserControllerInterface_FindUserById(t *testing.T) { 89 | ctrl := gomock.NewController(t) 90 | defer ctrl.Finish() 91 | 92 | service := mocks.NewMockUserDomainService(ctrl) 93 | controller := NewUserControllerInterface(service) 94 | 95 | t.Run("id_is_invalid_returns_error", func(t *testing.T) { 96 | recorder := httptest.NewRecorder() 97 | context := GetTestGinContext(recorder) 98 | 99 | param := []gin.Param{ 100 | { 101 | Key: "userId", 102 | Value: "teste", 103 | }, 104 | } 105 | 106 | MakeRequest(context, param, url.Values{}, "GET", nil) 107 | controller.FindUserByID(context) 108 | 109 | assert.EqualValues(t, http.StatusBadRequest, recorder.Code) 110 | }) 111 | 112 | t.Run("id_is_valid_service_returns_error", func(t *testing.T) { 113 | recorder := httptest.NewRecorder() 114 | context := GetTestGinContext(recorder) 115 | id := primitive.NewObjectID().Hex() 116 | 117 | param := []gin.Param{ 118 | { 119 | Key: "userId", 120 | Value: id, 121 | }, 122 | } 123 | 124 | service.EXPECT().FindUserByIDServices(id).Return( 125 | nil, rest_err.NewInternalServerError("error test")) 126 | 127 | MakeRequest(context, param, url.Values{}, "GET", nil) 128 | controller.FindUserByID(context) 129 | 130 | assert.EqualValues(t, http.StatusInternalServerError, recorder.Code) 131 | }) 132 | 133 | t.Run("email_is_valid_service_returns_success", func(t *testing.T) { 134 | recorder := httptest.NewRecorder() 135 | context := GetTestGinContext(recorder) 136 | id := primitive.NewObjectID().Hex() 137 | 138 | param := []gin.Param{ 139 | { 140 | Key: "userId", 141 | Value: id, 142 | }, 143 | } 144 | 145 | service.EXPECT().FindUserByIDServices(id).Return( 146 | model.NewUserDomain( 147 | "test@test.com", 148 | "test", 149 | "test", 150 | 20), nil) 151 | 152 | MakeRequest(context, param, url.Values{}, "GET", nil) 153 | controller.FindUserByID(context) 154 | 155 | assert.EqualValues(t, http.StatusOK, recorder.Code) 156 | }) 157 | 158 | } 159 | 160 | func GetTestGinContext(recorder *httptest.ResponseRecorder) *gin.Context { 161 | gin.SetMode(gin.TestMode) 162 | 163 | ctx, _ := gin.CreateTestContext(recorder) 164 | ctx.Request = &http.Request{ 165 | Header: make(http.Header), 166 | URL: &url.URL{}, 167 | } 168 | 169 | return ctx 170 | } 171 | 172 | func MakeRequest( 173 | c *gin.Context, 174 | param gin.Params, 175 | u url.Values, 176 | method string, 177 | body io.ReadCloser) { 178 | c.Request.Method = method 179 | c.Request.Header.Set("Content-Type", "application/json") 180 | c.Params = param 181 | 182 | c.Request.Body = body 183 | c.Request.URL.RawQuery = u.Encode() 184 | } 185 | -------------------------------------------------------------------------------- /src/controller/login_user.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/validation" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 9 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 10 | "github.com/HunCoding/meu-primeiro-crud-go/src/view" 11 | "github.com/gin-gonic/gin" 12 | "go.uber.org/zap" 13 | ) 14 | 15 | // LoginUser allows a user to log in and obtain an authentication token. 16 | // @Summary User Login 17 | // @Description Allows a user to log in and receive an authentication token. 18 | // @Tags Authentication 19 | // @Accept json 20 | // @Produce json 21 | // @Param userLogin body request.UserLogin true "User login credentials" 22 | // @Success 200 {object} response.UserResponse "Login successful, authentication token provided" 23 | // @Header 200 {string} Authorization "Authentication token" 24 | // @Failure 403 {object} rest_err.RestErr "Error: Invalid login credentials" 25 | // @Router /login [post] 26 | func (uc *userControllerInterface) LoginUser(c *gin.Context) { 27 | logger.Info("Init loginUser controller", 28 | zap.String("journey", "loginUser"), 29 | ) 30 | var userRequest request.UserLogin 31 | 32 | if err := c.ShouldBindJSON(&userRequest); err != nil { 33 | logger.Error("Error trying to validate user info", err, 34 | zap.String("journey", "loginUser")) 35 | errRest := validation.ValidateUserError(err) 36 | 37 | c.JSON(errRest.Code, errRest) 38 | return 39 | } 40 | 41 | domain := model.NewUserLoginDomain( 42 | userRequest.Email, 43 | userRequest.Password, 44 | ) 45 | domainResult, token, err := uc.service.LoginUserServices(domain) 46 | if err != nil { 47 | logger.Error( 48 | "Error trying to call loginUser service", 49 | err, 50 | zap.String("journey", "loginUser")) 51 | c.JSON(err.Code, err) 52 | return 53 | } 54 | 55 | logger.Info( 56 | "loginUser controller executed successfully", 57 | zap.String("userId", domainResult.GetID()), 58 | zap.String("journey", "loginUser")) 59 | 60 | c.Header("Authorization", token) 61 | c.JSON(http.StatusOK, view.ConvertDomainToResponse( 62 | domainResult, 63 | )) 64 | } 65 | -------------------------------------------------------------------------------- /src/controller/login_user_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 9 | "github.com/gin-gonic/gin" 10 | "github.com/golang/mock/gomock" 11 | "github.com/stretchr/testify/assert" 12 | "go.mongodb.org/mongo-driver/bson/primitive" 13 | "io" 14 | "net/http" 15 | "net/http/httptest" 16 | "net/url" 17 | "strings" 18 | "testing" 19 | ) 20 | 21 | func TestUserControllerInterface_LoginUser(t *testing.T) { 22 | ctrl := gomock.NewController(t) 23 | defer ctrl.Finish() 24 | 25 | service := mocks.NewMockUserDomainService(ctrl) 26 | controller := NewUserControllerInterface(service) 27 | 28 | t.Run("validation_got_error", func(t *testing.T) { 29 | recorder := httptest.NewRecorder() 30 | context := GetTestGinContext(recorder) 31 | 32 | userRequest := request.UserLogin{ 33 | Email: "ERROR@_EMAIL", 34 | Password: "teste", 35 | } 36 | 37 | b, _ := json.Marshal(userRequest) 38 | stringReader := io.NopCloser(strings.NewReader(string(b))) 39 | 40 | MakeRequest(context, []gin.Param{}, url.Values{}, "POST", stringReader) 41 | controller.LoginUser(context) 42 | 43 | assert.EqualValues(t, http.StatusBadRequest, recorder.Code) 44 | }) 45 | 46 | t.Run("object_is_valid_but_service_returns_error", func(t *testing.T) { 47 | recorder := httptest.NewRecorder() 48 | context := GetTestGinContext(recorder) 49 | 50 | userRequest := request.UserLogin{ 51 | Email: "test@test.com", 52 | Password: "teste@#@123", 53 | } 54 | 55 | domain := model.NewUserLoginDomain( 56 | userRequest.Email, 57 | userRequest.Password, 58 | ) 59 | 60 | b, _ := json.Marshal(userRequest) 61 | stringReader := io.NopCloser(strings.NewReader(string(b))) 62 | 63 | service.EXPECT().LoginUserServices(domain).Return( 64 | nil, "", rest_err.NewInternalServerError("error test")) 65 | 66 | MakeRequest(context, []gin.Param{}, url.Values{}, "POST", stringReader) 67 | controller.LoginUser(context) 68 | 69 | assert.EqualValues(t, http.StatusInternalServerError, recorder.Code) 70 | }) 71 | 72 | t.Run("object_is_valid_and_service_returns_success", func(t *testing.T) { 73 | recorder := httptest.NewRecorder() 74 | context := GetTestGinContext(recorder) 75 | id := primitive.NewObjectID().Hex() 76 | 77 | userRequest := request.UserLogin{ 78 | Email: "test@test.com", 79 | Password: "teste@#@123", 80 | } 81 | 82 | domain := model.NewUserLoginDomain( 83 | userRequest.Email, 84 | userRequest.Password, 85 | ) 86 | 87 | b, _ := json.Marshal(userRequest) 88 | stringReader := io.NopCloser(strings.NewReader(string(b))) 89 | 90 | service.EXPECT().LoginUserServices(domain).Return( 91 | domain, id, nil) 92 | 93 | MakeRequest(context, []gin.Param{}, url.Values{}, "POST", stringReader) 94 | controller.LoginUser(context) 95 | 96 | assert.EqualValues(t, http.StatusOK, recorder.Code) 97 | assert.EqualValues(t, recorder.Header().Values("Authorization")[0], id) 98 | }) 99 | } 100 | -------------------------------------------------------------------------------- /src/controller/model/request/user_login.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | // UserLogin represents the data required for user login. 4 | // @Summary User Login Data 5 | // @Description Structure containing the necessary fields for user login. 6 | type UserLogin struct { 7 | // User's email (required and must be a valid email address). 8 | Email string `json:"email" binding:"required,email" example:"test@test.com"` 9 | 10 | // User's password (required, minimum of 6 characters, and must contain at least one of the characters: !@#$%*). 11 | Password string `json:"password" binding:"required,min=6,containsany=!@#$%*" example:"password#@#@!2121"` 12 | } 13 | -------------------------------------------------------------------------------- /src/controller/model/request/user_request.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | // UserRequest represents the input data for creating a new user. 4 | // @Summary User Input Data 5 | // @Description Structure containing the required fields for creating a new user. 6 | type UserRequest struct { 7 | // User's email (required and must be a valid email address). 8 | // Example: user@example.com 9 | // @json 10 | // @jsonTag email 11 | // @jsonExample user@example.com 12 | // @binding required,email 13 | Email string `json:"email" binding:"required,email" example:"test@test.com"` 14 | 15 | // User's password (required, minimum of 6 characters, and must contain at least one of the characters: !@#$%*). 16 | // @json 17 | // @jsonTag password 18 | // @jsonExample P@ssw0rd! 19 | // @binding required,min=6,containsany=!@#$%* 20 | Password string `json:"password" binding:"required,min=6,containsany=!@#$%*" example:"password#@#@!2121"` 21 | 22 | // User's name (required, minimum of 4 characters, maximum of 100 characters). 23 | // Example: John Doe 24 | // @json 25 | // @jsonTag name 26 | // @jsonExample John Doe 27 | // @binding required,min=4,max=100 28 | Name string `json:"name" binding:"required,min=4,max=100" example:"John Doe"` 29 | 30 | // User's age (required, must be between 1 and 140). 31 | // @json 32 | // @jsonTag age 33 | // @jsonExample 30 34 | Age int8 `json:"age" binding:"required,min=1,max=140" example:"30"` 35 | } 36 | 37 | type UserUpdateRequest struct { 38 | // User's name (required, minimum of 4 characters, maximum of 100 characters). 39 | // Example: John Doe 40 | // @json 41 | // @jsonTag name 42 | // @jsonExample John Doe 43 | // @binding required,min=4,max=100 44 | Name string `json:"name" binding:"required,min=4,max=100" example:"John Doe"` 45 | 46 | // User's age (required, must be between 1 and 140). 47 | // @json 48 | // @jsonTag age 49 | // @jsonExample 30 50 | // @binding required,min=1,max=140 51 | Age int8 `json:"age" binding:"required,min=1,max=140" example:"30"` 52 | } 53 | -------------------------------------------------------------------------------- /src/controller/model/response/user_response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type UserResponse struct { 4 | ID string `json:"id" example:"82bdd399-321b-41d8-8b40-9a0116db9e92"` 5 | Email string `json:"email" example:"test@test.com"` 6 | Name string `json:"name" example:"John Doe"` 7 | Age int8 `json:"age" example:"30"` 8 | } 9 | -------------------------------------------------------------------------------- /src/controller/routes/routes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | "github.com/gin-gonic/gin" 7 | 8 | swaggerfiles "github.com/swaggo/files" 9 | ginSwagger "github.com/swaggo/gin-swagger" 10 | ) 11 | 12 | func InitRoutes( 13 | r *gin.RouterGroup, 14 | userController controller.UserControllerInterface) { 15 | 16 | r.GET("/getUserById/:userId", model.VerifyTokenMiddleware, userController.FindUserByID) 17 | r.GET("/getUserByEmail/:userEmail", model.VerifyTokenMiddleware, userController.FindUserByEmail) 18 | r.POST("/createUser", userController.CreateUser) 19 | r.PUT("/updateUser/:userId", model.VerifyTokenMiddleware, userController.UpdateUser) 20 | r.DELETE("/deleteUser/:userId", model.VerifyTokenMiddleware, userController.DeleteUser) 21 | 22 | r.POST("/login", userController.LoginUser) 23 | 24 | r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) 25 | } 26 | -------------------------------------------------------------------------------- /src/controller/update_user.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/validation" 9 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 10 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 11 | "github.com/gin-gonic/gin" 12 | "go.mongodb.org/mongo-driver/bson/primitive" 13 | "go.uber.org/zap" 14 | ) 15 | 16 | // UpdateUser updates user information with the specified ID. 17 | // @Summary Update User 18 | // @Description Updates user details based on the ID provided as a parameter. 19 | // @Tags Users 20 | // @Accept json 21 | // @Produce json 22 | // @Param userId path string true "ID of the user to be updated" 23 | // @Param userRequest body request.UserUpdateRequest true "User information for update" 24 | // @Param Authorization header string true "Insert your access token" default(Bearer ) 25 | // @Success 200 26 | // @Failure 400 {object} rest_err.RestErr 27 | // @Failure 500 {object} rest_err.RestErr 28 | // @Router /updateUser/{userId} [put] 29 | func (uc *userControllerInterface) UpdateUser(c *gin.Context) { 30 | logger.Info("Init updateUser controller", 31 | zap.String("journey", "updateUser"), 32 | ) 33 | var userRequest request.UserUpdateRequest 34 | 35 | if err := c.ShouldBindJSON(&userRequest); err != nil { 36 | logger.Error("Error trying to validate user info", err, 37 | zap.String("journey", "updateUser")) 38 | errRest := validation.ValidateUserError(err) 39 | 40 | c.JSON(errRest.Code, errRest) 41 | return 42 | } 43 | 44 | userId := c.Param("userId") 45 | if _, err := primitive.ObjectIDFromHex(userId); err != nil { 46 | errRest := rest_err.NewBadRequestError("Invalid userId, must be a hex value") 47 | c.JSON(errRest.Code, errRest) 48 | } 49 | 50 | domain := model.NewUserUpdateDomain( 51 | userRequest.Name, 52 | userRequest.Age, 53 | ) 54 | err := uc.service.UpdateUser(userId, domain) 55 | if err != nil { 56 | logger.Error( 57 | "Error trying to call updateUser service", 58 | err, 59 | zap.String("journey", "updateUser")) 60 | c.JSON(err.Code, err) 61 | return 62 | } 63 | 64 | logger.Info( 65 | "updateUser controller executed successfully", 66 | zap.String("userId", userId), 67 | zap.String("journey", "updateUser")) 68 | 69 | c.Status(http.StatusOK) 70 | } 71 | -------------------------------------------------------------------------------- /src/controller/update_user_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 9 | "github.com/gin-gonic/gin" 10 | "github.com/golang/mock/gomock" 11 | "github.com/stretchr/testify/assert" 12 | "go.mongodb.org/mongo-driver/bson/primitive" 13 | "io" 14 | "net/http" 15 | "net/http/httptest" 16 | "net/url" 17 | "strings" 18 | "testing" 19 | ) 20 | 21 | func TestUserControllerInterface_UpdateUser(t *testing.T) { 22 | ctrl := gomock.NewController(t) 23 | defer ctrl.Finish() 24 | 25 | service := mocks.NewMockUserDomainService(ctrl) 26 | controller := NewUserControllerInterface(service) 27 | 28 | t.Run("validation_body_got_error", func(t *testing.T) { 29 | recorder := httptest.NewRecorder() 30 | context := GetTestGinContext(recorder) 31 | 32 | userRequest := request.UserUpdateRequest{ 33 | Name: "", 34 | Age: -1, 35 | } 36 | 37 | b, _ := json.Marshal(userRequest) 38 | stringReader := io.NopCloser(strings.NewReader(string(b))) 39 | 40 | MakeRequest(context, []gin.Param{}, url.Values{}, "PUT", stringReader) 41 | controller.UpdateUser(context) 42 | 43 | assert.EqualValues(t, http.StatusBadRequest, recorder.Code) 44 | }) 45 | 46 | t.Run("validation_userId_got_error", func(t *testing.T) { 47 | recorder := httptest.NewRecorder() 48 | context := GetTestGinContext(recorder) 49 | 50 | userRequest := request.UserUpdateRequest{ 51 | Name: "teste user", 52 | Age: 10, 53 | } 54 | 55 | param := []gin.Param{ 56 | { 57 | Key: "userId", 58 | Value: "test", 59 | }, 60 | } 61 | 62 | b, _ := json.Marshal(userRequest) 63 | stringReader := io.NopCloser(strings.NewReader(string(b))) 64 | 65 | MakeRequest(context, param, url.Values{}, "PUT", stringReader) 66 | controller.UpdateUser(context) 67 | 68 | assert.EqualValues(t, http.StatusBadRequest, recorder.Code) 69 | }) 70 | 71 | t.Run("object_is_valid_but_service_returns_error", func(t *testing.T) { 72 | recorder := httptest.NewRecorder() 73 | context := GetTestGinContext(recorder) 74 | id := primitive.NewObjectID().Hex() 75 | 76 | param := []gin.Param{ 77 | { 78 | Key: "userId", 79 | Value: id, 80 | }, 81 | } 82 | 83 | userRequest := request.UserUpdateRequest{ 84 | Name: "Test User test", 85 | Age: 10, 86 | } 87 | 88 | domain := model.NewUserUpdateDomain( 89 | userRequest.Name, 90 | userRequest.Age, 91 | ) 92 | 93 | b, _ := json.Marshal(userRequest) 94 | stringReader := io.NopCloser(strings.NewReader(string(b))) 95 | 96 | service.EXPECT().UpdateUser(id, domain).Return(rest_err.NewInternalServerError("error test")) 97 | 98 | MakeRequest(context, param, url.Values{}, "PUT", stringReader) 99 | controller.UpdateUser(context) 100 | 101 | assert.EqualValues(t, http.StatusInternalServerError, recorder.Code) 102 | }) 103 | 104 | t.Run("object_is_valid_and_service_returns_success", func(t *testing.T) { 105 | recorder := httptest.NewRecorder() 106 | context := GetTestGinContext(recorder) 107 | id := primitive.NewObjectID().Hex() 108 | 109 | param := []gin.Param{ 110 | { 111 | Key: "userId", 112 | Value: id, 113 | }, 114 | } 115 | 116 | userRequest := request.UserUpdateRequest{ 117 | Name: "Test User test", 118 | Age: 10, 119 | } 120 | 121 | domain := model.NewUserUpdateDomain( 122 | userRequest.Name, 123 | userRequest.Age, 124 | ) 125 | 126 | b, _ := json.Marshal(userRequest) 127 | stringReader := io.NopCloser(strings.NewReader(string(b))) 128 | 129 | service.EXPECT().UpdateUser(id, domain).Return(nil) 130 | 131 | MakeRequest(context, param, url.Values{}, "PUT", stringReader) 132 | controller.UpdateUser(context) 133 | 134 | assert.EqualValues(t, http.StatusOK, recorder.Code) 135 | }) 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/controller/user_controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/service" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func NewUserControllerInterface( 9 | serviceInterface service.UserDomainService, 10 | ) UserControllerInterface { 11 | return &userControllerInterface{ 12 | service: serviceInterface, 13 | } 14 | } 15 | 16 | type UserControllerInterface interface { 17 | FindUserByID(c *gin.Context) 18 | FindUserByEmail(c *gin.Context) 19 | 20 | DeleteUser(c *gin.Context) 21 | CreateUser(c *gin.Context) 22 | UpdateUser(c *gin.Context) 23 | LoginUser(c *gin.Context) 24 | } 25 | 26 | type userControllerInterface struct { 27 | service service.UserDomainService 28 | } 29 | -------------------------------------------------------------------------------- /src/model/repository/create_user_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 9 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 10 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity/converter" 11 | "go.mongodb.org/mongo-driver/bson/primitive" 12 | "go.uber.org/zap" 13 | ) 14 | 15 | func (ur *userRepository) CreateUser( 16 | userDomain model.UserDomainInterface, 17 | ) (model.UserDomainInterface, *rest_err.RestErr) { 18 | logger.Info("Init createUser repository", 19 | zap.String("journey", "createUser")) 20 | 21 | collection_name := os.Getenv(MONGODB_USER_DB) 22 | 23 | collection := ur.databaseConnection.Collection(collection_name) 24 | 25 | value := converter.ConvertDomainToEntity(userDomain) 26 | 27 | result, err := collection.InsertOne(context.Background(), value) 28 | if err != nil { 29 | logger.Error("Error trying to create user", 30 | err, 31 | zap.String("journey", "createUser")) 32 | return nil, rest_err.NewInternalServerError(err.Error()) 33 | } 34 | 35 | value.ID = result.InsertedID.(primitive.ObjectID) 36 | 37 | logger.Info( 38 | "CreateUser repository executed successfully", 39 | zap.String("userId", value.ID.Hex()), 40 | zap.String("journey", "createUser")) 41 | 42 | return converter.ConvertEntityToDomain(*value), nil 43 | } 44 | -------------------------------------------------------------------------------- /src/model/repository/create_user_repository_test.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 5 | "github.com/stretchr/testify/assert" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/bson/primitive" 8 | "go.mongodb.org/mongo-driver/mongo/integration/mtest" 9 | "os" 10 | "testing" 11 | ) 12 | 13 | func TestUserRepository_CreateUser(t *testing.T) { 14 | databaseName := "user_database_test" 15 | collectionName := "user_collection_test" 16 | 17 | err := os.Setenv("MONGODB_USER_DB", collectionName) 18 | if err != nil { 19 | t.FailNow() 20 | return 21 | } 22 | defer os.Clearenv() 23 | 24 | mtestDb := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) 25 | defer mtestDb.Close() 26 | 27 | mtestDb.Run("when_sending_a_valid_domain_returns_success", func(mt *mtest.T) { 28 | mt.AddMockResponses(bson.D{ 29 | {Key: "ok", Value: 1}, 30 | {Key: "n", Value: 1}, 31 | {Key: "acknowledged", Value: true}, 32 | }) 33 | databaseMock := mt.Client.Database(databaseName) 34 | 35 | repo := NewUserRepository(databaseMock) 36 | domain := model.NewUserDomain( 37 | "test@test.com", "test", "test", 90) 38 | userDomain, err := repo.CreateUser(domain) 39 | 40 | _, errId := primitive.ObjectIDFromHex(userDomain.GetID()) 41 | 42 | assert.Nil(t, err) 43 | assert.Nil(t, errId) 44 | assert.EqualValues(t, userDomain.GetEmail(), domain.GetEmail()) 45 | assert.EqualValues(t, userDomain.GetName(), domain.GetName()) 46 | assert.EqualValues(t, userDomain.GetAge(), domain.GetAge()) 47 | assert.EqualValues(t, userDomain.GetPassword(), domain.GetPassword()) 48 | }) 49 | 50 | mtestDb.Run("return_error_from_database", func(mt *mtest.T) { 51 | mt.AddMockResponses(bson.D{ 52 | {Key: "ok", Value: 0}, 53 | }) 54 | databaseMock := mt.Client.Database(databaseName) 55 | 56 | repo := NewUserRepository(databaseMock) 57 | domain := model.NewUserDomain( 58 | "test@test.com", "test", "test", 90) 59 | userDomain, err := repo.CreateUser(domain) 60 | 61 | assert.NotNil(t, err) 62 | assert.Nil(t, userDomain) 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /src/model/repository/delete_user_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 9 | "go.mongodb.org/mongo-driver/bson" 10 | "go.mongodb.org/mongo-driver/bson/primitive" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | func (ur *userRepository) DeleteUser( 15 | userId string, 16 | ) *rest_err.RestErr { 17 | logger.Info("Init deleteUser repository", 18 | zap.String("journey", "deleteUser")) 19 | 20 | collection_name := os.Getenv(MONGODB_USER_DB) 21 | collection := ur.databaseConnection.Collection(collection_name) 22 | 23 | userIdHex, _ := primitive.ObjectIDFromHex(userId) 24 | 25 | filter := bson.D{{Key: "_id", Value: userIdHex}} 26 | 27 | _, err := collection.DeleteOne(context.Background(), filter) 28 | if err != nil { 29 | logger.Error("Error trying to delete user", 30 | err, 31 | zap.String("journey", "deleteUser")) 32 | return rest_err.NewInternalServerError(err.Error()) 33 | } 34 | 35 | logger.Info( 36 | "deleteUser repository executed successfully", 37 | zap.String("userId", userId), 38 | zap.String("journey", "deleteUser")) 39 | 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /src/model/repository/delete_user_repository_test.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "go.mongodb.org/mongo-driver/bson" 6 | "go.mongodb.org/mongo-driver/mongo/integration/mtest" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | func TestUserRepository_DeleteUser(t *testing.T) { 12 | databaseName := "user_database_test" 13 | collectionName := "user_collection_test" 14 | 15 | err := os.Setenv("MONGODB_USER_DB", collectionName) 16 | if err != nil { 17 | t.FailNow() 18 | return 19 | } 20 | defer os.Clearenv() 21 | 22 | mtestDb := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) 23 | defer mtestDb.Close() 24 | 25 | mtestDb.Run("when_sending_a_valid_userId_return_success", func(mt *mtest.T) { 26 | mt.AddMockResponses(bson.D{ 27 | {Key: "ok", Value: 1}, 28 | {Key: "n", Value: 1}, 29 | {Key: "acknowledged", Value: true}, 30 | }) 31 | databaseMock := mt.Client.Database(databaseName) 32 | 33 | repo := NewUserRepository(databaseMock) 34 | err := repo.DeleteUser("test") 35 | 36 | assert.Nil(t, err) 37 | }) 38 | 39 | mtestDb.Run("return_error_from_database", func(mt *mtest.T) { 40 | mt.AddMockResponses(bson.D{ 41 | {Key: "ok", Value: 0}, 42 | }) 43 | databaseMock := mt.Client.Database(databaseName) 44 | 45 | repo := NewUserRepository(databaseMock) 46 | err := repo.DeleteUser("test") 47 | 48 | assert.NotNil(t, err) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /src/model/repository/entity/converter/convert_domain_to_entity.go: -------------------------------------------------------------------------------- 1 | package converter 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity" 6 | ) 7 | 8 | func ConvertDomainToEntity( 9 | domain model.UserDomainInterface, 10 | ) *entity.UserEntity { 11 | return &entity.UserEntity{ 12 | Email: domain.GetEmail(), 13 | Password: domain.GetPassword(), 14 | Name: domain.GetName(), 15 | Age: domain.GetAge(), 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/model/repository/entity/converter/convert_entity_to_domain.go: -------------------------------------------------------------------------------- 1 | package converter 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity" 6 | ) 7 | 8 | func ConvertEntityToDomain( 9 | entity entity.UserEntity, 10 | ) model.UserDomainInterface { 11 | domain := model.NewUserDomain( 12 | entity.Email, 13 | entity.Password, 14 | entity.Name, 15 | entity.Age, 16 | ) 17 | 18 | domain.SetID(entity.ID.Hex()) 19 | return domain 20 | } 21 | -------------------------------------------------------------------------------- /src/model/repository/entity/user_entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "go.mongodb.org/mongo-driver/bson/primitive" 4 | 5 | type UserEntity struct { 6 | ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` 7 | Email string `bson:"email,omitempty"` 8 | Password string `bson:"password,omitempty"` 9 | Name string `bson:"name,omitempty"` 10 | Age int8 `bson:"age,omitempty"` 11 | } 12 | -------------------------------------------------------------------------------- /src/model/repository/find_user_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 9 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 10 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 11 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity" 12 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity/converter" 13 | "go.mongodb.org/mongo-driver/bson" 14 | "go.mongodb.org/mongo-driver/bson/primitive" 15 | "go.mongodb.org/mongo-driver/mongo" 16 | "go.uber.org/zap" 17 | ) 18 | 19 | func (ur *userRepository) FindUserByEmail( 20 | email string, 21 | ) (model.UserDomainInterface, *rest_err.RestErr) { 22 | logger.Info("Init findUserByEmail repository", 23 | zap.String("journey", "findUserByEmail")) 24 | 25 | collection_name := os.Getenv(MONGODB_USER_DB) 26 | collection := ur.databaseConnection.Collection(collection_name) 27 | 28 | userEntity := &entity.UserEntity{} 29 | 30 | filter := bson.D{{Key: "email", Value: email}} 31 | err := collection.FindOne( 32 | context.Background(), 33 | filter, 34 | ).Decode(userEntity) 35 | 36 | if err != nil { 37 | if err == mongo.ErrNoDocuments { 38 | errorMessage := fmt.Sprintf( 39 | "User not found with this email: %s", email) 40 | logger.Error(errorMessage, 41 | err, 42 | zap.String("journey", "findUserByEmail")) 43 | 44 | return nil, rest_err.NewNotFoundError(errorMessage) 45 | } 46 | errorMessage := "Error trying to find user by email" 47 | logger.Error(errorMessage, 48 | err, 49 | zap.String("journey", "findUserByEmail")) 50 | 51 | return nil, rest_err.NewInternalServerError(errorMessage) 52 | } 53 | 54 | logger.Info("FindUserByEmail repository executed successfully", 55 | zap.String("journey", "findUserByEmail"), 56 | zap.String("email", email), 57 | zap.String("userId", userEntity.ID.Hex())) 58 | return converter.ConvertEntityToDomain(*userEntity), nil 59 | } 60 | 61 | func (ur *userRepository) FindUserByID( 62 | id string, 63 | ) (model.UserDomainInterface, *rest_err.RestErr) { 64 | logger.Info("Init findUserByID repository", 65 | zap.String("journey", "findUserByID")) 66 | 67 | collection_name := os.Getenv(MONGODB_USER_DB) 68 | collection := ur.databaseConnection.Collection(collection_name) 69 | 70 | userEntity := &entity.UserEntity{} 71 | 72 | objectId, _ := primitive.ObjectIDFromHex(id) 73 | filter := bson.D{{Key: "_id", Value: objectId}} 74 | err := collection.FindOne( 75 | context.Background(), 76 | filter, 77 | ).Decode(userEntity) 78 | 79 | if err != nil { 80 | if err == mongo.ErrNoDocuments { 81 | errorMessage := fmt.Sprintf( 82 | "User not found with this ID: %s", id) 83 | logger.Error(errorMessage, 84 | err, 85 | zap.String("journey", "findUserByID")) 86 | 87 | return nil, rest_err.NewNotFoundError(errorMessage) 88 | } 89 | errorMessage := "Error trying to find user by ID" 90 | logger.Error(errorMessage, 91 | err, 92 | zap.String("journey", "findUserByID")) 93 | 94 | return nil, rest_err.NewInternalServerError(errorMessage) 95 | } 96 | 97 | logger.Info("FindUserByID repository executed successfully", 98 | zap.String("journey", "findUserByID"), 99 | zap.String("userId", userEntity.ID.Hex())) 100 | return converter.ConvertEntityToDomain(*userEntity), nil 101 | } 102 | 103 | func (ur *userRepository) FindUserByEmailAndPassword( 104 | email string, 105 | password string, 106 | ) (model.UserDomainInterface, *rest_err.RestErr) { 107 | logger.Info("Init findUserByEmailAndPassword repository", 108 | zap.String("journey", "findUserByEmailAndPassword")) 109 | 110 | collection_name := os.Getenv(MONGODB_USER_DB) 111 | collection := ur.databaseConnection.Collection(collection_name) 112 | 113 | userEntity := &entity.UserEntity{} 114 | 115 | filter := bson.D{ 116 | {Key: "email", Value: email}, 117 | {Key: "password", Value: password}, 118 | } 119 | err := collection.FindOne( 120 | context.Background(), 121 | filter, 122 | ).Decode(userEntity) 123 | 124 | if err != nil { 125 | if err == mongo.ErrNoDocuments { 126 | errorMessage := "User or password is invalid" 127 | logger.Error(errorMessage, 128 | err, 129 | zap.String("journey", "findUserByEmailAndPassword")) 130 | 131 | return nil, rest_err.NewForbiddenError(errorMessage) 132 | } 133 | errorMessage := "Error trying to find user by email and password" 134 | logger.Error(errorMessage, 135 | err, 136 | zap.String("journey", "findUserByEmailAndPassword")) 137 | 138 | return nil, rest_err.NewInternalServerError(errorMessage) 139 | } 140 | 141 | logger.Info("FindUserByEmailAndPassword repository executed successfully", 142 | zap.String("journey", "findUserByEmailAndPassword"), 143 | zap.String("email", email), 144 | zap.String("userId", userEntity.ID.Hex())) 145 | return converter.ConvertEntityToDomain(*userEntity), nil 146 | } 147 | -------------------------------------------------------------------------------- /src/model/repository/find_user_repository_test.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "fmt" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity" 6 | "github.com/stretchr/testify/assert" 7 | "go.mongodb.org/mongo-driver/bson" 8 | "go.mongodb.org/mongo-driver/bson/primitive" 9 | "go.mongodb.org/mongo-driver/mongo/integration/mtest" 10 | "os" 11 | "testing" 12 | ) 13 | 14 | func TestUserRepository_FindUserByEmail(t *testing.T) { 15 | databaseName := "user_database_test" 16 | collectionName := "user_collection_test" 17 | 18 | err := os.Setenv("MONGODB_USER_DB", collectionName) 19 | if err != nil { 20 | t.FailNow() 21 | return 22 | } 23 | defer os.Clearenv() 24 | 25 | mtestDb := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) 26 | defer mtestDb.Close() 27 | 28 | mtestDb.Run("when_sending_a_valid_email_returns_success", func(mt *mtest.T) { 29 | userEntity := entity.UserEntity{ 30 | ID: primitive.NewObjectID(), 31 | Email: "test@test.com", 32 | Password: "test", 33 | Name: "test", 34 | Age: 50, 35 | } 36 | mt.AddMockResponses(mtest.CreateCursorResponse( 37 | 1, 38 | fmt.Sprintf("%s.%s", databaseName, collectionName), 39 | mtest.FirstBatch, 40 | convertEntityToBson(userEntity))) 41 | 42 | databaseMock := mt.Client.Database(databaseName) 43 | 44 | repo := NewUserRepository(databaseMock) 45 | userDomain, err := repo.FindUserByEmail(userEntity.Email) 46 | 47 | assert.Nil(t, err) 48 | assert.EqualValues(t, userDomain.GetID(), userEntity.ID.Hex()) 49 | assert.EqualValues(t, userDomain.GetEmail(), userDomain.GetEmail()) 50 | assert.EqualValues(t, userDomain.GetName(), userDomain.GetName()) 51 | assert.EqualValues(t, userDomain.GetAge(), userDomain.GetAge()) 52 | assert.EqualValues(t, userDomain.GetPassword(), userDomain.GetPassword()) 53 | }) 54 | 55 | mtestDb.Run("returns_error_when_mongodb_returns_error", func(mt *mtest.T) { 56 | mt.AddMockResponses(bson.D{ 57 | {Key: "ok", Value: 0}, 58 | }) 59 | 60 | databaseMock := mt.Client.Database(databaseName) 61 | 62 | repo := NewUserRepository(databaseMock) 63 | userDomain, err := repo.FindUserByEmail("test") 64 | 65 | assert.NotNil(t, err) 66 | assert.Nil(t, userDomain) 67 | }) 68 | 69 | mtestDb.Run("returns_no_document_found", func(mt *mtest.T) { 70 | mt.AddMockResponses(mtest.CreateCursorResponse( 71 | 0, 72 | fmt.Sprintf("%s.%s", databaseName, collectionName), 73 | mtest.FirstBatch)) 74 | 75 | databaseMock := mt.Client.Database(databaseName) 76 | 77 | repo := NewUserRepository(databaseMock) 78 | userDomain, err := repo.FindUserByEmail("test") 79 | 80 | assert.NotNil(t, err) 81 | assert.Nil(t, userDomain) 82 | }) 83 | } 84 | 85 | func TestUserRepository_FindUserByEmailAndPassword(t *testing.T) { 86 | databaseName := "user_database_test" 87 | collectionName := "user_collection_test" 88 | 89 | err := os.Setenv("MONGODB_USER_DB", collectionName) 90 | if err != nil { 91 | t.FailNow() 92 | return 93 | } 94 | defer os.Clearenv() 95 | 96 | mtestDb := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) 97 | defer mtestDb.Close() 98 | 99 | mtestDb.Run("when_sending_a_valid_email_and_password_returns_success", func(mt *mtest.T) { 100 | userEntity := entity.UserEntity{ 101 | ID: primitive.NewObjectID(), 102 | Email: "test@test.com", 103 | Password: "test", 104 | Name: "test", 105 | Age: 50, 106 | } 107 | mt.AddMockResponses(mtest.CreateCursorResponse( 108 | 1, 109 | fmt.Sprintf("%s.%s", databaseName, collectionName), 110 | mtest.FirstBatch, 111 | convertEntityToBson(userEntity))) 112 | 113 | databaseMock := mt.Client.Database(databaseName) 114 | 115 | repo := NewUserRepository(databaseMock) 116 | userDomain, err := repo.FindUserByEmailAndPassword(userEntity.Email, userEntity.Password) 117 | 118 | assert.Nil(t, err) 119 | assert.EqualValues(t, userDomain.GetID(), userEntity.ID.Hex()) 120 | assert.EqualValues(t, userDomain.GetEmail(), userDomain.GetEmail()) 121 | assert.EqualValues(t, userDomain.GetName(), userDomain.GetName()) 122 | assert.EqualValues(t, userDomain.GetAge(), userDomain.GetAge()) 123 | assert.EqualValues(t, userDomain.GetPassword(), userDomain.GetPassword()) 124 | }) 125 | 126 | mtestDb.Run("returns_error_when_mongodb_returns_error", func(mt *mtest.T) { 127 | mt.AddMockResponses(bson.D{ 128 | {Key: "ok", Value: 0}, 129 | }) 130 | 131 | databaseMock := mt.Client.Database(databaseName) 132 | 133 | repo := NewUserRepository(databaseMock) 134 | userDomain, err := repo.FindUserByEmailAndPassword("test", "testpass") 135 | assert.NotNil(t, err) 136 | assert.Nil(t, userDomain) 137 | }) 138 | 139 | mtestDb.Run("returns_no_document_found", func(mt *mtest.T) { 140 | mt.AddMockResponses(mtest.CreateCursorResponse( 141 | 0, 142 | fmt.Sprintf("%s.%s", databaseName, collectionName), 143 | mtest.FirstBatch)) 144 | 145 | databaseMock := mt.Client.Database(databaseName) 146 | 147 | repo := NewUserRepository(databaseMock) 148 | userDomain, err := repo.FindUserByEmailAndPassword("test", "testpass") 149 | 150 | assert.NotNil(t, err) 151 | assert.Nil(t, userDomain) 152 | }) 153 | } 154 | 155 | func TestUserRepository_FindUserById(t *testing.T) { 156 | databaseName := "user_database_test" 157 | collectionName := "user_collection_test" 158 | 159 | err := os.Setenv("MONGODB_USER_DB", collectionName) 160 | if err != nil { 161 | t.FailNow() 162 | return 163 | } 164 | defer os.Clearenv() 165 | 166 | mtestDb := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) 167 | defer mtestDb.Close() 168 | 169 | mtestDb.Run("when_sending_a_valid_id_returns_success", func(mt *mtest.T) { 170 | userEntity := entity.UserEntity{ 171 | ID: primitive.NewObjectID(), 172 | Email: "test@test.com", 173 | Password: "test", 174 | Name: "test", 175 | Age: 50, 176 | } 177 | mt.AddMockResponses(mtest.CreateCursorResponse( 178 | 1, 179 | fmt.Sprintf("%s.%s", databaseName, collectionName), 180 | mtest.FirstBatch, 181 | convertEntityToBson(userEntity))) 182 | 183 | databaseMock := mt.Client.Database(databaseName) 184 | 185 | repo := NewUserRepository(databaseMock) 186 | userDomain, err := repo.FindUserByID(userEntity.ID.Hex()) 187 | 188 | assert.Nil(t, err) 189 | assert.EqualValues(t, userDomain.GetID(), userEntity.ID.Hex()) 190 | assert.EqualValues(t, userDomain.GetEmail(), userDomain.GetEmail()) 191 | assert.EqualValues(t, userDomain.GetName(), userDomain.GetName()) 192 | assert.EqualValues(t, userDomain.GetAge(), userDomain.GetAge()) 193 | assert.EqualValues(t, userDomain.GetPassword(), userDomain.GetPassword()) 194 | }) 195 | 196 | mtestDb.Run("returns_error_when_mongodb_returns_error", func(mt *mtest.T) { 197 | mt.AddMockResponses(bson.D{ 198 | {Key: "ok", Value: 0}, 199 | }) 200 | 201 | databaseMock := mt.Client.Database(databaseName) 202 | 203 | repo := NewUserRepository(databaseMock) 204 | userDomain, err := repo.FindUserByID("test") 205 | 206 | assert.NotNil(t, err) 207 | assert.Nil(t, userDomain) 208 | }) 209 | 210 | mtestDb.Run("returns_no_document_found", func(mt *mtest.T) { 211 | mt.AddMockResponses(mtest.CreateCursorResponse( 212 | 0, 213 | fmt.Sprintf("%s.%s", databaseName, collectionName), 214 | mtest.FirstBatch)) 215 | 216 | databaseMock := mt.Client.Database(databaseName) 217 | 218 | repo := NewUserRepository(databaseMock) 219 | userDomain, err := repo.FindUserByID("test") 220 | 221 | assert.NotNil(t, err) 222 | assert.Equal(t, err.Message, fmt.Sprintf("User not found with this ID: test")) 223 | assert.Nil(t, userDomain) 224 | }) 225 | } 226 | 227 | func convertEntityToBson(userEntity entity.UserEntity) bson.D { 228 | return bson.D{ 229 | {Key: "_id", Value: userEntity.ID}, 230 | {Key: "email", Value: userEntity.Email}, 231 | {Key: "password", Value: userEntity.Password}, 232 | {Key: "name", Value: userEntity.Name}, 233 | {Key: "age", Value: userEntity.Age}, 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/model/repository/update_user_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 9 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 10 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity/converter" 11 | "go.mongodb.org/mongo-driver/bson" 12 | "go.mongodb.org/mongo-driver/bson/primitive" 13 | "go.uber.org/zap" 14 | ) 15 | 16 | func (ur *userRepository) UpdateUser( 17 | userId string, 18 | userDomain model.UserDomainInterface, 19 | ) *rest_err.RestErr { 20 | logger.Info("Init updateUser repository", 21 | zap.String("journey", "updateUser")) 22 | 23 | collection_name := os.Getenv(MONGODB_USER_DB) 24 | collection := ur.databaseConnection.Collection(collection_name) 25 | 26 | value := converter.ConvertDomainToEntity(userDomain) 27 | userIdHex, _ := primitive.ObjectIDFromHex(userId) 28 | 29 | filter := bson.D{{Key: "_id", Value: userIdHex}} 30 | update := bson.D{{Key: "$set", Value: value}} 31 | 32 | _, err := collection.UpdateOne(context.Background(), filter, update) 33 | if err != nil { 34 | logger.Error("Error trying to update user", 35 | err, 36 | zap.String("journey", "updateUser")) 37 | return rest_err.NewInternalServerError(err.Error()) 38 | } 39 | 40 | logger.Info( 41 | "updateUser repository executed successfully", 42 | zap.String("userId", userId), 43 | zap.String("journey", "updateUser")) 44 | 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /src/model/repository/update_user_repository_test.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 5 | "github.com/stretchr/testify/assert" 6 | "go.mongodb.org/mongo-driver/bson" 7 | "go.mongodb.org/mongo-driver/bson/primitive" 8 | "go.mongodb.org/mongo-driver/mongo/integration/mtest" 9 | "os" 10 | "testing" 11 | ) 12 | 13 | func TestUserRepository_UpdateUser(t *testing.T) { 14 | databaseName := "user_database_test" 15 | collectionName := "user_collection_test" 16 | 17 | err := os.Setenv("MONGODB_USER_DB", collectionName) 18 | if err != nil { 19 | t.FailNow() 20 | return 21 | } 22 | defer os.Clearenv() 23 | 24 | mtestDb := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) 25 | defer mtestDb.Close() 26 | 27 | mtestDb.Run("when_sending_a_valid_user_return_success", func(mt *mtest.T) { 28 | mt.AddMockResponses(bson.D{ 29 | {Key: "ok", Value: 1}, 30 | {Key: "n", Value: 1}, 31 | {Key: "acknowledged", Value: true}, 32 | }) 33 | databaseMock := mt.Client.Database(databaseName) 34 | 35 | repo := NewUserRepository(databaseMock) 36 | domain := model.NewUserDomain( 37 | "test@test.com", "test", "test", 90) 38 | domain.SetID(primitive.NewObjectID().Hex()) 39 | 40 | err := repo.UpdateUser(domain.GetID(), domain) 41 | 42 | assert.Nil(t, err) 43 | }) 44 | 45 | mtestDb.Run("return_error_from_database", func(mt *mtest.T) { 46 | mt.AddMockResponses(bson.D{ 47 | {Key: "ok", Value: 0}, 48 | }) 49 | databaseMock := mt.Client.Database(databaseName) 50 | 51 | repo := NewUserRepository(databaseMock) 52 | domain := model.NewUserDomain( 53 | "test@test.com", "test", "test", 90) 54 | domain.SetID(primitive.NewObjectID().Hex()) 55 | err := repo.UpdateUser(domain.GetID(), domain) 56 | 57 | assert.NotNil(t, err) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /src/model/repository/user_repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | "go.mongodb.org/mongo-driver/mongo" 7 | ) 8 | 9 | const ( 10 | MONGODB_USER_DB = "MONGODB_USER_DB" 11 | ) 12 | 13 | func NewUserRepository( 14 | database *mongo.Database, 15 | ) UserRepository { 16 | return &userRepository{ 17 | database, 18 | } 19 | } 20 | 21 | type userRepository struct { 22 | databaseConnection *mongo.Database 23 | } 24 | 25 | type UserRepository interface { 26 | CreateUser( 27 | userDomain model.UserDomainInterface, 28 | ) (model.UserDomainInterface, *rest_err.RestErr) 29 | 30 | UpdateUser( 31 | userId string, 32 | userDomain model.UserDomainInterface, 33 | ) *rest_err.RestErr 34 | 35 | DeleteUser( 36 | userId string, 37 | ) *rest_err.RestErr 38 | 39 | FindUserByEmail( 40 | email string, 41 | ) (model.UserDomainInterface, *rest_err.RestErr) 42 | FindUserByEmailAndPassword( 43 | email string, 44 | password string, 45 | ) (model.UserDomainInterface, *rest_err.RestErr) 46 | FindUserByID( 47 | id string, 48 | ) (model.UserDomainInterface, *rest_err.RestErr) 49 | } 50 | -------------------------------------------------------------------------------- /src/model/service/create_user.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 7 | "go.uber.org/zap" 8 | ) 9 | 10 | func (ud *userDomainService) CreateUserServices( 11 | userDomain model.UserDomainInterface, 12 | ) (model.UserDomainInterface, *rest_err.RestErr) { 13 | 14 | logger.Info("Init createUser model.", 15 | zap.String("journey", "createUser")) 16 | 17 | user, _ := ud.FindUserByEmailServices(userDomain.GetEmail()) 18 | if user != nil { 19 | return nil, rest_err.NewBadRequestError("Email is already registered in another account") 20 | } 21 | 22 | userDomain.EncryptPassword() 23 | userDomainRepository, err := ud.userRepository.CreateUser(userDomain) 24 | if err != nil { 25 | logger.Error("Error trying to call repository", 26 | err, 27 | zap.String("journey", "createUser")) 28 | return nil, err 29 | } 30 | 31 | logger.Info( 32 | "CreateUser service executed successfully", 33 | zap.String("userId", userDomainRepository.GetID()), 34 | zap.String("journey", "createUser")) 35 | return userDomainRepository, nil 36 | } 37 | -------------------------------------------------------------------------------- /src/model/service/create_user_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 7 | "github.com/stretchr/testify/assert" 8 | "go.mongodb.org/mongo-driver/bson/primitive" 9 | "go.uber.org/mock/gomock" 10 | "testing" 11 | ) 12 | 13 | func TestUserDomainService_CreateUserServices(t *testing.T) { 14 | ctrl := gomock.NewController(t) 15 | defer ctrl.Finish() 16 | 17 | repository := mocks.NewMockUserRepository(ctrl) 18 | service := NewUserDomainService(repository) 19 | 20 | t.Run("when_user_already_exists_returns_error", func(t *testing.T) { 21 | id := primitive.NewObjectID().Hex() 22 | 23 | userDomain := model.NewUserDomain("test@test.com", "test", "test", 50) 24 | userDomain.SetID(id) 25 | 26 | repository.EXPECT().FindUserByEmail(userDomain.GetEmail()).Return(userDomain, nil) 27 | 28 | user, err := service.CreateUserServices(userDomain) 29 | 30 | assert.Nil(t, user) 31 | assert.NotNil(t, err) 32 | assert.EqualValues(t, err.Message, "Email is already registered in another account") 33 | }) 34 | 35 | t.Run("when_user_is_not_registered_returns_error", func(t *testing.T) { 36 | id := primitive.NewObjectID().Hex() 37 | 38 | userDomain := model.NewUserDomain("test@test.com", "test", "test", 50) 39 | userDomain.SetID(id) 40 | 41 | repository.EXPECT().FindUserByEmail(userDomain.GetEmail()).Return( 42 | nil, nil) 43 | 44 | repository.EXPECT().CreateUser(userDomain).Return( 45 | nil, rest_err.NewInternalServerError("error trying to create user")) 46 | 47 | user, err := service.CreateUserServices(userDomain) 48 | 49 | assert.Nil(t, user) 50 | assert.NotNil(t, err) 51 | assert.EqualValues(t, err.Message, "error trying to create user") 52 | }) 53 | 54 | t.Run("when_user_is_not_registered_returns_success", func(t *testing.T) { 55 | id := primitive.NewObjectID().Hex() 56 | 57 | userDomain := model.NewUserDomain("test@test.com", "test", "test", 50) 58 | userDomain.SetID(id) 59 | 60 | repository.EXPECT().FindUserByEmail(userDomain.GetEmail()).Return( 61 | nil, nil) 62 | 63 | repository.EXPECT().CreateUser(userDomain).Return( 64 | userDomain, nil) 65 | 66 | user, err := service.CreateUserServices(userDomain) 67 | 68 | assert.Nil(t, err) 69 | assert.EqualValues(t, user.GetName(), userDomain.GetName()) 70 | assert.EqualValues(t, user.GetEmail(), userDomain.GetEmail()) 71 | assert.EqualValues(t, user.GetAge(), userDomain.GetAge()) 72 | assert.EqualValues(t, user.GetID(), userDomain.GetID()) 73 | assert.EqualValues(t, user.GetPassword(), userDomain.GetPassword()) 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /src/model/service/delete_user.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 6 | "go.uber.org/zap" 7 | ) 8 | 9 | func (ud *userDomainService) DeleteUser( 10 | userId string) *rest_err.RestErr { 11 | 12 | logger.Info("Init deleteUser model.", 13 | zap.String("journey", "deleteUser")) 14 | 15 | err := ud.userRepository.DeleteUser(userId) 16 | if err != nil { 17 | logger.Error("Error trying to call repository", 18 | err, 19 | zap.String("journey", "deleteUser")) 20 | return err 21 | } 22 | 23 | logger.Info( 24 | "deleteUser service executed successfully", 25 | zap.String("userId", userId), 26 | zap.String("journey", "deleteUser")) 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /src/model/service/delete_user_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 6 | "github.com/stretchr/testify/assert" 7 | "go.mongodb.org/mongo-driver/bson/primitive" 8 | "go.uber.org/mock/gomock" 9 | "testing" 10 | ) 11 | 12 | func TestUserDomainService_DeleteUser(t *testing.T) { 13 | ctrl := gomock.NewController(t) 14 | defer ctrl.Finish() 15 | 16 | repository := mocks.NewMockUserRepository(ctrl) 17 | service := NewUserDomainService(repository) 18 | 19 | t.Run("when_sending_a_valid_userId_returns_success", func(t *testing.T) { 20 | id := primitive.NewObjectID().Hex() 21 | 22 | repository.EXPECT().DeleteUser(id).Return(nil) 23 | 24 | err := service.DeleteUser(id) 25 | 26 | assert.Nil(t, err) 27 | }) 28 | 29 | t.Run("when_sending_a_invalid_userId_returns_error", func(t *testing.T) { 30 | id := primitive.NewObjectID().Hex() 31 | 32 | repository.EXPECT().DeleteUser(id).Return( 33 | rest_err.NewInternalServerError("error trying to update user")) 34 | 35 | err := service.DeleteUser(id) 36 | 37 | assert.NotNil(t, err) 38 | assert.EqualValues(t, err.Message, "error trying to update user") 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /src/model/service/find_user.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 7 | "go.uber.org/zap" 8 | ) 9 | 10 | func (ud *userDomainService) FindUserByIDServices( 11 | id string, 12 | ) (model.UserDomainInterface, *rest_err.RestErr) { 13 | logger.Info("Init findUserByID services.", 14 | zap.String("journey", "findUserById")) 15 | 16 | return ud.userRepository.FindUserByID(id) 17 | } 18 | 19 | func (ud *userDomainService) FindUserByEmailServices( 20 | email string, 21 | ) (model.UserDomainInterface, *rest_err.RestErr) { 22 | logger.Info("Init findUserByEmail services.", 23 | zap.String("journey", "findUserById")) 24 | 25 | return ud.userRepository.FindUserByEmail(email) 26 | } 27 | 28 | func (ud *userDomainService) findUserByEmailAndPasswordServices( 29 | email string, 30 | password string, 31 | ) (model.UserDomainInterface, *rest_err.RestErr) { 32 | logger.Info("Init findUserByEmail services.", 33 | zap.String("journey", "findUserById")) 34 | 35 | return ud.userRepository.FindUserByEmailAndPassword(email, password) 36 | } 37 | -------------------------------------------------------------------------------- /src/model/service/find_user_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 7 | "github.com/stretchr/testify/assert" 8 | "go.mongodb.org/mongo-driver/bson/primitive" 9 | "go.uber.org/mock/gomock" 10 | "math/rand" 11 | "strconv" 12 | "testing" 13 | ) 14 | 15 | func TestUserDomainService_FindUserByIDServices(t *testing.T) { 16 | ctrl := gomock.NewController(t) 17 | defer ctrl.Finish() 18 | 19 | repository := mocks.NewMockUserRepository(ctrl) 20 | service := NewUserDomainService(repository) 21 | 22 | t.Run("when_exists_an_user_returns_success", func(t *testing.T) { 23 | id := primitive.NewObjectID().Hex() 24 | 25 | userDomain := model.NewUserDomain("test@test.com", "test", "test", 50) 26 | userDomain.SetID(id) 27 | 28 | repository.EXPECT().FindUserByID(id).Return(userDomain, nil) 29 | 30 | userDomainReturn, err := service.FindUserByIDServices(id) 31 | 32 | assert.Nil(t, err) 33 | assert.EqualValues(t, userDomainReturn.GetID(), id) 34 | assert.EqualValues(t, userDomainReturn.GetEmail(), userDomain.GetEmail()) 35 | assert.EqualValues(t, userDomainReturn.GetPassword(), userDomain.GetPassword()) 36 | assert.EqualValues(t, userDomainReturn.GetName(), userDomain.GetName()) 37 | assert.EqualValues(t, userDomainReturn.GetAge(), userDomain.GetAge()) 38 | }) 39 | 40 | t.Run("when_does_not_exists_an_user_returns_error", func(t *testing.T) { 41 | id := primitive.NewObjectID().Hex() 42 | 43 | repository.EXPECT().FindUserByID(id).Return(nil, rest_err.NewNotFoundError("user not found")) 44 | userDomainReturn, err := service.FindUserByIDServices(id) 45 | 46 | assert.Nil(t, userDomainReturn) 47 | assert.NotNil(t, err) 48 | assert.EqualValues(t, err.Message, "user not found") 49 | }) 50 | } 51 | 52 | func TestUserDomainService_FindUserByEmailServices(t *testing.T) { 53 | ctrl := gomock.NewController(t) 54 | defer ctrl.Finish() 55 | 56 | repository := mocks.NewMockUserRepository(ctrl) 57 | service := NewUserDomainService(repository) 58 | 59 | t.Run("when_exists_an_user_returns_success", func(t *testing.T) { 60 | id := primitive.NewObjectID().Hex() 61 | email := "test@success.com" 62 | 63 | userDomain := model.NewUserDomain(email, "test", "test", 50) 64 | userDomain.SetID(id) 65 | 66 | repository.EXPECT().FindUserByEmail(email).Return(userDomain, nil) 67 | 68 | userDomainReturn, err := service.FindUserByEmailServices(email) 69 | 70 | assert.Nil(t, err) 71 | assert.EqualValues(t, userDomainReturn.GetID(), id) 72 | assert.EqualValues(t, userDomainReturn.GetEmail(), userDomain.GetEmail()) 73 | assert.EqualValues(t, userDomainReturn.GetPassword(), userDomain.GetPassword()) 74 | assert.EqualValues(t, userDomainReturn.GetName(), userDomain.GetName()) 75 | assert.EqualValues(t, userDomainReturn.GetAge(), userDomain.GetAge()) 76 | }) 77 | 78 | t.Run("when_does_not_exists_an_user_returns_error", func(t *testing.T) { 79 | email := "test@error.com" 80 | 81 | repository.EXPECT().FindUserByEmail(email).Return(nil, rest_err.NewNotFoundError("user not found")) 82 | userDomainReturn, err := service.FindUserByEmailServices(email) 83 | 84 | assert.Nil(t, userDomainReturn) 85 | assert.NotNil(t, err) 86 | assert.EqualValues(t, err.Message, "user not found") 87 | }) 88 | } 89 | 90 | func TestUserDomainService_FindUserByEmailAndPasswordServices(t *testing.T) { 91 | ctrl := gomock.NewController(t) 92 | defer ctrl.Finish() 93 | 94 | repository := mocks.NewMockUserRepository(ctrl) 95 | service := &userDomainService{repository} 96 | 97 | t.Run("when_exists_an_user_returns_success", func(t *testing.T) { 98 | id := primitive.NewObjectID().Hex() 99 | email := "test@success.com" 100 | password := strconv.FormatInt(rand.Int63(), 10) 101 | 102 | userDomain := model.NewUserDomain(email, password, "test", 50) 103 | userDomain.SetID(id) 104 | 105 | repository.EXPECT().FindUserByEmailAndPassword(email, password).Return(userDomain, nil) 106 | 107 | userDomainReturn, err := service.findUserByEmailAndPasswordServices(email, password) 108 | 109 | assert.Nil(t, err) 110 | assert.EqualValues(t, userDomainReturn.GetID(), id) 111 | assert.EqualValues(t, userDomainReturn.GetEmail(), userDomain.GetEmail()) 112 | assert.EqualValues(t, userDomainReturn.GetPassword(), userDomain.GetPassword()) 113 | assert.EqualValues(t, userDomainReturn.GetName(), userDomain.GetName()) 114 | assert.EqualValues(t, userDomainReturn.GetAge(), userDomain.GetAge()) 115 | }) 116 | 117 | t.Run("when_does_not_exists_an_user_returns_error", func(t *testing.T) { 118 | email := "test@error.com" 119 | password := strconv.FormatInt(rand.Int63(), 10) 120 | 121 | repository.EXPECT().FindUserByEmailAndPassword(email, password).Return(nil, rest_err.NewNotFoundError("user not found")) 122 | userDomainReturn, err := service.findUserByEmailAndPasswordServices(email, password) 123 | 124 | assert.Nil(t, userDomainReturn) 125 | assert.NotNil(t, err) 126 | assert.EqualValues(t, err.Message, "user not found") 127 | }) 128 | } 129 | -------------------------------------------------------------------------------- /src/model/service/login_user.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 7 | "go.uber.org/zap" 8 | ) 9 | 10 | func (ud *userDomainService) LoginUserServices( 11 | userDomain model.UserDomainInterface, 12 | ) (model.UserDomainInterface, string, *rest_err.RestErr) { 13 | 14 | logger.Info("Init loginUser model.", 15 | zap.String("journey", "loginUser")) 16 | 17 | userDomain.EncryptPassword() 18 | 19 | user, err := ud.findUserByEmailAndPasswordServices( 20 | userDomain.GetEmail(), 21 | userDomain.GetPassword(), 22 | ) 23 | if err != nil { 24 | return nil, "", err 25 | } 26 | 27 | token, err := user.GenerateToken() 28 | if err != nil { 29 | return nil, "", err 30 | } 31 | 32 | logger.Info( 33 | "loginUser service executed successfully", 34 | zap.String("userId", user.GetID()), 35 | zap.String("journey", "loginUser")) 36 | return user, token, nil 37 | } 38 | -------------------------------------------------------------------------------- /src/model/service/login_user_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 7 | "github.com/golang-jwt/jwt" 8 | "github.com/stretchr/testify/assert" 9 | "go.mongodb.org/mongo-driver/bson/primitive" 10 | "go.uber.org/mock/gomock" 11 | "os" 12 | "testing" 13 | ) 14 | 15 | func TestUserDomainService_LoginUserServices(t *testing.T) { 16 | ctrl := gomock.NewController(t) 17 | defer ctrl.Finish() 18 | 19 | repository := mocks.NewMockUserRepository(ctrl) 20 | service := NewUserDomainService(repository) 21 | 22 | t.Run("when_calling_repository_returns_error", func(t *testing.T) { 23 | id := primitive.NewObjectID().Hex() 24 | 25 | userDomain := model.NewUserDomain("test@test.com", "test", "test", 50) 26 | userDomain.SetID(id) 27 | 28 | userDomainMock := model.NewUserDomain( 29 | userDomain.GetEmail(), 30 | userDomain.GetPassword(), 31 | userDomain.GetName(), 32 | userDomain.GetAge()) 33 | userDomainMock.EncryptPassword() 34 | 35 | repository.EXPECT().FindUserByEmailAndPassword( 36 | userDomain.GetEmail(), userDomainMock.GetPassword()).Return( 37 | nil, rest_err.NewInternalServerError("error trying to find user by email and password")) 38 | 39 | user, token, err := service.LoginUserServices(userDomain) 40 | assert.Nil(t, user) 41 | assert.Empty(t, token) 42 | assert.NotNil(t, err) 43 | assert.EqualValues(t, err.Message, "error trying to find user by email and password") 44 | }) 45 | 46 | t.Run("when_calling_create_token_returns_error", func(t *testing.T) { 47 | userDomainMock := mocks.NewMockUserDomainInterface(ctrl) 48 | 49 | userDomainMock.EXPECT().GetEmail().Return("test@test.com") 50 | userDomainMock.EXPECT().GetPassword().Return("test") 51 | userDomainMock.EXPECT().EncryptPassword() 52 | 53 | userDomainMock.EXPECT().GenerateToken().Return("", 54 | rest_err.NewInternalServerError("error trying to create token")) 55 | 56 | repository.EXPECT().FindUserByEmailAndPassword( 57 | "test@test.com", "test").Return( 58 | userDomainMock, nil) 59 | 60 | user, token, err := service.LoginUserServices(userDomainMock) 61 | assert.Nil(t, user) 62 | assert.Empty(t, token) 63 | assert.NotNil(t, err) 64 | assert.EqualValues(t, err.Message, "error trying to create token") 65 | }) 66 | 67 | t.Run("when_user_and_password_is_valid_return_success", func(t *testing.T) { 68 | id := primitive.NewObjectID().Hex() 69 | secret := "test" 70 | 71 | err := os.Setenv("JWT_SECRET_KEY", secret) 72 | if err != nil { 73 | t.FailNow() 74 | return 75 | } 76 | defer os.Clearenv() 77 | 78 | userDomain := model.NewUserDomain("test@test.com", "test", "test", 50) 79 | userDomain.SetID(id) 80 | 81 | repository.EXPECT().FindUserByEmailAndPassword( 82 | userDomain.GetEmail(), gomock.Any()).Return( 83 | userDomain, nil) 84 | 85 | userDomainReturn, token, err := service.LoginUserServices(userDomain) 86 | assert.Nil(t, err) 87 | assert.EqualValues(t, userDomainReturn.GetID(), id) 88 | assert.EqualValues(t, userDomainReturn.GetEmail(), userDomain.GetEmail()) 89 | assert.EqualValues(t, userDomainReturn.GetPassword(), userDomain.GetPassword()) 90 | assert.EqualValues(t, userDomainReturn.GetName(), userDomain.GetName()) 91 | assert.EqualValues(t, userDomainReturn.GetAge(), userDomain.GetAge()) 92 | 93 | tokenReturned, _ := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) { 94 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { 95 | return []byte(secret), nil 96 | } 97 | 98 | return nil, rest_err.NewBadRequestError("invalid token") 99 | }) 100 | _, ok := tokenReturned.Claims.(jwt.MapClaims) 101 | if !ok || !tokenReturned.Valid { 102 | t.FailNow() 103 | return 104 | } 105 | }) 106 | } 107 | -------------------------------------------------------------------------------- /src/model/service/update_user.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 7 | "go.uber.org/zap" 8 | ) 9 | 10 | func (ud *userDomainService) UpdateUser( 11 | userId string, 12 | userDomain model.UserDomainInterface, 13 | ) *rest_err.RestErr { 14 | logger.Info("Init updateUser model.", 15 | zap.String("journey", "updateUser")) 16 | 17 | err := ud.userRepository.UpdateUser(userId, userDomain) 18 | if err != nil { 19 | logger.Error("Error trying to call repository", 20 | err, 21 | zap.String("journey", "updateUser")) 22 | return err 23 | } 24 | 25 | logger.Info( 26 | "updateUser service executed successfully", 27 | zap.String("userId", userId), 28 | zap.String("journey", "updateUser")) 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /src/model/service/update_user_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/mocks" 7 | "github.com/stretchr/testify/assert" 8 | "go.mongodb.org/mongo-driver/bson/primitive" 9 | "go.uber.org/mock/gomock" 10 | "testing" 11 | ) 12 | 13 | func TestUserDomainService_UpdateUser(t *testing.T) { 14 | ctrl := gomock.NewController(t) 15 | defer ctrl.Finish() 16 | 17 | repository := mocks.NewMockUserRepository(ctrl) 18 | service := NewUserDomainService(repository) 19 | 20 | t.Run("when_sending_a_valid_user_and_userId_returns_success", func(t *testing.T) { 21 | id := primitive.NewObjectID().Hex() 22 | 23 | userDomain := model.NewUserDomain("test@test.com", "test", "test", 50) 24 | userDomain.SetID(id) 25 | 26 | repository.EXPECT().UpdateUser(id, userDomain).Return(nil) 27 | 28 | err := service.UpdateUser(id, userDomain) 29 | 30 | assert.Nil(t, err) 31 | }) 32 | 33 | t.Run("when_sending_a_invalid_user_and_userId_returns_error", func(t *testing.T) { 34 | id := primitive.NewObjectID().Hex() 35 | 36 | userDomain := model.NewUserDomain("test@test.com", "test", "test", 50) 37 | userDomain.SetID(id) 38 | 39 | repository.EXPECT().UpdateUser(id, userDomain).Return( 40 | rest_err.NewInternalServerError("error trying to update user")) 41 | 42 | err := service.UpdateUser(id, userDomain) 43 | 44 | assert.NotNil(t, err) 45 | assert.EqualValues(t, err.Message, "error trying to update user") 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /src/model/service/user_interface.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository" 7 | ) 8 | 9 | func NewUserDomainService( 10 | userRepository repository.UserRepository, 11 | ) UserDomainService { 12 | return &userDomainService{userRepository} 13 | } 14 | 15 | type userDomainService struct { 16 | userRepository repository.UserRepository 17 | } 18 | 19 | type UserDomainService interface { 20 | CreateUserServices(model.UserDomainInterface) ( 21 | model.UserDomainInterface, *rest_err.RestErr) 22 | 23 | FindUserByIDServices( 24 | id string, 25 | ) (model.UserDomainInterface, *rest_err.RestErr) 26 | FindUserByEmailServices( 27 | email string, 28 | ) (model.UserDomainInterface, *rest_err.RestErr) 29 | 30 | UpdateUser(string, model.UserDomainInterface) *rest_err.RestErr 31 | DeleteUser(string) *rest_err.RestErr 32 | 33 | LoginUserServices( 34 | userDomain model.UserDomainInterface, 35 | ) (model.UserDomainInterface, string, *rest_err.RestErr) 36 | } 37 | -------------------------------------------------------------------------------- /src/model/user_domain.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type userDomain struct { 4 | id string 5 | email string 6 | password string 7 | name string 8 | age int8 9 | } 10 | 11 | func (ud *userDomain) GetID() string { 12 | return ud.id 13 | } 14 | 15 | func (ud *userDomain) SetID(id string) { 16 | ud.id = id 17 | } 18 | 19 | func (ud *userDomain) GetEmail() string { 20 | return ud.email 21 | } 22 | 23 | func (ud *userDomain) GetPassword() string { 24 | return ud.password 25 | } 26 | 27 | func (ud *userDomain) GetName() string { 28 | return ud.name 29 | } 30 | 31 | func (ud *userDomain) GetAge() int8 { 32 | return ud.age 33 | } 34 | -------------------------------------------------------------------------------- /src/model/user_domain_interface.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 4 | 5 | type UserDomainInterface interface { 6 | GetEmail() string 7 | GetPassword() string 8 | GetAge() int8 9 | GetName() string 10 | GetID() string 11 | 12 | SetID(string) 13 | 14 | EncryptPassword() 15 | GenerateToken() (string, *rest_err.RestErr) 16 | } 17 | 18 | func NewUserDomain( 19 | email, password, name string, 20 | age int8, 21 | ) UserDomainInterface { 22 | return &userDomain{ 23 | email: email, 24 | password: password, 25 | name: name, 26 | age: age, 27 | } 28 | } 29 | 30 | func NewUserLoginDomain( 31 | email, password string, 32 | ) UserDomainInterface { 33 | return &userDomain{ 34 | email: email, 35 | password: password, 36 | } 37 | } 38 | 39 | func NewUserUpdateDomain( 40 | name string, 41 | age int8, 42 | ) UserDomainInterface { 43 | return &userDomain{ 44 | name: name, 45 | age: age, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/model/user_domain_password.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | func (ud *userDomain) EncryptPassword() { 9 | hash := md5.New() 10 | defer hash.Reset() 11 | hash.Write([]byte(ud.password)) 12 | ud.password = hex.EncodeToString(hash.Sum(nil)) 13 | } 14 | -------------------------------------------------------------------------------- /src/model/user_token_domain.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/logger" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 7 | "github.com/gin-gonic/gin" 8 | "github.com/golang-jwt/jwt" 9 | "os" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | var ( 15 | JWT_SECRET_KEY = "JWT_SECRET_KEY" 16 | ) 17 | 18 | func (ud *userDomain) GenerateToken() (string, *rest_err.RestErr) { 19 | secret := os.Getenv(JWT_SECRET_KEY) 20 | 21 | claims := jwt.MapClaims{ 22 | "id": ud.id, 23 | "email": ud.email, 24 | "name": ud.name, 25 | "age": ud.age, 26 | "exp": time.Now().Add(time.Hour * 24).Unix(), 27 | } 28 | 29 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 30 | 31 | tokenString, err := token.SignedString([]byte(secret)) 32 | if err != nil { 33 | return "", rest_err.NewInternalServerError( 34 | fmt.Sprintf("error trying to generate jwt token, err=%s", err.Error())) 35 | } 36 | 37 | return tokenString, nil 38 | } 39 | 40 | func VerifyToken(tokenValue string) (UserDomainInterface, *rest_err.RestErr) { 41 | secret := os.Getenv(JWT_SECRET_KEY) 42 | 43 | token, err := jwt.Parse(RemoveBearerPrefix(tokenValue), func(token *jwt.Token) (interface{}, error) { 44 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { 45 | return []byte(secret), nil 46 | } 47 | 48 | return nil, rest_err.NewBadRequestError("invalid token") 49 | }) 50 | if err != nil { 51 | return nil, rest_err.NewUnauthorizedRequestError("invalid token") 52 | } 53 | 54 | claims, ok := token.Claims.(jwt.MapClaims) 55 | if !ok || !token.Valid { 56 | return nil, rest_err.NewUnauthorizedRequestError("invalid token") 57 | } 58 | 59 | return &userDomain{ 60 | id: claims["id"].(string), 61 | email: claims["email"].(string), 62 | name: claims["name"].(string), 63 | age: int8(claims["age"].(float64)), 64 | }, nil 65 | } 66 | 67 | func VerifyTokenMiddleware(c *gin.Context) { 68 | secret := os.Getenv(JWT_SECRET_KEY) 69 | tokenValue := RemoveBearerPrefix(c.Request.Header.Get("Authorization")) 70 | 71 | token, err := jwt.Parse(tokenValue, func(token *jwt.Token) (interface{}, error) { 72 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { 73 | return []byte(secret), nil 74 | } 75 | 76 | return nil, rest_err.NewBadRequestError("invalid token") 77 | }) 78 | if err != nil { 79 | errRest := rest_err.NewUnauthorizedRequestError("invalid token") 80 | c.JSON(errRest.Code, errRest) 81 | c.Abort() 82 | return 83 | } 84 | 85 | claims, ok := token.Claims.(jwt.MapClaims) 86 | if !ok || !token.Valid { 87 | errRest := rest_err.NewUnauthorizedRequestError("invalid token") 88 | c.JSON(errRest.Code, errRest) 89 | c.Abort() 90 | return 91 | } 92 | 93 | userDomain := userDomain{ 94 | id: claims["id"].(string), 95 | email: claims["email"].(string), 96 | name: claims["name"].(string), 97 | age: int8(claims["age"].(float64)), 98 | } 99 | logger.Info(fmt.Sprintf("User authenticated: %#v", userDomain)) 100 | } 101 | 102 | func RemoveBearerPrefix(token string) string { 103 | return strings.TrimPrefix(token, "Bearer ") 104 | } 105 | -------------------------------------------------------------------------------- /src/tests/connection/open_connection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/ory/dockertest" 7 | "go.mongodb.org/mongo-driver/mongo" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | "log" 10 | "os" 11 | ) 12 | 13 | func OpenConnection() (database *mongo.Database, close func()) { 14 | pool, err := dockertest.NewPool("") 15 | if err != nil { 16 | log.Fatalf("Could not construct pool: %s", err) 17 | return 18 | } 19 | 20 | resource, err := pool.RunWithOptions(&dockertest.RunOptions{ 21 | Repository: "mongo", 22 | Tag: "latest", 23 | }) 24 | if err != nil { 25 | log.Fatalf("Could not create mongo container: %s", err) 26 | return 27 | } 28 | 29 | client, err := mongo.NewClient(options.Client().ApplyURI( 30 | fmt.Sprintf("mongodb://127.0.0.1:%s", resource.GetPort("27017/tcp")))) 31 | if err != nil { 32 | log.Println("Error trying to open connection") 33 | return 34 | } 35 | 36 | err = client.Connect(context.Background()) 37 | if err != nil { 38 | log.Println("Error trying to open connection") 39 | return 40 | } 41 | 42 | database = client.Database(os.Getenv("MONGODB_USER_DB")) 43 | close = func() { 44 | err := resource.Close() 45 | if err != nil { 46 | log.Println("Error trying to open connection") 47 | return 48 | } 49 | } 50 | 51 | return 52 | } 53 | -------------------------------------------------------------------------------- /src/tests/create_user_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity" 9 | "github.com/gin-gonic/gin" 10 | "github.com/stretchr/testify/assert" 11 | "go.mongodb.org/mongo-driver/bson" 12 | "io" 13 | "math/rand" 14 | "net/http" 15 | "net/http/httptest" 16 | "net/url" 17 | "strings" 18 | "testing" 19 | ) 20 | 21 | func TestCreateUser(t *testing.T) { 22 | 23 | t.Run("user_already_registered_with_this__email", func(t *testing.T) { 24 | recorder := httptest.NewRecorder() 25 | ctx := GetTestGinContext(recorder) 26 | 27 | email := fmt.Sprintf("%d@test.com", rand.Int()) 28 | 29 | _, err := Database. 30 | Collection("test_user"). 31 | InsertOne(context.Background(), bson.M{"name": t.Name(), "email": email}) 32 | if err != nil { 33 | t.Fatal(err) 34 | return 35 | } 36 | 37 | userRequest := request.UserRequest{ 38 | Email: email, 39 | Password: "teste@#@123", 40 | Name: "Test User", 41 | Age: 10, 42 | } 43 | 44 | b, _ := json.Marshal(userRequest) 45 | stringReader := io.NopCloser(strings.NewReader(string(b))) 46 | 47 | MakeRequest(ctx, []gin.Param{}, url.Values{}, "POST", stringReader) 48 | UserController.CreateUser(ctx) 49 | 50 | assert.EqualValues(t, http.StatusBadRequest, recorder.Code) 51 | }) 52 | 53 | t.Run("user_is_not_registered_in_database", func(t *testing.T) { 54 | recorder := httptest.NewRecorder() 55 | ctx := GetTestGinContext(recorder) 56 | 57 | email := fmt.Sprintf("%d@test.com", rand.Int()) 58 | 59 | userRequest := request.UserRequest{ 60 | Email: email, 61 | Password: "teste@#@123", 62 | Name: "Test User", 63 | Age: 10, 64 | } 65 | 66 | b, _ := json.Marshal(userRequest) 67 | stringReader := io.NopCloser(strings.NewReader(string(b))) 68 | 69 | MakeRequest(ctx, []gin.Param{}, url.Values{}, "POST", stringReader) 70 | UserController.CreateUser(ctx) 71 | 72 | userEntity := entity.UserEntity{} 73 | 74 | filter := bson.D{{Key: "email", Value: email}} 75 | _ = Database. 76 | Collection("test_user"). 77 | FindOne(context.Background(), filter).Decode(&userEntity) 78 | 79 | assert.EqualValues(t, http.StatusOK, recorder.Code) 80 | assert.EqualValues(t, userEntity.Email, userRequest.Email) 81 | assert.EqualValues(t, userEntity.Age, userRequest.Age) 82 | assert.EqualValues(t, userEntity.Name, userRequest.Name) 83 | }) 84 | } 85 | -------------------------------------------------------------------------------- /src/tests/delete_user_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "github.com/gin-gonic/gin" 6 | "github.com/stretchr/testify/assert" 7 | "go.mongodb.org/mongo-driver/bson" 8 | "go.mongodb.org/mongo-driver/bson/primitive" 9 | "net/http" 10 | "net/http/httptest" 11 | "net/url" 12 | "testing" 13 | ) 14 | 15 | func TestDeleteUser(t *testing.T) { 16 | recorder := httptest.NewRecorder() 17 | ctx := GetTestGinContext(recorder) 18 | id := primitive.NewObjectID() 19 | 20 | _, err := Database. 21 | Collection("test_user"). 22 | InsertOne(context.Background(), bson.M{"_id": id, "name": t.Name(), "email": "test@test.com"}) 23 | if err != nil { 24 | t.Fatal(err) 25 | return 26 | } 27 | 28 | param := []gin.Param{ 29 | { 30 | Key: "userId", 31 | Value: id.Hex(), 32 | }, 33 | } 34 | 35 | MakeRequest(ctx, param, url.Values{}, "DELETE", nil) 36 | UserController.DeleteUser(ctx) 37 | 38 | assert.EqualValues(t, http.StatusOK, recorder.Code) 39 | 40 | filter := bson.D{{Key: "_id", Value: id}} 41 | result := Database. 42 | Collection("test_user"). 43 | FindOne(context.Background(), filter) 44 | 45 | assert.NotNil(t, result.Err()) 46 | } 47 | -------------------------------------------------------------------------------- /src/tests/find_user_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/service" 8 | "github.com/HunCoding/meu-primeiro-crud-go/src/tests/connection" 9 | "github.com/gin-gonic/gin" 10 | "github.com/stretchr/testify/assert" 11 | "go.mongodb.org/mongo-driver/bson" 12 | "go.mongodb.org/mongo-driver/bson/primitive" 13 | "go.mongodb.org/mongo-driver/mongo" 14 | "io" 15 | "net/http" 16 | "net/http/httptest" 17 | "net/url" 18 | "os" 19 | "testing" 20 | ) 21 | 22 | var ( 23 | UserController controller.UserControllerInterface 24 | Database *mongo.Database 25 | ) 26 | 27 | func TestMain(m *testing.M) { 28 | err := os.Setenv("MONGODB_USER_DB", "test_user") 29 | if err != nil { 30 | return 31 | } 32 | 33 | closeConnection := func() {} 34 | Database, closeConnection = connection.OpenConnection() 35 | 36 | repo := repository.NewUserRepository(Database) 37 | userService := service.NewUserDomainService(repo) 38 | UserController = controller.NewUserControllerInterface(userService) 39 | 40 | defer func() { 41 | os.Clearenv() 42 | closeConnection() 43 | }() 44 | 45 | os.Exit(m.Run()) 46 | } 47 | 48 | func TestFindUserByEmail(t *testing.T) { 49 | 50 | t.Run("user_not_found_with_this__email", func(t *testing.T) { 51 | recorder := httptest.NewRecorder() 52 | ctx := GetTestGinContext(recorder) 53 | 54 | param := []gin.Param{ 55 | { 56 | Key: "userEmail", 57 | Value: "test@test.com", 58 | }, 59 | } 60 | 61 | MakeRequest(ctx, param, url.Values{}, "GET", nil) 62 | UserController.FindUserByEmail(ctx) 63 | 64 | assert.EqualValues(t, http.StatusNotFound, recorder.Code) 65 | }) 66 | 67 | t.Run("user__found_with_specified_email", func(t *testing.T) { 68 | recorder := httptest.NewRecorder() 69 | ctx := GetTestGinContext(recorder) 70 | id := primitive.NewObjectID().Hex() 71 | 72 | _, err := Database. 73 | Collection("test_user"). 74 | InsertOne(context.Background(), bson.M{"_id": id, "name": t.Name(), "email": "test@test.com"}) 75 | if err != nil { 76 | t.Fatal(err) 77 | return 78 | } 79 | 80 | param := []gin.Param{ 81 | { 82 | Key: "userEmail", 83 | Value: "test@test.com", 84 | }, 85 | } 86 | 87 | MakeRequest(ctx, param, url.Values{}, "GET", nil) 88 | UserController.FindUserByEmail(ctx) 89 | 90 | assert.EqualValues(t, http.StatusOK, recorder.Code) 91 | }) 92 | } 93 | 94 | func TestFindUserById(t *testing.T) { 95 | 96 | t.Run("user_not_found_with_this_id", func(t *testing.T) { 97 | recorder := httptest.NewRecorder() 98 | ctx := GetTestGinContext(recorder) 99 | id := primitive.NewObjectID().Hex() 100 | 101 | param := []gin.Param{ 102 | { 103 | Key: "userId", 104 | Value: id, 105 | }, 106 | } 107 | 108 | MakeRequest(ctx, param, url.Values{}, "GET", nil) 109 | UserController.FindUserByID(ctx) 110 | 111 | assert.EqualValues(t, http.StatusNotFound, recorder.Code) 112 | }) 113 | 114 | t.Run("user_found_with_specified_id", func(t *testing.T) { 115 | recorder := httptest.NewRecorder() 116 | ctx := GetTestGinContext(recorder) 117 | id := primitive.NewObjectID() 118 | 119 | _, err := Database. 120 | Collection("test_user"). 121 | InsertOne(context.Background(), bson.M{"_id": id, "name": t.Name(), "email": "test@test.com"}) 122 | if err != nil { 123 | t.Fatal(err) 124 | return 125 | } 126 | 127 | param := []gin.Param{ 128 | { 129 | Key: "userId", 130 | Value: id.Hex(), 131 | }, 132 | } 133 | 134 | MakeRequest(ctx, param, url.Values{}, "GET", nil) 135 | UserController.FindUserByID(ctx) 136 | 137 | assert.EqualValues(t, http.StatusOK, recorder.Code) 138 | }) 139 | } 140 | 141 | func GetTestGinContext(recorder *httptest.ResponseRecorder) *gin.Context { 142 | gin.SetMode(gin.TestMode) 143 | 144 | ctx, _ := gin.CreateTestContext(recorder) 145 | ctx.Request = &http.Request{ 146 | Header: make(http.Header), 147 | URL: &url.URL{}, 148 | } 149 | 150 | return ctx 151 | } 152 | 153 | func MakeRequest( 154 | c *gin.Context, 155 | param gin.Params, 156 | u url.Values, 157 | method string, 158 | body io.ReadCloser) { 159 | c.Request.Method = method 160 | c.Request.Header.Set("Content-Type", "application/json") 161 | c.Params = param 162 | 163 | c.Request.Body = body 164 | c.Request.URL.RawQuery = u.Encode() 165 | } 166 | -------------------------------------------------------------------------------- /src/tests/login_user_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 7 | "github.com/gin-gonic/gin" 8 | "github.com/stretchr/testify/assert" 9 | "io" 10 | "math/rand" 11 | "net/http" 12 | "net/http/httptest" 13 | "net/url" 14 | "strings" 15 | "testing" 16 | ) 17 | 18 | func TestLoginUser(t *testing.T) { 19 | 20 | t.Run("user_and_password_is_not__valid", func(t *testing.T) { 21 | recorderCreateUser := httptest.NewRecorder() 22 | ctxCreateUser := GetTestGinContext(recorderCreateUser) 23 | 24 | recorderLoginUser := httptest.NewRecorder() 25 | ctxLoginUser := GetTestGinContext(recorderLoginUser) 26 | 27 | email := fmt.Sprintf("%d@testfds.com", rand.Int()) 28 | password := fmt.Sprintf("%d$#@$#@", rand.Int()) 29 | 30 | userCreateRequest := request.UserRequest{ 31 | Email: email, 32 | Password: password, 33 | Name: "Test User", 34 | Age: 10, 35 | } 36 | 37 | bCreate, _ := json.Marshal(userCreateRequest) 38 | stringReaderCreate := io.NopCloser(strings.NewReader(string(bCreate))) 39 | 40 | MakeRequest(ctxCreateUser, []gin.Param{}, url.Values{}, "POST", stringReaderCreate) 41 | UserController.CreateUser(ctxCreateUser) 42 | 43 | userLoginRequest := request.UserLogin{ 44 | Email: "test@testnotpassing.com", 45 | Password: "fdsfdsr43$#@$#@$@#", 46 | } 47 | 48 | bLogin, _ := json.Marshal(userLoginRequest) 49 | stringReaderLogin := io.NopCloser(strings.NewReader(string(bLogin))) 50 | 51 | MakeRequest(ctxLoginUser, []gin.Param{}, url.Values{}, "POST", stringReaderLogin) 52 | UserController.LoginUser(ctxLoginUser) 53 | 54 | assert.EqualValues(t, http.StatusForbidden, recorderLoginUser.Result().StatusCode) 55 | }) 56 | 57 | t.Run("user_and_password_is_valid", func(t *testing.T) { 58 | recorderCreateUser := httptest.NewRecorder() 59 | ctxCreateUser := GetTestGinContext(recorderCreateUser) 60 | 61 | recorderLoginUser := httptest.NewRecorder() 62 | ctxLoginUser := GetTestGinContext(recorderLoginUser) 63 | 64 | email := fmt.Sprintf("%d@test.com", rand.Int()) 65 | password := fmt.Sprintf("%d$#@$#@", rand.Int()) 66 | 67 | userCreateRequest := request.UserRequest{ 68 | Email: email, 69 | Password: password, 70 | Name: "Test User", 71 | Age: 10, 72 | } 73 | 74 | bCreate, _ := json.Marshal(userCreateRequest) 75 | stringReaderCreate := io.NopCloser(strings.NewReader(string(bCreate))) 76 | 77 | MakeRequest(ctxCreateUser, []gin.Param{}, url.Values{}, "POST", stringReaderCreate) 78 | UserController.CreateUser(ctxCreateUser) 79 | 80 | userLoginRequest := request.UserLogin{ 81 | Email: email, 82 | Password: password, 83 | } 84 | 85 | bLogin, _ := json.Marshal(userLoginRequest) 86 | stringReaderLogin := io.NopCloser(strings.NewReader(string(bLogin))) 87 | 88 | MakeRequest(ctxLoginUser, []gin.Param{}, url.Values{}, "POST", stringReaderLogin) 89 | UserController.LoginUser(ctxLoginUser) 90 | 91 | assert.EqualValues(t, http.StatusOK, recorderLoginUser.Result().StatusCode) 92 | assert.NotEmpty(t, recorderLoginUser.Result().Header.Get("Authorization")) 93 | }) 94 | } 95 | -------------------------------------------------------------------------------- /src/tests/mocks/user_domain_interface_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: src/model/user_domain_interface.go 3 | 4 | // Package mocks is a generated GoMock package. 5 | package mocks 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | rest_err "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 11 | gomock "go.uber.org/mock/gomock" 12 | ) 13 | 14 | // MockUserDomainInterface is a mock of UserDomainInterface interface. 15 | type MockUserDomainInterface struct { 16 | ctrl *gomock.Controller 17 | recorder *MockUserDomainInterfaceMockRecorder 18 | } 19 | 20 | // MockUserDomainInterfaceMockRecorder is the mock recorder for MockUserDomainInterface. 21 | type MockUserDomainInterfaceMockRecorder struct { 22 | mock *MockUserDomainInterface 23 | } 24 | 25 | // NewMockUserDomainInterface creates a new mock instance. 26 | func NewMockUserDomainInterface(ctrl *gomock.Controller) *MockUserDomainInterface { 27 | mock := &MockUserDomainInterface{ctrl: ctrl} 28 | mock.recorder = &MockUserDomainInterfaceMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockUserDomainInterface) EXPECT() *MockUserDomainInterfaceMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // EncryptPassword mocks base method. 38 | func (m *MockUserDomainInterface) EncryptPassword() { 39 | m.ctrl.T.Helper() 40 | m.ctrl.Call(m, "EncryptPassword") 41 | } 42 | 43 | // EncryptPassword indicates an expected call of EncryptPassword. 44 | func (mr *MockUserDomainInterfaceMockRecorder) EncryptPassword() *gomock.Call { 45 | mr.mock.ctrl.T.Helper() 46 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncryptPassword", reflect.TypeOf((*MockUserDomainInterface)(nil).EncryptPassword)) 47 | } 48 | 49 | // GenerateToken mocks base method. 50 | func (m *MockUserDomainInterface) GenerateToken() (string, *rest_err.RestErr) { 51 | m.ctrl.T.Helper() 52 | ret := m.ctrl.Call(m, "GenerateToken") 53 | ret0, _ := ret[0].(string) 54 | ret1, _ := ret[1].(*rest_err.RestErr) 55 | return ret0, ret1 56 | } 57 | 58 | // GenerateToken indicates an expected call of GenerateToken. 59 | func (mr *MockUserDomainInterfaceMockRecorder) GenerateToken() *gomock.Call { 60 | mr.mock.ctrl.T.Helper() 61 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateToken", reflect.TypeOf((*MockUserDomainInterface)(nil).GenerateToken)) 62 | } 63 | 64 | // GetAge mocks base method. 65 | func (m *MockUserDomainInterface) GetAge() int8 { 66 | m.ctrl.T.Helper() 67 | ret := m.ctrl.Call(m, "GetAge") 68 | ret0, _ := ret[0].(int8) 69 | return ret0 70 | } 71 | 72 | // GetAge indicates an expected call of GetAge. 73 | func (mr *MockUserDomainInterfaceMockRecorder) GetAge() *gomock.Call { 74 | mr.mock.ctrl.T.Helper() 75 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAge", reflect.TypeOf((*MockUserDomainInterface)(nil).GetAge)) 76 | } 77 | 78 | // GetEmail mocks base method. 79 | func (m *MockUserDomainInterface) GetEmail() string { 80 | m.ctrl.T.Helper() 81 | ret := m.ctrl.Call(m, "GetEmail") 82 | ret0, _ := ret[0].(string) 83 | return ret0 84 | } 85 | 86 | // GetEmail indicates an expected call of GetEmail. 87 | func (mr *MockUserDomainInterfaceMockRecorder) GetEmail() *gomock.Call { 88 | mr.mock.ctrl.T.Helper() 89 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEmail", reflect.TypeOf((*MockUserDomainInterface)(nil).GetEmail)) 90 | } 91 | 92 | // GetID mocks base method. 93 | func (m *MockUserDomainInterface) GetID() string { 94 | m.ctrl.T.Helper() 95 | ret := m.ctrl.Call(m, "GetID") 96 | ret0, _ := ret[0].(string) 97 | return ret0 98 | } 99 | 100 | // GetID indicates an expected call of GetID. 101 | func (mr *MockUserDomainInterfaceMockRecorder) GetID() *gomock.Call { 102 | mr.mock.ctrl.T.Helper() 103 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetID", reflect.TypeOf((*MockUserDomainInterface)(nil).GetID)) 104 | } 105 | 106 | // GetName mocks base method. 107 | func (m *MockUserDomainInterface) GetName() string { 108 | m.ctrl.T.Helper() 109 | ret := m.ctrl.Call(m, "GetName") 110 | ret0, _ := ret[0].(string) 111 | return ret0 112 | } 113 | 114 | // GetName indicates an expected call of GetName. 115 | func (mr *MockUserDomainInterfaceMockRecorder) GetName() *gomock.Call { 116 | mr.mock.ctrl.T.Helper() 117 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockUserDomainInterface)(nil).GetName)) 118 | } 119 | 120 | // GetPassword mocks base method. 121 | func (m *MockUserDomainInterface) GetPassword() string { 122 | m.ctrl.T.Helper() 123 | ret := m.ctrl.Call(m, "GetPassword") 124 | ret0, _ := ret[0].(string) 125 | return ret0 126 | } 127 | 128 | // GetPassword indicates an expected call of GetPassword. 129 | func (mr *MockUserDomainInterfaceMockRecorder) GetPassword() *gomock.Call { 130 | mr.mock.ctrl.T.Helper() 131 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPassword", reflect.TypeOf((*MockUserDomainInterface)(nil).GetPassword)) 132 | } 133 | 134 | // SetID mocks base method. 135 | func (m *MockUserDomainInterface) SetID(arg0 string) { 136 | m.ctrl.T.Helper() 137 | m.ctrl.Call(m, "SetID", arg0) 138 | } 139 | 140 | // SetID indicates an expected call of SetID. 141 | func (mr *MockUserDomainInterfaceMockRecorder) SetID(arg0 interface{}) *gomock.Call { 142 | mr.mock.ctrl.T.Helper() 143 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetID", reflect.TypeOf((*MockUserDomainInterface)(nil).SetID), arg0) 144 | } 145 | -------------------------------------------------------------------------------- /src/tests/mocks/user_repository_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: src/model/repository/user_repository.go 3 | 4 | // Package mocks is a generated GoMock package. 5 | package mocks 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | rest_err "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 11 | model "github.com/HunCoding/meu-primeiro-crud-go/src/model" 12 | gomock "go.uber.org/mock/gomock" 13 | ) 14 | 15 | // MockUserRepository is a mock of UserRepository interface. 16 | type MockUserRepository struct { 17 | ctrl *gomock.Controller 18 | recorder *MockUserRepositoryMockRecorder 19 | } 20 | 21 | // MockUserRepositoryMockRecorder is the mock recorder for MockUserRepository. 22 | type MockUserRepositoryMockRecorder struct { 23 | mock *MockUserRepository 24 | } 25 | 26 | // NewMockUserRepository creates a new mock instance. 27 | func NewMockUserRepository(ctrl *gomock.Controller) *MockUserRepository { 28 | mock := &MockUserRepository{ctrl: ctrl} 29 | mock.recorder = &MockUserRepositoryMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use. 34 | func (m *MockUserRepository) EXPECT() *MockUserRepositoryMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // CreateUser mocks base method. 39 | func (m *MockUserRepository) CreateUser(userDomain model.UserDomainInterface) (model.UserDomainInterface, *rest_err.RestErr) { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "CreateUser", userDomain) 42 | ret0, _ := ret[0].(model.UserDomainInterface) 43 | ret1, _ := ret[1].(*rest_err.RestErr) 44 | return ret0, ret1 45 | } 46 | 47 | // CreateUser indicates an expected call of CreateUser. 48 | func (mr *MockUserRepositoryMockRecorder) CreateUser(userDomain interface{}) *gomock.Call { 49 | mr.mock.ctrl.T.Helper() 50 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockUserRepository)(nil).CreateUser), userDomain) 51 | } 52 | 53 | // DeleteUser mocks base method. 54 | func (m *MockUserRepository) DeleteUser(userId string) *rest_err.RestErr { 55 | m.ctrl.T.Helper() 56 | ret := m.ctrl.Call(m, "DeleteUser", userId) 57 | ret0, _ := ret[0].(*rest_err.RestErr) 58 | return ret0 59 | } 60 | 61 | // DeleteUser indicates an expected call of DeleteUser. 62 | func (mr *MockUserRepositoryMockRecorder) DeleteUser(userId interface{}) *gomock.Call { 63 | mr.mock.ctrl.T.Helper() 64 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUser", reflect.TypeOf((*MockUserRepository)(nil).DeleteUser), userId) 65 | } 66 | 67 | // FindUserByEmail mocks base method. 68 | func (m *MockUserRepository) FindUserByEmail(email string) (model.UserDomainInterface, *rest_err.RestErr) { 69 | m.ctrl.T.Helper() 70 | ret := m.ctrl.Call(m, "FindUserByEmail", email) 71 | ret0, _ := ret[0].(model.UserDomainInterface) 72 | ret1, _ := ret[1].(*rest_err.RestErr) 73 | return ret0, ret1 74 | } 75 | 76 | // FindUserByEmail indicates an expected call of FindUserByEmail. 77 | func (mr *MockUserRepositoryMockRecorder) FindUserByEmail(email interface{}) *gomock.Call { 78 | mr.mock.ctrl.T.Helper() 79 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserByEmail", reflect.TypeOf((*MockUserRepository)(nil).FindUserByEmail), email) 80 | } 81 | 82 | // FindUserByEmailAndPassword mocks base method. 83 | func (m *MockUserRepository) FindUserByEmailAndPassword(email, password string) (model.UserDomainInterface, *rest_err.RestErr) { 84 | m.ctrl.T.Helper() 85 | ret := m.ctrl.Call(m, "FindUserByEmailAndPassword", email, password) 86 | ret0, _ := ret[0].(model.UserDomainInterface) 87 | ret1, _ := ret[1].(*rest_err.RestErr) 88 | return ret0, ret1 89 | } 90 | 91 | // FindUserByEmailAndPassword indicates an expected call of FindUserByEmailAndPassword. 92 | func (mr *MockUserRepositoryMockRecorder) FindUserByEmailAndPassword(email, password interface{}) *gomock.Call { 93 | mr.mock.ctrl.T.Helper() 94 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserByEmailAndPassword", reflect.TypeOf((*MockUserRepository)(nil).FindUserByEmailAndPassword), email, password) 95 | } 96 | 97 | // FindUserByID mocks base method. 98 | func (m *MockUserRepository) FindUserByID(id string) (model.UserDomainInterface, *rest_err.RestErr) { 99 | m.ctrl.T.Helper() 100 | ret := m.ctrl.Call(m, "FindUserByID", id) 101 | ret0, _ := ret[0].(model.UserDomainInterface) 102 | ret1, _ := ret[1].(*rest_err.RestErr) 103 | return ret0, ret1 104 | } 105 | 106 | // FindUserByID indicates an expected call of FindUserByID. 107 | func (mr *MockUserRepositoryMockRecorder) FindUserByID(id interface{}) *gomock.Call { 108 | mr.mock.ctrl.T.Helper() 109 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserByID", reflect.TypeOf((*MockUserRepository)(nil).FindUserByID), id) 110 | } 111 | 112 | // UpdateUser mocks base method. 113 | func (m *MockUserRepository) UpdateUser(userId string, userDomain model.UserDomainInterface) *rest_err.RestErr { 114 | m.ctrl.T.Helper() 115 | ret := m.ctrl.Call(m, "UpdateUser", userId, userDomain) 116 | ret0, _ := ret[0].(*rest_err.RestErr) 117 | return ret0 118 | } 119 | 120 | // UpdateUser indicates an expected call of UpdateUser. 121 | func (mr *MockUserRepositoryMockRecorder) UpdateUser(userId, userDomain interface{}) *gomock.Call { 122 | mr.mock.ctrl.T.Helper() 123 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUser", reflect.TypeOf((*MockUserRepository)(nil).UpdateUser), userId, userDomain) 124 | } 125 | -------------------------------------------------------------------------------- /src/tests/mocks/user_service_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: src/model/service/user_interface.go 3 | 4 | // Package mocks is a generated GoMock package. 5 | package mocks 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | rest_err "github.com/HunCoding/meu-primeiro-crud-go/src/configuration/rest_err" 11 | model "github.com/HunCoding/meu-primeiro-crud-go/src/model" 12 | gomock "github.com/golang/mock/gomock" 13 | ) 14 | 15 | // MockUserDomainService is a mock of UserDomainService interface. 16 | type MockUserDomainService struct { 17 | ctrl *gomock.Controller 18 | recorder *MockUserDomainServiceMockRecorder 19 | } 20 | 21 | // MockUserDomainServiceMockRecorder is the mock recorder for MockUserDomainService. 22 | type MockUserDomainServiceMockRecorder struct { 23 | mock *MockUserDomainService 24 | } 25 | 26 | // NewMockUserDomainService creates a new mock instance. 27 | func NewMockUserDomainService(ctrl *gomock.Controller) *MockUserDomainService { 28 | mock := &MockUserDomainService{ctrl: ctrl} 29 | mock.recorder = &MockUserDomainServiceMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use. 34 | func (m *MockUserDomainService) EXPECT() *MockUserDomainServiceMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // CreateUserServices mocks base method. 39 | func (m *MockUserDomainService) CreateUserServices(arg0 model.UserDomainInterface) (model.UserDomainInterface, *rest_err.RestErr) { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "CreateUserServices", arg0) 42 | ret0, _ := ret[0].(model.UserDomainInterface) 43 | ret1, _ := ret[1].(*rest_err.RestErr) 44 | return ret0, ret1 45 | } 46 | 47 | // CreateUserServices indicates an expected call of CreateUserServices. 48 | func (mr *MockUserDomainServiceMockRecorder) CreateUserServices(arg0 interface{}) *gomock.Call { 49 | mr.mock.ctrl.T.Helper() 50 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUserServices", reflect.TypeOf((*MockUserDomainService)(nil).CreateUserServices), arg0) 51 | } 52 | 53 | // DeleteUser mocks base method. 54 | func (m *MockUserDomainService) DeleteUser(arg0 string) *rest_err.RestErr { 55 | m.ctrl.T.Helper() 56 | ret := m.ctrl.Call(m, "DeleteUser", arg0) 57 | ret0, _ := ret[0].(*rest_err.RestErr) 58 | return ret0 59 | } 60 | 61 | // DeleteUser indicates an expected call of DeleteUser. 62 | func (mr *MockUserDomainServiceMockRecorder) DeleteUser(arg0 interface{}) *gomock.Call { 63 | mr.mock.ctrl.T.Helper() 64 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteUser", reflect.TypeOf((*MockUserDomainService)(nil).DeleteUser), arg0) 65 | } 66 | 67 | // FindUserByEmailServices mocks base method. 68 | func (m *MockUserDomainService) FindUserByEmailServices(email string) (model.UserDomainInterface, *rest_err.RestErr) { 69 | m.ctrl.T.Helper() 70 | ret := m.ctrl.Call(m, "FindUserByEmailServices", email) 71 | ret0, _ := ret[0].(model.UserDomainInterface) 72 | ret1, _ := ret[1].(*rest_err.RestErr) 73 | return ret0, ret1 74 | } 75 | 76 | // FindUserByEmailServices indicates an expected call of FindUserByEmailServices. 77 | func (mr *MockUserDomainServiceMockRecorder) FindUserByEmailServices(email interface{}) *gomock.Call { 78 | mr.mock.ctrl.T.Helper() 79 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserByEmailServices", reflect.TypeOf((*MockUserDomainService)(nil).FindUserByEmailServices), email) 80 | } 81 | 82 | // FindUserByIDServices mocks base method. 83 | func (m *MockUserDomainService) FindUserByIDServices(id string) (model.UserDomainInterface, *rest_err.RestErr) { 84 | m.ctrl.T.Helper() 85 | ret := m.ctrl.Call(m, "FindUserByIDServices", id) 86 | ret0, _ := ret[0].(model.UserDomainInterface) 87 | ret1, _ := ret[1].(*rest_err.RestErr) 88 | return ret0, ret1 89 | } 90 | 91 | // FindUserByIDServices indicates an expected call of FindUserByIDServices. 92 | func (mr *MockUserDomainServiceMockRecorder) FindUserByIDServices(id interface{}) *gomock.Call { 93 | mr.mock.ctrl.T.Helper() 94 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserByIDServices", reflect.TypeOf((*MockUserDomainService)(nil).FindUserByIDServices), id) 95 | } 96 | 97 | // LoginUserServices mocks base method. 98 | func (m *MockUserDomainService) LoginUserServices(userDomain model.UserDomainInterface) (model.UserDomainInterface, string, *rest_err.RestErr) { 99 | m.ctrl.T.Helper() 100 | ret := m.ctrl.Call(m, "LoginUserServices", userDomain) 101 | ret0, _ := ret[0].(model.UserDomainInterface) 102 | ret1, _ := ret[1].(string) 103 | ret2, _ := ret[2].(*rest_err.RestErr) 104 | return ret0, ret1, ret2 105 | } 106 | 107 | // LoginUserServices indicates an expected call of LoginUserServices. 108 | func (mr *MockUserDomainServiceMockRecorder) LoginUserServices(userDomain interface{}) *gomock.Call { 109 | mr.mock.ctrl.T.Helper() 110 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoginUserServices", reflect.TypeOf((*MockUserDomainService)(nil).LoginUserServices), userDomain) 111 | } 112 | 113 | // UpdateUser mocks base method. 114 | func (m *MockUserDomainService) UpdateUser(arg0 string, arg1 model.UserDomainInterface) *rest_err.RestErr { 115 | m.ctrl.T.Helper() 116 | ret := m.ctrl.Call(m, "UpdateUser", arg0, arg1) 117 | ret0, _ := ret[0].(*rest_err.RestErr) 118 | return ret0 119 | } 120 | 121 | // UpdateUser indicates an expected call of UpdateUser. 122 | func (mr *MockUserDomainServiceMockRecorder) UpdateUser(arg0, arg1 interface{}) *gomock.Call { 123 | mr.mock.ctrl.T.Helper() 124 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUser", reflect.TypeOf((*MockUserDomainService)(nil).UpdateUser), arg0, arg1) 125 | } 126 | -------------------------------------------------------------------------------- /src/tests/update_user_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/request" 7 | "github.com/HunCoding/meu-primeiro-crud-go/src/model/repository/entity" 8 | "github.com/gin-gonic/gin" 9 | "github.com/stretchr/testify/assert" 10 | "go.mongodb.org/mongo-driver/bson" 11 | "go.mongodb.org/mongo-driver/bson/primitive" 12 | "io" 13 | "math/rand" 14 | "net/http" 15 | "net/http/httptest" 16 | "net/url" 17 | "strings" 18 | "testing" 19 | ) 20 | 21 | func TestUpdateUser(t *testing.T) { 22 | recorder := httptest.NewRecorder() 23 | ctx := GetTestGinContext(recorder) 24 | id := primitive.NewObjectID() 25 | 26 | _, err := Database. 27 | Collection("test_user"). 28 | InsertOne(context.Background(), bson.M{ 29 | "_id": id, 30 | "name": "OLD_NAME", 31 | "age": 10, 32 | "email": "test@test.com"}) 33 | if err != nil { 34 | t.Fatal(err) 35 | return 36 | } 37 | 38 | param := []gin.Param{ 39 | { 40 | Key: "userId", 41 | Value: id.Hex(), 42 | }, 43 | } 44 | 45 | userRequest := request.UserUpdateRequest{ 46 | Name: "HUNCODING_NEW_NAME", 47 | Age: int8(rand.Uint64()), 48 | } 49 | 50 | b, _ := json.Marshal(userRequest) 51 | stringReader := io.NopCloser(strings.NewReader(string(b))) 52 | 53 | MakeRequest(ctx, param, url.Values{}, "PUT", stringReader) 54 | UserController.UpdateUser(ctx) 55 | 56 | assert.EqualValues(t, http.StatusOK, recorder.Result().StatusCode) 57 | 58 | userEntity := entity.UserEntity{} 59 | 60 | filter := bson.D{{Key: "_id", Value: id}} 61 | _ = Database. 62 | Collection("test_user"). 63 | FindOne(context.Background(), filter).Decode(&userEntity) 64 | 65 | assert.EqualValues(t, userRequest.Name, userEntity.Name) 66 | assert.EqualValues(t, userRequest.Age, userEntity.Age) 67 | } 68 | -------------------------------------------------------------------------------- /src/view/convert_domain_to_response.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "github.com/HunCoding/meu-primeiro-crud-go/src/controller/model/response" 5 | "github.com/HunCoding/meu-primeiro-crud-go/src/model" 6 | ) 7 | 8 | func ConvertDomainToResponse( 9 | userDomain model.UserDomainInterface, 10 | ) response.UserResponse { 11 | return response.UserResponse{ 12 | ID: userDomain.GetID(), 13 | Email: userDomain.GetEmail(), 14 | Name: userDomain.GetName(), 15 | Age: userDomain.GetAge(), 16 | } 17 | } 18 | --------------------------------------------------------------------------------