Skip to content

Commit 102f325

Browse files
committed
add author books relations and basic structure
1 parent 279a5e4 commit 102f325

File tree

20 files changed

+592
-3
lines changed

20 files changed

+592
-3
lines changed

data/authors/mapper.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package authors
2+
3+
import (
4+
domain "github.com/eldimious/golang-api-showcase/domain/authors"
5+
)
6+
7+
func toDBModel(entity *domain.Author) *Author {
8+
return &Author{
9+
ID: entity.ID,
10+
Name: entity.Name,
11+
Surname: entity.Surname,
12+
}
13+
}
14+
15+
func toDomainModel(entity *Author) *domain.Author {
16+
return &domain.Author{
17+
ID: entity.ID,
18+
Name: entity.Name,
19+
Surname: entity.Surname,
20+
CreatedAt: entity.CreatedAt,
21+
UpdatedAt: entity.UpdatedAt,
22+
DeletedAt: entity.DeletedAt,
23+
}
24+
}

data/authors/schema.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package authors
2+
3+
import (
4+
"time"
5+
)
6+
7+
// struct defines the database model for a Author.
8+
type Author struct {
9+
ID int `gorm:"primary_key;type:int;"`
10+
CreatedAt time.Time
11+
UpdatedAt time.Time
12+
DeletedAt *time.Time
13+
Name string
14+
Surname string
15+
}

data/authors/store.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package authors
2+
3+
import (
4+
"github.com/jinzhu/gorm"
5+
"github.com/pkg/errors"
6+
7+
domain "github.com/eldimious/golang-api-showcase/domain/authors"
8+
booksDomain "github.com/eldimious/golang-api-showcase/domain/books"
9+
domainErrors "github.com/eldimious/golang-api-showcase/domain/errors"
10+
)
11+
12+
const (
13+
createError = "Error in creating new author"
14+
readError = "Error in finding author in the database"
15+
listError = "Error in getting authors from the database"
16+
)
17+
18+
// Store struct manages interactions with authors store
19+
type Store struct {
20+
db *gorm.DB
21+
booksRepo booksDomain.BookRepository
22+
}
23+
24+
// New creates a new Store struct
25+
func New(db *gorm.DB, booksRepo booksDomain.BookRepository) *Store {
26+
db.AutoMigrate(&Author{})
27+
28+
return &Store{
29+
db: db,
30+
booksRepo: booksRepo,
31+
}
32+
}
33+
34+
func (s *Store) CreateAuthor(author *domain.Author) (*domain.Author, error) {
35+
entity := toDBModel(author)
36+
37+
if err := s.db.Create(entity).Error; err != nil {
38+
appErr := domainErrors.NewAppError(errors.Wrap(err, createError), domainErrors.RepositoryError)
39+
return nil, appErr
40+
}
41+
42+
return toDomainModel(entity), nil
43+
}
44+
45+
func (s *Store) ReadAuthor(id int) (*domain.Author, error) {
46+
result := &Author{}
47+
48+
query := s.db.Where("id = ?", id).First(result)
49+
50+
if query.RecordNotFound() {
51+
appErr := domainErrors.NewAppErrorWithType(domainErrors.NotFound)
52+
return nil, appErr
53+
}
54+
55+
if err := query.Error; err != nil {
56+
appErr := domainErrors.NewAppError(errors.Wrap(err, readError), domainErrors.RepositoryError)
57+
return nil, appErr
58+
}
59+
60+
return toDomainModel(result), nil
61+
}
62+
63+
func (s *Store) ListAuthors() ([]domain.Author, error) {
64+
var results []Author
65+
66+
if err := s.db.Find(&results).Error; err != nil {
67+
appErr := domainErrors.NewAppError(errors.Wrap(err, listError), domainErrors.RepositoryError)
68+
return nil, appErr
69+
}
70+
71+
var authors = make([]domain.Author, len(results))
72+
73+
for i, element := range results {
74+
authors[i] = *toDomainModel(&element)
75+
}
76+
77+
return authors, nil
78+
}

data/books/mapper.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package books
2+
3+
import (
4+
domain "github.com/eldimious/golang-api-showcase/domain/books"
5+
)
6+
7+
func toDBModel(entity *domain.Book) *Book {
8+
return &Book{
9+
ID: entity.ID,
10+
Name: entity.Name,
11+
Publisher: entity.Publisher,
12+
AuthorID: entity.AuthorID,
13+
}
14+
}
15+
16+
func toDomainModel(entity *Book) *domain.Book {
17+
return &domain.Book{
18+
ID: entity.ID,
19+
Name: entity.Name,
20+
Publisher: entity.Publisher,
21+
CreatedAt: entity.CreatedAt,
22+
UpdatedAt: entity.UpdatedAt,
23+
DeletedAt: entity.DeletedAt,
24+
}
25+
}

data/books/schema.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package books
2+
3+
import (
4+
"time"
5+
6+
authorSchema "github.com/eldimious/golang-api-showcase/data/authors"
7+
)
8+
9+
// struct defines the database model for a Author.
10+
type Book struct {
11+
ID int `gorm:"primary_key;type:int;"`
12+
CreatedAt time.Time
13+
UpdatedAt time.Time
14+
DeletedAt *time.Time
15+
Name string
16+
Publisher string
17+
Author authorSchema.Author
18+
AuthorID int `gorm:"type:int;"`
19+
}

