Skip to content

Commit

Permalink
Merge pull request #13 from TheFeij/ft/grpc
Browse files Browse the repository at this point in the history
Ft/grpc added grpc
  • Loading branch information
TheFeij authored May 4, 2024
2 parents aba1b3b + 586a470 commit f05057d
Show file tree
Hide file tree
Showing 61 changed files with 4,173 additions and 78 deletions.
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ From alpine:3.19
WORKDIR /app
COPY --from=builder /app/main .
COPY --from=builder /app/migrate .
COPY config/config.json /app/config/
COPY doc/swagger/ /app/doc/swagger/
COPY config/config.json /app/config/
COPY db/migration ./migration
COPY start.sh .
RUN chmod +x start.sh
Expand Down
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ server:
test:
go test -v -cover ./...

proto:
rm -f pb/*.go
rm -f doc/swagger/*.swagger.json
protoc --go_out=pb --proto_path=proto --go_opt=paths=source_relative \
--go-grpc_out=pb --go-grpc_opt=paths=source_relative \
--grpc-gateway_out=pb --grpc-gateway_opt=paths=source_relative \
--openapiv2_out=doc/swagger --openapiv2_opt=allow_merge=true,merge_file_name=simple_bank\
proto/*.proto

.PHONY: postgres, createdb, dropdb, createtestdb, droptestdb, mockdb, mocktokenmaker
.PHONY: migratedown, migrateup, testmigratedown. testmigrateup, server
.PHONY: migratedown1, migrateup1, testmigrateup1, testmigratedown1
.PHONY: migratedown1, migrateup1, testmigrateup1, testmigratedown1, proto
36 changes: 36 additions & 0 deletions api/gin_validators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package api

import (
"Simple-Bank/util"
"github.com/go-playground/validator/v10"
)

var ValidUsername validator.Func = func(fl validator.FieldLevel) bool {
if username, ok := fl.Field().Interface().(string); ok {
if err := util.ValidateUsername(username); err != nil {
return false
}
return true
}
return false
}

var ValidPassword validator.Func = func(fl validator.FieldLevel) bool {
if password, ok := fl.Field().Interface().(string); ok {
if err := util.ValidatePassword(password); err != nil {
return false
}
return true
}
return false
}

var ValidFullname validator.Func = func(fl validator.FieldLevel) bool {
if fullname, ok := fl.Field().Interface().(string); ok {
if err := util.ValidateFullname(fullname); err != nil {
return false
}
return true
}
return false
}
7 changes: 3 additions & 4 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package api
import (
"Simple-Bank/config"
"Simple-Bank/db/services"
"Simple-Bank/requests"
"Simple-Bank/token"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
Expand Down Expand Up @@ -48,13 +47,13 @@ func (server *Server) setupRouter() {

func registerCustomValidators() {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
if err := v.RegisterValidation("validUsername", requests.ValidUsername); err != nil {
if err := v.RegisterValidation("validUsername", ValidUsername); err != nil {
log.Fatal("could not register validUsername validator")
}
if err := v.RegisterValidation("validPassword", requests.ValidPassword); err != nil {
if err := v.RegisterValidation("validPassword", ValidPassword); err != nil {
log.Fatal("could not register validPassword validator")
}
if err := v.RegisterValidation("validFullname", requests.ValidFullname); err != nil {
if err := v.RegisterValidation("validFullname", ValidFullname); err != nil {
log.Fatal("could not register validFullname validator")
}
}
Expand Down
6 changes: 4 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
type Config struct {
DatabaseDriver string `mapstructure:"DATABASE_DRIVER"`
DatabaseSource string `mapstructure:"DATABASE_SOURCE"`
ServerHost string `mapstructure:"SERVER_HOST"`
ServerPort string `mapstructure:"SERVER_PORT"`
HTTPServerHost string `mapstructure:"HTTP_SERVER_HOST"`
HTTPServerPort string `mapstructure:"HTTP_SERVER_PORT"`
GrpcServerHost string `mapstructure:"GRPC_SERVER_HOST"`
GrpcServerPort string `mapstructure:"GRPC_SERVER_PORT"`
TokenSymmetricKey string `mapstructure:"TOKEN_SYMMETRIC_KEY"`
TokenAccessTokenDuration time.Duration `mapstructure:"TOKEN_ACCESS_TOKEN_DURATION"`
TokenRefreshTokenDuration time.Duration `mapstructure:"TOKEN_REFRESH_TOKEN_DURATION"`
Expand Down
6 changes: 4 additions & 2 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ func TestLoadConfig(t *testing.T) {
require.NoError(t, err)
require.NotEmpty(t, config)

require.Equal(t, "host", config.ServerHost)
require.Equal(t, "port", config.ServerPort)
require.Equal(t, "http host", config.HTTPServerHost)
require.Equal(t, "http port", config.HTTPServerPort)
require.Equal(t, "grpc host", config.GrpcServerHost)
require.Equal(t, "grpc port", config.GrpcServerPort)
require.Equal(t, "source", config.DatabaseSource)
require.Equal(t, "driver", config.DatabaseDriver)
require.Equal(t, "key", config.TokenSymmetricKey)
Expand Down
6 changes: 4 additions & 2 deletions config/config_test.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"DATABASE_DRIVER": "driver",
"DATABASE_SOURCE": "source",
"SERVER_HOST": "host",
"SERVER_PORT": "port",
"HTTP_SERVER_HOST": "http host",
"HTTP_SERVER_PORT": "http port",
"GRPC_SERVER_HOST": "grpc host",
"GRPC_SERVER_PORT": "grpc port",
"TOKEN_SYMMETRIC_KEY": "key",
"TOKEN_ACCESS_TOKEN_DURATION": "1m"
}
13 changes: 13 additions & 0 deletions db/services/requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package services

// UpdateUserRequest represents a request to update user information.
type UpdateUserRequest struct {
// Username of the user to update.
Username string
// Full name of the user (optional).
Fullname *string
// New password for the user (optional).
Password *string
// New email address for the user (optional).
Email *string
}
32 changes: 32 additions & 0 deletions db/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,38 @@ func (services *SQLServices) GetSession(id uuid.UUID) (models.Session, error) {
return session, nil
}

// UpdateUser updates the user information in the database based on the provided request.
// It hashes the password if provided, and updates the fullname and email fields if they are not nil.
// It returns the updated user model and any error encountered.
func (services *SQLServices) UpdateUser(req UpdateUserRequest) (models.User, error) {
updateData := map[string]interface{}{}

if req.Password != nil {
hashedPassword, err := util.HashPassword(*req.Password)
if err != nil {
return models.User{}, err
}
updateData["password"] = hashedPassword
}
if req.Fullname != nil {
updateData["fullname"] = req.Fullname
}
if req.Email != nil {
updateData["email"] = req.Email
}

var user models.User
if err := services.DB.
Model(&models.User{}).
Where("username = ?", req.Username).
Updates(updateData).
First(&user).Error; err != nil {
return models.User{}, err
}

return user, nil
}

func acquireLock(tx *gorm.DB, lowerAccountID, higherAccountID int64) (models.Account, models.Account, error) {
var lowerAccount, higherAccount models.Account
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Expand Down
1 change: 1 addition & 0 deletions db/services/services_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Services interface {
CreateUser(req requests.CreateUserRequest) (models.User, error)
GetSession(id uuid.UUID) (models.Session, error)
CreateSession(session models.Session) (models.Session, error)
UpdateUser(req UpdateUserRequest) (models.User, error)
}

var _ Services = (*SQLServices)(nil)
Binary file added doc/swagger/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/swagger/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions doc/swagger/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}

*,
*:before,
*:after {
box-sizing: inherit;
}

body {
margin: 0;
background: #fafafa;
}
19 changes: 19 additions & 0 deletions doc/swagger/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="index.css" />
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
</head>

<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
</body>
</html>
79 changes: 79 additions & 0 deletions doc/swagger/oauth2-redirect.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Swagger UI: OAuth2 Redirect</title>
</head>
<body>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;

if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1).replace('?', '&');
} else {
qp = location.search.substring(1);
}

arr = qp.split("&");
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value);
}
) : {};

isValid = qp.state === sentState;

if ((
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
});
}

if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg;
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}

oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}

if (document.readyState !== 'loading') {
run();
} else {
document.addEventListener('DOMContentLoaded', function () {
run();
});
}
</script>
</body>
</html>
Loading

0 comments on commit f05057d

Please sign in to comment.