Skip to content

Commit

Permalink
refactor: define repository and service error (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
kumachan-mis authored Mar 3, 2024
1 parent 08978f3 commit 46969b2
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 124 deletions.
24 changes: 12 additions & 12 deletions internal/api/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ func (api projectApi) HandleList(c *gin.Context) {
return
}

res, err := api.usecase.ListProjects(request)
res, ucErr := api.usecase.ListProjects(request)

if err != nil && err.Code() == usecase.InvalidArgumentError {
resErr := UseCaseErrorToResponse(err)
if ucErr != nil && ucErr.Code() == usecase.InvalidArgumentError {
resErr := UseCaseErrorToResponse(ucErr)
c.JSON(http.StatusBadRequest, model.ProjectListErrorResponse{
Message: UseCaseErrorToMessage(err),
Message: UseCaseErrorToMessage(ucErr),
User: resErr.User,
})
return
}

if err != nil {
if ucErr != nil {
c.JSON(http.StatusInternalServerError, model.ApplicationErrorResponse{
Message: UseCaseErrorToMessage(err),
Message: UseCaseErrorToMessage(ucErr),
})
return
}
Expand All @@ -60,21 +60,21 @@ func (api projectApi) HandleCreate(c *gin.Context) {
return
}

res, err := api.usecase.CreateProject(request)
res, ucErr := api.usecase.CreateProject(request)

if err != nil && err.Code() == usecase.InvalidArgumentError {
resErr := UseCaseErrorToResponse(err)
if ucErr != nil && ucErr.Code() == usecase.InvalidArgumentError {
resErr := UseCaseErrorToResponse(ucErr)
c.JSON(http.StatusBadRequest, model.ProjectCreateErrorResponse{
Message: UseCaseErrorToMessage(err),
Message: UseCaseErrorToMessage(ucErr),
User: resErr.User,
Project: resErr.Project,
})
return
}

if err != nil {
if ucErr != nil {
c.JSON(http.StatusInternalServerError, model.ApplicationErrorResponse{
Message: UseCaseErrorToMessage(err),
Message: UseCaseErrorToMessage(ucErr),
})
return
}
Expand Down
4 changes: 3 additions & 1 deletion internal/domain/user_id_object.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package domain

import "fmt"
import (
"fmt"
)

type UserIdObject struct {
value string
Expand Down
33 changes: 33 additions & 0 deletions internal/repository/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package repository

import (
"fmt"
)

type ErrorCode string

const (
ReadFailurePanic ErrorCode = "read failure"
WriteFailurePanic ErrorCode = "write failure"
)

type Error struct {
code ErrorCode
err error
}

func Errorf(code ErrorCode, format string, args ...any) *Error {
return &Error{code: code, err: fmt.Errorf(format, args...)}
}

func (e Error) Error() string {
return fmt.Sprintf("%s: %s", e.code, e.err.Error())
}

func (e Error) Unwrap() error {
return e.err
}

func (e Error) Code() ErrorCode {
return e.code
}
18 changes: 8 additions & 10 deletions internal/repository/project.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package repository

import (
"fmt"

"cloud.google.com/go/firestore"
"github.com/kumachan-mis/knodeledge-api/internal/db"
"github.com/kumachan-mis/knodeledge-api/internal/record"
Expand All @@ -13,8 +11,8 @@ import (
const ProjectCollection = "projects"

type ProjectRepository interface {
FetchUserProjects(userId string) (map[string]record.ProjectEntry, error)
InsertProject(entry record.ProjectWithoutAutofieldEntry) (string, *record.ProjectEntry, error)
FetchUserProjects(userId string) (map[string]record.ProjectEntry, *Error)
InsertProject(entry record.ProjectWithoutAutofieldEntry) (string, *record.ProjectEntry, *Error)
}

type projectRepository struct {
Expand All @@ -25,7 +23,7 @@ func NewProjectRepository(client firestore.Client) ProjectRepository {
return projectRepository{client: client}
}

func (r projectRepository) FetchUserProjects(userId string) (map[string]record.ProjectEntry, error) {
func (r projectRepository) FetchUserProjects(userId string) (map[string]record.ProjectEntry, *Error) {
iter := r.client.Collection(ProjectCollection).
Where("userId", "==", userId).
Documents(db.FirestoreContext())
Expand All @@ -41,7 +39,7 @@ func (r projectRepository) FetchUserProjects(userId string) (map[string]record.P
var entry record.ProjectEntry
err = snapshot.DataTo(&entry)
if err != nil {
return nil, fmt.Errorf("failed to convert snapshot to entry: %w", err)
return nil, Errorf(ReadFailurePanic, "failed to convert snapshot to entry: %w", err)
}

projects[snapshot.Ref.ID] = entry
Expand All @@ -50,7 +48,7 @@ func (r projectRepository) FetchUserProjects(userId string) (map[string]record.P
return projects, nil
}

func (r projectRepository) InsertProject(entry record.ProjectWithoutAutofieldEntry) (string, *record.ProjectEntry, error) {
func (r projectRepository) InsertProject(entry record.ProjectWithoutAutofieldEntry) (string, *record.ProjectEntry, *Error) {
ref, _, err := r.client.Collection(ProjectCollection).
Add(db.FirestoreContext(), map[string]any{
"name": entry.Name,
Expand All @@ -60,18 +58,18 @@ func (r projectRepository) InsertProject(entry record.ProjectWithoutAutofieldEnt
"updatedAt": firestore.ServerTimestamp,
})
if err != nil {
return "", nil, fmt.Errorf("failed to insert project: %w", err)
return "", nil, Errorf(WriteFailurePanic, "failed to insert project: %w", err)
}

snapshot, err := ref.Get(db.FirestoreContext())
if err != nil {
return "", nil, fmt.Errorf("failed to get inserted project: %w", err)
return "", nil, Errorf(ReadFailurePanic, "failed to get inserted project: %w", err)
}

var entryWithTimestamp record.ProjectEntry
err = snapshot.DataTo(&entryWithTimestamp)
if err != nil {
return "", nil, fmt.Errorf("failed to convert snapshot to entry: %w", err)
return "", nil, Errorf(ReadFailurePanic, "failed to convert snapshot to entry: %w", err)
}

return ref.ID, &entryWithTimestamp, nil
Expand Down
28 changes: 18 additions & 10 deletions internal/repository/project_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package repository_test

import (
"fmt"
"testing"
"time"

Expand All @@ -16,9 +17,9 @@ func TestFetchUserProjectsValidDocument(t *testing.T) {
r := repository.NewProjectRepository(*client)

userId := testutil.ReadOnlyUserId()
projects, err := r.FetchUserProjects(userId)
projects, rErr := r.FetchUserProjects(userId)

assert.NoError(t, err)
assert.Nil(t, rErr)

assert.Len(t, projects, 2)

Expand All @@ -42,25 +43,30 @@ func TestFetchUserProjectsNoDocument(t *testing.T) {
r := repository.NewProjectRepository(*client)

userId := testutil.UnknownUserId()
projects, err := r.FetchUserProjects(userId)
projects, rErr := r.FetchUserProjects(userId)

assert.NoError(t, err)
assert.Nil(t, rErr)

assert.Empty(t, projects)
}

func TestFetchUserProjectsInvalidDocument(t *testing.T) {
tt := []struct {
name string
userId string
name string
userId string
expectedError string
}{
{
name: "should return error when project name is invalid",
userId: testutil.ErrorUserId(0),
expectedError: "failed to convert snapshot to entry: record.ProjectEntry.name: " +
"firestore: cannot set type string to int",
},
{
name: "should return error when project description is invalid",
userId: testutil.ErrorUserId(1),
expectedError: "failed to convert snapshot to entry: record.ProjectEntry.description: " +
"firestore: cannot set type string to bool",
},
}

Expand All @@ -69,9 +75,11 @@ func TestFetchUserProjectsInvalidDocument(t *testing.T) {
client := db.FirestoreClient()
r := repository.NewProjectRepository(*client)

projects, err := r.FetchUserProjects(tc.userId)
projects, rErr := r.FetchUserProjects(tc.userId)

assert.ErrorContains(t, err, "failed to convert snapshot to entry:")
assert.NotNil(t, rErr)
assert.Equal(t, repository.ReadFailurePanic, rErr.Code())
assert.Equal(t, fmt.Sprintf("read failure: %s", tc.expectedError), rErr.Error())
assert.Nil(t, projects)
})
}
Expand All @@ -88,10 +96,10 @@ func TestInsertProjectValidEntry(t *testing.T) {
UserId: userId,
}

id, project, err := r.InsertProject(entry)
id, project, rErr := r.InsertProject(entry)
now := time.Now()

assert.NoError(t, err)
assert.Nil(t, rErr)

assert.NotEmpty(t, id)
assert.Equal(t, entry.Name, project.Name)
Expand Down
33 changes: 33 additions & 0 deletions internal/service/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package service

import (
"fmt"
)

type ErrorCode string

const (
DomainFailurePanic ErrorCode = "domain failure"
RepositoryFailurePanic ErrorCode = "repository failure"
)

type Error struct {
code ErrorCode
err error
}

func Errorf(code ErrorCode, format string, args ...any) *Error {
return &Error{code: code, err: fmt.Errorf(format, args...)}
}

func (e Error) Error() string {
return fmt.Sprintf("%s: %s", e.code, e.err.Error())
}

func (e Error) Unwrap() error {
return e.err
}

func (e Error) Code() ErrorCode {
return e.code
}
Loading

0 comments on commit 46969b2

Please sign in to comment.