data/books/store.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package books
2+
3+
import (
4+
"github.com/jinzhu/gorm"
5+
"github.com/pkg/errors"
6+
7+
authorSchema "github.com/eldimious/golang-api-showcase/data/authors"
8+
domain "github.com/eldimious/golang-api-showcase/domain/books"
9+
domainErrors "github.com/eldimious/golang-api-showcase/domain/errors"
10+
)
11+
12+
const (
13+
createError = "Error in creating new book"
14+
readError = "Error in finding book in the database"
15+
listError = "Error in getting books from the database"
16+
)
17+
18+
// Store struct manages interactions with books store
19+
type Store struct {
20+
db *gorm.DB
21+
}
22+
23+
// New creates a new Store struct
24+
func New(db *gorm.DB) *Store {
25+
db.AutoMigrate(&Book{})
26+
27+
return &Store{
28+
db: db,
29+
}
30+
}
31+
32+
// AddAuthorFKConstraint creates a foreign key constraint for the author ID field
33+
func (s *Store) AddAuthorFKConstraint() {
34+
s.db.Model(&Book{}).AddForeignKey("author_id", "authors(id)", "CASCADE", "CASCADE")
35+
}
36+
37+
func (s *Store) CreateBook(book *domain.Book) (*domain.Book, error) {
38+
entity := toDBModel(book)
39+
40+
if err := s.db.Create(entity).Error; err != nil {
41+
appErr := domainErrors.NewAppError(errors.Wrap(err, createError), domainErrors.RepositoryError)
42+
return nil, appErr
43+
}
44+
45+
return toDomainModel(entity), nil
46+
}
47+
48+
func (s *Store) ReadBook(id int) (*domain.Book, error) {
49+
result := &Book{}
50+
51+
query := s.db.Where("id = ?", id).First(result)
52+
53+
if query.RecordNotFound() {
54+
appErr := domainErrors.NewAppErrorWithType(domainErrors.NotFound)
55+
return nil, appErr
56+
}
57+
58+
if err := query.Error; err != nil {
59+
appErr := domainErrors.NewAppError(errors.Wrap(err, readError), domainErrors.RepositoryError)
60+
return nil, appErr
61+
}
62+
63+
return toDomainModel(result), nil
64+
}
65+
66+
func (s *Store) ListBooks(authorID int) ([]domain.Book, error) {
67+
var results []Book
68+
author := &authorSchema.Author{
69+
ID: authorID,
70+
}
71+
72+
if err := s.db.Model(&author).Related(&results).Error; err != nil {
73+
appErr := domainErrors.NewAppError(errors.Wrap(err, listError), domainErrors.RepositoryError)
74+
return nil, appErr
75+
}
76+
77+
var books = make([]domain.Book, len(results))
78+
79+
for i, element := range results {
80+
books[i] = *toDomainModel(&element)
81+
}
82+
83+
return books, nil
84+
}

data/database/postgres.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
_ "github.com/jinzhu/gorm/dialects/postgres" // Import GORM postgres dialect for its side effects, according to GORM docs.
99
)
1010

11-
// Open creates a database handle from a connection string.
12-
func Open(configuration *config.Postgres) (*gorm.DB, error) {
11+
// Connect to a database handle from a connection string.
12+
func Connect(configuration *config.Postgres) (*gorm.DB, error) {
1313
connStr := fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=disable", configuration.Host, configuration.Port, configuration.DB, configuration.User, configuration.Password)
1414
db, err := gorm.Open("postgres", connStr)
1515

domain/authors/author.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package authors
2+
3+
import (
4+
"time"
5+
)
6+
7+
// Author struct contains information an author.
8+
type Author struct {
9+
ID int
10+
Name string
11+
Surname string
12+
CreatedAt time.Time
13+
UpdatedAt time.Time
14+
DeletedAt *time.Time
15+
}

domain/authors/repository.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package authors
2+
3+
// AuthorRepository provides an abstraction on top of the author data source
4+
type AuthorRepository interface {
5+
CreateAuthor(*Author) (*Author, error)
6+
ReadAuthor(int) (*Author, error)
7+
ListAuthors() ([]Author, error)
8+
}

domain/authors/service.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package authors
2+
3+
// AuthorService defines author service behavior.
4+
type AuthorService interface {
5+
CreateAuthor(*Author) (*Author, error)
6+
ReadAuthor(id int) (*Author, error)
7+
ListAuthors() ([]Author, error)
8+
}
9+
10+
// Service struct handles author business logic tasks.
11+
type Service struct {
12+
repository AuthorRepository
13+
}
14+
15+
func (svc *Service) CreateAuthor(author *Author) (*Author, error) {
16+
return svc.repository.CreateAuthor(author)
17+
}
18+
19+
func (svc *Service) ReadAuthor(id int) (*Author, error) {
20+
return svc.repository.ReadAuthor(id)
21+
}
22+
23+
func (svc *Service) ListAuthors() ([]Author, error) {
24+
return svc.repository.ListAuthors()
25+
}
26+
27+
// NewService creates a new service struct
28+
func NewService(repository AuthorRepository) *Service {
29+
return &Service{repository: repository}
30+
}

0 commit comments

Comments
 (0)