tsk-18: Create service user (#22)
Closes #18 Reviewed-on: phoenix/textsender-auth#22 Co-authored-by: phoenix <kundeng00@pm.me> Co-committed-by: phoenix <kundeng00@pm.me>
This commit is contained in:
@@ -107,20 +107,22 @@ jobs:
|
||||
echo "DB_PORT=$DB_PORT" >> .env
|
||||
echo "DB_SSLMODE=$DB_SSLMODE" >> .env
|
||||
|
||||
echo "Initializing config"
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/textsender_models_deploy_key
|
||||
chmod 600 ~/.ssh/textsender_models_deploy_key
|
||||
ssh-keyscan ${{ vars.MY_HOST }} >> ~/.ssh/known_hosts
|
||||
|
||||
eval $(ssh-agent -s)
|
||||
ssh-add -v ~/.ssh/textsender_models_deploy_key
|
||||
|
||||
go env -w GOPRIVATE='${{ secrets.GIT_HOST_ROOT }}'
|
||||
|
||||
echo "Creating local .gitconfig"
|
||||
touch ~/.gitconfig
|
||||
cat > ~/.gitconfig << "EOF"
|
||||
[url "ssh://git@${{ secrets.GIT_HOST_ROOT }}"]
|
||||
insteadOf = https://${{ secrets.GIT_HOST_ROOT }}
|
||||
EOF
|
||||
|
||||
go test -v ./...
|
||||
|
||||
# - name: Run gofmt (optional)
|
||||
# Uncomment to check code formatting with gofmt
|
||||
# run: |
|
||||
# if [ -n "$(gofmt -l.)" ]; then
|
||||
# echo "Go code is not formatted. Please run 'gofmt -w.' to fix it."
|
||||
# exit 1
|
||||
# fi
|
||||
|
||||
# - name: Run golint (optional)
|
||||
# Uncomment to check code style with golint
|
||||
# run: |
|
||||
# if [ -n "$(golint./...)" ]; then
|
||||
# echo "Go code has style issues. Please run 'golint./...' to see them."
|
||||
# exit 1
|
||||
# fi
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/handler/endpoint"
|
||||
mdleware "git.kundeng.us/phoenix/textsender-auth/internal/middleware"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/model"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/store"
|
||||
)
|
||||
|
||||
// @title textsender-auth
|
||||
@@ -73,8 +74,11 @@ func main() {
|
||||
|
||||
// Services
|
||||
userStore := model.NewUserStore(db.Pool)
|
||||
serviceStore := store.NewServiceStore(db.Pool)
|
||||
|
||||
userHandler := handler.NewUserHandler(userStore)
|
||||
loginHandler := handler.NewLoginHandler(userStore)
|
||||
serviceHandler := handler.NewServiceHandler(serviceStore)
|
||||
|
||||
router := chi.NewRouter()
|
||||
|
||||
@@ -85,6 +89,7 @@ func main() {
|
||||
|
||||
router.Method("Post", endpoint.Register, http.HandlerFunc(userHandler.Register))
|
||||
router.Method("Post", endpoint.Login, http.HandlerFunc(loginHandler.Login))
|
||||
router.Method("Post", endpoint.CreateServiceUser, http.HandlerFunc(serviceHandler.Register))
|
||||
|
||||
router.Method("GET", "/swagger/*", httpSwagger.Handler(
|
||||
httpSwagger.URL(fmt.Sprintf("http://localhost:%s/swagger/doc.json", config.Port)),
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/handler"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/handler/endpoint"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/model"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/store"
|
||||
)
|
||||
|
||||
var testRouter *mux.Router
|
||||
@@ -23,27 +24,30 @@ var testRouter *mux.Router
|
||||
func TestMain(m *testing.M) {
|
||||
cfg := load()
|
||||
|
||||
db, err := db.NewDatabase(cfg.GetDBConnString())
|
||||
database, err := db.NewDatabase(cfg.GetDBConnString())
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
panic("Failed to initialize database")
|
||||
}
|
||||
defer db.Close()
|
||||
defer database.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
err = db.ResetDatabase(ctx)
|
||||
err = database.ResetDatabase(ctx)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
panic("Failed to initialize database")
|
||||
}
|
||||
|
||||
userStore := model.NewUserStore(db.Pool)
|
||||
userStore := model.NewUserStore(database.Pool)
|
||||
serviceStore := store.NewServiceStore(database.Pool)
|
||||
userHandler := handler.NewUserHandler(userStore)
|
||||
loginHandler := handler.NewLoginHandler(userStore)
|
||||
serviceHandler := handler.NewServiceHandler(serviceStore)
|
||||
|
||||
testRouter = mux.NewRouter()
|
||||
testRouter.HandleFunc(endpoint.Register, userHandler.Register).Methods("POST")
|
||||
testRouter.HandleFunc(endpoint.Login, loginHandler.Login).Methods("POST")
|
||||
testRouter.HandleFunc(endpoint.CreateServiceUser, serviceHandler.Register).Methods("POST")
|
||||
|
||||
code := m.Run()
|
||||
os.Exit(code)
|
||||
|
||||
@@ -116,6 +116,57 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/service/register": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Create a service user that can send texts (requires JWT)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"service users"
|
||||
],
|
||||
"summary": "Register service user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Data to add user",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ServiceCreationRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ServiceCreationResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ServiceCreationResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ServiceCreationResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -186,6 +237,31 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.ServiceCreationRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"passphrase": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.ServiceCreationResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/user.ServiceUser"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"token.Login": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -199,6 +275,23 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user.ServiceUser": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"date_created": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"passphrase": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -110,6 +110,57 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/service/register": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Create a service user that can send texts (requires JWT)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"service users"
|
||||
],
|
||||
"summary": "Register service user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Data to add user",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ServiceCreationRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ServiceCreationResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ServiceCreationResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.ServiceCreationResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -180,6 +231,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.ServiceCreationRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"passphrase": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.ServiceCreationResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/user.ServiceUser"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"token.Login": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -193,6 +269,23 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user.ServiceUser": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"date_created": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"passphrase": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -43,6 +43,22 @@ definitions:
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
handler.ServiceCreationRequest:
|
||||
properties:
|
||||
passphrase:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
handler.ServiceCreationResponse:
|
||||
properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/user.ServiceUser'
|
||||
type: array
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
token.Login:
|
||||
properties:
|
||||
access_token:
|
||||
@@ -52,6 +68,17 @@ definitions:
|
||||
token_type:
|
||||
type: string
|
||||
type: object
|
||||
user.ServiceUser:
|
||||
properties:
|
||||
date_created:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
passphrase:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
host: localhost:9080
|
||||
info:
|
||||
contact: {}
|
||||
@@ -123,6 +150,38 @@ paths:
|
||||
summary: Register user
|
||||
tags:
|
||||
- users
|
||||
/service/register:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Create a service user that can send texts (requires JWT)
|
||||
parameters:
|
||||
- description: Data to add user
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handler.ServiceCreationRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.ServiceCreationResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handler.ServiceCreationResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handler.ServiceCreationResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Register service user
|
||||
tags:
|
||||
- service users
|
||||
securityDefinitions:
|
||||
BearerAuth:
|
||||
description: JWT Bearer Token
|
||||
|
||||
@@ -3,7 +3,7 @@ module git.kundeng.us/phoenix/textsender-auth
|
||||
go 1.25.3
|
||||
|
||||
require (
|
||||
git.kundeng.us/phoenix/textsender-models v0.0.6-main-28b29802b9-556
|
||||
git.kundeng.us/phoenix/textsender-models v0.0.8-main-3ab81c2b6d-556
|
||||
github.com/go-chi/chi/v5 v5.2.3
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
git.kundeng.us/phoenix/textsender-models v0.0.6-main-28b29802b9-556 h1:2NI8cXaxt2rsXrhy/rDhgoOzvjLQwbhwr4nv2g35Ah8=
|
||||
git.kundeng.us/phoenix/textsender-models v0.0.6-main-28b29802b9-556/go.mod h1:lx5MCnOgGgsdpwzrfi9uph5xmkeb6H8AuexUNGss2no=
|
||||
git.kundeng.us/phoenix/textsender-models v0.0.8-main-3ab81c2b6d-556 h1:iVE8Dg3aYugoZseneN9tQtdV2cymukspShF7dfExSVw=
|
||||
git.kundeng.us/phoenix/textsender-models v0.0.8-main-3ab81c2b6d-556/go.mod h1:lx5MCnOgGgsdpwzrfi9uph5xmkeb6H8AuexUNGss2no=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
|
||||
@@ -3,3 +3,4 @@ package endpoint
|
||||
// Endpoint for registering a user
|
||||
const Register = "/api/v1/register"
|
||||
const Login = "/api/v1/login"
|
||||
const CreateServiceUser = "/api/v1/service/register"
|
||||
|
||||
@@ -12,11 +12,12 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/handler/endpoint"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/store/mock"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/utility"
|
||||
)
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
mockstore := NewMockUserStore()
|
||||
mockstore := mock.NewMockUserStore()
|
||||
handler := NewLoginHandler(mockstore)
|
||||
|
||||
testUser := GetTestUser()
|
||||
|
||||
@@ -10,10 +10,11 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/handler/endpoint"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/store/mock"
|
||||
)
|
||||
|
||||
func TestCreateUserWithMock(t *testing.T) {
|
||||
mockstore := NewMockUserStore()
|
||||
mockstore := mock.NewMockUserStore()
|
||||
handler := NewUserHandler(mockstore)
|
||||
|
||||
testUser := GetTestUser()
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.kundeng.us/phoenix/textsender-models/pkg/user"
|
||||
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/store"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/utility"
|
||||
)
|
||||
|
||||
type ServiceCreationRequest struct {
|
||||
Username string `json:"username"`
|
||||
Passphrase string `json:"passphrase"`
|
||||
}
|
||||
|
||||
type ServiceCreationResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data []*user.ServiceUser `json:"data"`
|
||||
}
|
||||
|
||||
type ServiceHandler struct {
|
||||
ServiceStore store.ServiceStore
|
||||
}
|
||||
|
||||
func NewServiceHandler(serviceStore store.ServiceStore) *ServiceHandler {
|
||||
return &ServiceHandler{ServiceStore: serviceStore}
|
||||
}
|
||||
|
||||
// Register godoc
|
||||
// @Summary Register service user
|
||||
// @Description Create a service user that can send texts (requires JWT)
|
||||
// @Tags service users
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param request body ServiceCreationRequest true "Data to add user"
|
||||
// @Success 200 {object} ServiceCreationResponse
|
||||
// @Failure 400 {object} ServiceCreationResponse
|
||||
// @Failure 500 {object} ServiceCreationResponse
|
||||
// @Router /service/register [post]
|
||||
func (s *ServiceHandler) Register(w http.ResponseWriter, r *http.Request) {
|
||||
var req ServiceCreationRequest
|
||||
if err := ExtractFromRequest(r, &req); err != nil {
|
||||
http.Error(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var statusCode int
|
||||
var resp ServiceCreationResponse
|
||||
|
||||
ctx := r.Context()
|
||||
if exists, err := s.ServiceStore.CheckWithUsername(ctx, req.Username); err != nil {
|
||||
statusCode = http.StatusInternalServerError
|
||||
resp.Message = err.Error()
|
||||
} else {
|
||||
if exists {
|
||||
statusCode = http.StatusBadRequest
|
||||
resp.Message = "Service user already exists"
|
||||
} else {
|
||||
hashing := utility.HashMash{Password: req.Passphrase}
|
||||
if hashedPassword, err := hashing.HashPassword(); err != nil {
|
||||
statusCode = http.StatusInternalServerError
|
||||
resp.Message = err.Error()
|
||||
} else {
|
||||
serviceUser := user.ServiceUser{Username: req.Username, Passphrase: hashedPassword}
|
||||
if err := s.ServiceStore.Create(ctx, &serviceUser); err != nil {
|
||||
statusCode = http.StatusInternalServerError
|
||||
resp.Message = err.Error()
|
||||
} else {
|
||||
statusCode = http.StatusCreated
|
||||
resp.Message = "Successful"
|
||||
resp.Data = append(resp.Data, &serviceUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RespondWithJson(w, statusCode, &resp)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/handler/endpoint"
|
||||
"git.kundeng.us/phoenix/textsender-auth/internal/store/mock"
|
||||
)
|
||||
|
||||
func TestCreateServiceUserWithMock(t *testing.T) {
|
||||
mockStore := mock.NewMockServiceUserStore()
|
||||
handler := NewServiceHandler(mockStore)
|
||||
|
||||
testService := ServiceCreationRequest{Username: "swoon", Passphrase: "ewrewr329n12y3x2!2"}
|
||||
jsonValue, err := json.Marshal(testService)
|
||||
assert.NoError(t, err, "Error marshaling request")
|
||||
|
||||
req, _ := http.NewRequest("POST", endpoint.CreateServiceUser, strings.NewReader(string(jsonValue)))
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
handler.Register(rr, req)
|
||||
assert.Equal(t, http.StatusCreated, rr.Code)
|
||||
|
||||
var response ServiceCreationResponse
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"git.kundeng.us/phoenix/textsender-models/pkg/user"
|
||||
)
|
||||
|
||||
type MockServiceUserStore struct {
|
||||
ServiceUsers map[uuid.UUID]*user.ServiceUser
|
||||
ServiceUsersByUsername map[string]*user.ServiceUser
|
||||
mu sync.RWMutex
|
||||
Error error // Optional: simulate errors
|
||||
}
|
||||
|
||||
func NewMockServiceUserStore() *MockServiceUserStore {
|
||||
return &MockServiceUserStore{
|
||||
ServiceUsers: make(map[uuid.UUID]*user.ServiceUser),
|
||||
ServiceUsersByUsername: make(map[string]*user.ServiceUser),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockServiceUserStore) Create(ctx context.Context, user *user.ServiceUser) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.Error != nil {
|
||||
return m.Error
|
||||
}
|
||||
|
||||
if user.Id == uuid.Nil {
|
||||
user.Id = uuid.New()
|
||||
}
|
||||
|
||||
if _, exists := m.ServiceUsersByUsername[user.Username]; exists {
|
||||
return errors.New("service User with username already exists")
|
||||
}
|
||||
|
||||
m.ServiceUsers[user.Id] = user
|
||||
m.ServiceUsersByUsername[user.Username] = user
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockServiceUserStore) CheckWithUsername(ctx context.Context, username string) (bool, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.Error != nil {
|
||||
return false, m.Error
|
||||
}
|
||||
|
||||
if m.Error != nil {
|
||||
return false, m.Error
|
||||
}
|
||||
|
||||
var exists bool
|
||||
|
||||
for _, serviceUser := range m.ServiceUsers {
|
||||
if serviceUser.Username == username {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return exists, nil
|
||||
} else {
|
||||
return exists, nil
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package handler
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -0,0 +1,50 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.kundeng.us/phoenix/textsender-models/pkg/user"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type ServiceStore interface {
|
||||
CheckWithUsername(ctx context.Context, username string) (bool, error)
|
||||
Create(ctx context.Context, serviceUser *user.ServiceUser) error
|
||||
}
|
||||
|
||||
type PGServiceStore struct {
|
||||
db *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewServiceStore(db *pgxpool.Pool) *PGServiceStore {
|
||||
return &PGServiceStore{db: db}
|
||||
}
|
||||
|
||||
func (s *PGServiceStore) CheckWithUsername(ctx context.Context, username string) (bool, error) {
|
||||
var exists bool
|
||||
query := `SELECT EXISTS(SELECT 1 FROM service_users WHERE Username = $1)`
|
||||
|
||||
if err := s.db.QueryRow(ctx, query, username).Scan(&exists); err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return exists, nil
|
||||
} else {
|
||||
return exists, fmt.Errorf("Error querying row: %v", err)
|
||||
}
|
||||
} else {
|
||||
return exists, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PGServiceStore) Create(ctx context.Context, serviceUser *user.ServiceUser) error {
|
||||
query := `
|
||||
INSERT INTO service_users (username, passphrase)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, created
|
||||
`
|
||||
|
||||
return s.db.QueryRow(ctx, query, serviceUser.Username, serviceUser.Passphrase).Scan(
|
||||
&serviceUser.Id, &serviceUser.Created,
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
DROP TABLE IF EXISTS users CASCADE;
|
||||
DROP TABLE IF EXISTS service_users CASCADE;
|
||||
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
@@ -9,3 +10,9 @@ CREATE TABLE users (
|
||||
password TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE service_users (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
username TEXT NOT NULL,
|
||||
passphrase TEXT NOT NULL,
|
||||
created timestamptz DEFAULT now()
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user