moved away from the api directory to internal per go doc recommendations
This commit is contained in:
parent
150c54c4bd
commit
a2e740eefd
@ -11,6 +11,15 @@ type UserEntity struct {
|
||||
Scopes string
|
||||
}
|
||||
|
||||
type RefreshTokenEntity struct {
|
||||
Id int
|
||||
Username string
|
||||
Token string
|
||||
ExpiresAt time.Time
|
||||
CreatedAt time.Time
|
||||
LastUpdated time.Time
|
||||
}
|
||||
|
||||
type RecipeEntity struct {
|
||||
Id int32
|
||||
CreatedAt time.Time
|
@ -8,3 +8,9 @@ type UpdateScopesRequest struct {
|
||||
Username string `json:"username"`
|
||||
Scopes []string `json:"scopes" validate:"required"`
|
||||
}
|
||||
|
||||
type RefreshTokenRequest struct {
|
||||
Username string `json:"username"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
}
|
@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/repositories"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/repositories"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/labstack/echo/v4"
|
||||
@ -116,6 +116,16 @@ func (h *Handler) validateAdminToken(c echo.Context, password string) error {
|
||||
return c.JSON(http.StatusOK, token)
|
||||
}
|
||||
|
||||
func (h *Handler) GenerateRefreshToken(c echo.Context) error {
|
||||
// Check the context for the refresh token
|
||||
var request domain.RefreshTokenRequest
|
||||
err := (&echo.DefaultBinder{}).BindBody(c, &request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.refreshTokenRepo.Create()
|
||||
}
|
||||
|
||||
func (h *Handler) AddScopes(c echo.Context) error {
|
||||
token, err := h.getJwtToken(c)
|
||||
if err != nil {
|
@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
v1 "git.jamestombleson.com/jtom38/go-cook/api/handlers/v1"
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
v1 "git.jamestombleson.com/jtom38/go-cook/internal/handlers/v1"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
|
||||
_ "github.com/glebarez/go-sqlite"
|
||||
"github.com/labstack/echo/v4"
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
@ -4,9 +4,9 @@ import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/repositories"
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/services"
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/repositories"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/services"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
echojwt "github.com/labstack/echo-jwt/v4"
|
||||
@ -16,17 +16,19 @@ import (
|
||||
type Handler struct {
|
||||
Config domain.EnvConfig
|
||||
|
||||
UserService services.UserService
|
||||
userRepo repositories.IUserTable
|
||||
recipeRepo repositories.IRecipeTable
|
||||
UserService services.UserService
|
||||
userRepo repositories.IUserTable
|
||||
recipeRepo repositories.IRecipeTable
|
||||
refreshTokenRepo repositories.RefreshTokenRepository
|
||||
}
|
||||
|
||||
func NewHandler(conn *sql.DB, cfg domain.EnvConfig) *Handler {
|
||||
return &Handler{
|
||||
Config: cfg,
|
||||
UserService: services.NewUserService(conn),
|
||||
userRepo: repositories.NewUserRepository(conn),
|
||||
recipeRepo: repositories.NewRecipeRepository(conn),
|
||||
Config: cfg,
|
||||
UserService: services.NewUserService(conn),
|
||||
userRepo: repositories.NewUserRepository(conn),
|
||||
recipeRepo: repositories.NewRecipeRepository(conn),
|
||||
refreshTokenRepo: repositories.NewRefreshTokenRepository(conn),
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
17
internal/migrations/20240416180636_refreshtoken.sql
Normal file
17
internal/migrations/20240416180636_refreshtoken.sql
Normal file
@ -0,0 +1,17 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
SELECT 'up SQL query';
|
||||
CREATE TABLE RefreshTokens (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
Username TEXT NOT NULL,
|
||||
Token TEXT NOT NULL,
|
||||
ExpiresAt DATETIME NOT NULL,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
LastUpdated DATETIME NOT NULL
|
||||
)
|
||||
-- +goose StatementEnd
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
SELECT 'down SQL query';
|
||||
DROP TABLE IF EXISTS RefreshTokens;
|
||||
-- +goose StatementEnd
|
@ -4,7 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
)
|
||||
|
||||
type IRecipeTable interface {
|
111
internal/repositories/refreshTokens.go
Normal file
111
internal/repositories/refreshTokens.go
Normal file
@ -0,0 +1,111 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
const (
|
||||
refreshTokenTableName = "RefreshTokens"
|
||||
)
|
||||
|
||||
type RefreshTokenTable interface {
|
||||
}
|
||||
|
||||
type RefreshTokenRepository struct {
|
||||
connection *sql.DB
|
||||
}
|
||||
|
||||
func NewRefreshTokenRepository(conn *sql.DB) RefreshTokenRepository {
|
||||
return RefreshTokenRepository{
|
||||
connection: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func (rt RefreshTokenRepository) Create(username string, token string, expiresAt time.Time) (int64, error) {
|
||||
dt := time.Now()
|
||||
builder := sqlbuilder.NewInsertBuilder()
|
||||
builder.InsertInto(refreshTokenTableName)
|
||||
builder.Cols("Username", "Token", "ExpiresAt", "CreatedAt", "LastUpdated")
|
||||
builder.Values(username, token, expiresAt, dt, dt)
|
||||
query, args := builder.Build()
|
||||
|
||||
_, err := rt.connection.Exec(query, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (rt RefreshTokenRepository) GetByUsername(name string) (domain.RefreshTokenEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*").From(refreshTokenTableName).Where(
|
||||
builder.E("Username", name),
|
||||
)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := rt.connection.Query(query, args...)
|
||||
if err != nil {
|
||||
return domain.RefreshTokenEntity{}, err
|
||||
}
|
||||
|
||||
data := rt.processRows(rows)
|
||||
if len(data) == 0 {
|
||||
return domain.RefreshTokenEntity{}, errors.New("no token found for user")
|
||||
}
|
||||
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
func (rt RefreshTokenRepository) DeleteById(id int) (int64, error) {
|
||||
builder := sqlbuilder.NewDeleteBuilder()
|
||||
builder.DeleteFrom(refreshTokenTableName)
|
||||
builder.Where(
|
||||
builder.EQ("Id", id),
|
||||
)
|
||||
|
||||
query, args := builder.Build()
|
||||
_, err := rt.connection.Exec(query, args...)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (rd RefreshTokenRepository) processRows(rows *sql.Rows) []domain.RefreshTokenEntity {
|
||||
items := []domain.RefreshTokenEntity{}
|
||||
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var username string
|
||||
var token string
|
||||
var expiresAt time.Time
|
||||
var createdAt time.Time
|
||||
var lastUpdated time.Time
|
||||
|
||||
err := rows.Scan(&id, &username, &token, &expiresAt, &createdAt, &lastUpdated)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
items = append(items, domain.RefreshTokenEntity{
|
||||
Id: id,
|
||||
Username: username,
|
||||
Token: token,
|
||||
ExpiresAt: expiresAt,
|
||||
CreatedAt: createdAt,
|
||||
LastUpdated: lastUpdated,
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
//func (rt RefreshTokenRepository) Delete()
|
111
internal/repositories/refreshTokens_test.go
Normal file
111
internal/repositories/refreshTokens_test.go
Normal file
@ -0,0 +1,111 @@
|
||||
package repositories_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/repositories"
|
||||
_ "github.com/glebarez/go-sqlite"
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
func TestRefreshTokenCreate(t *testing.T) {
|
||||
conn, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
client := repositories.NewRefreshTokenRepository(conn)
|
||||
rows, err := client.Create("tester", "BadTokenDontUse", time.Now().Add(time.Hour+1))
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
t.Log("expected one row to come back but got 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshTokenGetByUsername(t *testing.T) {
|
||||
conn, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
client := repositories.NewRefreshTokenRepository(conn)
|
||||
rows, err := client.Create("tester", "BadTokenDoNotUse", time.Now().Add(time.Hour+1))
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if rows != 1 {
|
||||
t.Log("expected a row to be added but not the wrong value back")
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
model, err := client.GetByUsername("tester")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if model.Username != "tester" {
|
||||
t.Log("got the wrong user back")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshTokenDeleteById(t *testing.T) {
|
||||
conn, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
client := repositories.NewRefreshTokenRepository(conn)
|
||||
_, err = client.Create("tester", "BadTokenDoNotUse", time.Now().Add(time.Hour+1))
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
model, err := client.GetByUsername("tester")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
updated, err := client.DeleteById(model.Id)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if updated != 1 {
|
||||
t.Log("deleted the wrong number of records")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func setupInMemoryDb() (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = goose.SetDialect("sqlite3")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = goose.Up(db, "../migrations")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
"golang.org/x/crypto/bcrypt"
|
@ -5,8 +5,8 @@ import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/repositories"
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/repositories"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
_ "github.com/glebarez/go-sqlite"
|
@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
@ -5,8 +5,8 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/repositories"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/domain"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/repositories"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
@ -3,7 +3,7 @@ package services_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.jamestombleson.com/jtom38/go-cook/api/services"
|
||||
"git.jamestombleson.com/jtom38/go-cook/internal/services"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
)
|
Loading…
Reference in New Issue
Block a user