Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#69 Refactoring Web APIs #70

Merged
merged 12 commits into from
Jul 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
${{ runner.os }}-go-
# Run golangci-lint using reviewdog
- name: golangci-lint
uses: reviewdog/action-golangci-lint@v1.21
uses: reviewdog/action-golangci-lint@v1.24
with:
github_token: ${{ secrets.github_token }}
level: warning
Expand Down
29 changes: 14 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,40 +113,39 @@ There are the following services in the book management.

|Service Name|HTTP Method|URL|Parameter|Summary|
|:---|:---:|:---|:---|:---|
|Get Service|GET|``/api/book?id=[BOOK_ID]``|Book ID|Get a book data.|
|List Service|GET|``/api/book/list?page=[PAGE_NUMBER]&size=[PAGE_SIZE]``|Page|Get a list of books.|
|Regist Service|POST|``/api/book/new``|Book|Regist a book data.|
|Edit Service|POST|``/api/book/edit``|Book|Edit a book data.|
|Delete Service|POST|``/api/book/delete``|Book|Delete a book data.|
|Search Title Service|GET|``/api/book/search?query=[KEYWORD]&page=[PAGE_NUMBER]&size=[PAGE_SIZE]``|Keyword, Page|Search a title with the specified keyword.|
|Get Service|GET|``/api/books/[BOOK_ID]``|Book ID|Get a book data.|
|List/Search Service|GET|``/api/books?query=[KEYWORD]&page=[PAGE_NUMBER]&size=[PAGE_SIZE]``|Page, Keyword(Optional)|Get a list of books.|
|Regist Service|POST|``/api/books``|Book|Regist a book data.|
|Edit Service|PUT|``/api/books``|Book|Edit a book data.|
|Delete Service|DELETE|``/api/books``|Book|Delete a book data.|

### Account Management
There are the following services in the Account management.

|Service Name|HTTP Method|URL|Parameter|Summary|
|:---|:---:|:---|:---|:---|
|Login Service|POST|``/api/account/login``|Session ID, User Name, Password|Session authentication with username and password.|
|Logout Service|POST|``/api/account/logout``|Session ID|Logout a user.|
|Login Status Check Service|GET|``/api/account/loginStatus``|Session ID|Check if the user is logged in.|
|Login Username Service|GET|``/api/account/loginAccount``|Session ID|Get the login user's username.|
|Login Service|POST|``/api/auth/login``|Session ID, User Name, Password|Session authentication with username and password.|
|Logout Service|POST|``/api/auth/logout``|Session ID|Logout a user.|
|Login Status Check Service|GET|``/api/auth/loginStatus``|Session ID|Check if the user is logged in.|
|Login Username Service|GET|``/api/auth/loginAccount``|Session ID|Get the login user's username.|

### Master Management
There are the following services in the Master management.

|Service Name|HTTP Method|URL|Parameter|Summary|
|:---|:---:|:---|:---|:---|
|Category List Service|GET|``/api/master/category``|Nothing|Get a list of categories.|
|Format List Service|GET|``/api/master/format``|Nothing|Get a list of formats.|
|Category List Service|GET|``/api/categories``|Nothing|Get a list of categories.|
|Format List Service|GET|``/api/formats``|Nothing|Get a list of formats.|

## Libraries
This sample uses the following libraries.

|Library Name|Version|
|:---|:---:|
|Echo|4.3.0|
|Gorm|1.9.16|
|echo|4.3.0|
|gorm|1.21.11|
|go-playground/validator.v9|9.31.0|
|Zap/logger|1.17.0|
|zap|1.18.1|

## Contribution
Please read [CONTRIBUTING.md](https://github.com/ybkuroki/go-webapp-sample/blob/master/CONTRIBUTING.md) for proposing new functions, reporting bugs and submitting pull requests before contributing to this repository.
Expand Down
9 changes: 3 additions & 6 deletions application.develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,10 @@ security:
auth_path:
- /api/.*
exclude_path:
- /api/account/login$
- /api/account/logout$
- /api/auth/login$
- /api/auth/logout$
- /api/health$
user_path:
- /api/account/.*
- /api/master/.*
- /api/book/list
- /api/book/search.*
- /api/.*
admin_path:
- /api/.*
9 changes: 3 additions & 6 deletions application.docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,10 @@ security:
auth_path:
- /api/.*
exclude_path:
- /api/account/login$
- /api/account/logout$
- /api/auth/login$
- /api/auth/logout$
- /api/health$
user_path:
- /api/account/.*
- /api/master/.*
- /api/book/list
- /api/book/search.*
- /api/.*
admin_path:
- /api/.*
9 changes: 3 additions & 6 deletions application.k8s.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,10 @@ security:
auth_path:
- /api/.*
exclude_path:
- /api/account/login$
- /api/account/logout$
- /api/auth/login$
- /api/auth/logout$
- /api/health$
user_path:
- /api/account/.*
- /api/master/.*
- /api/book/list
- /api/book/search.*
- /api/.*
admin_path:
- /api/.*
17 changes: 10 additions & 7 deletions controller/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/labstack/echo/v4"
"github.com/ybkuroki/go-webapp-sample/model"
"github.com/ybkuroki/go-webapp-sample/model/dto"
"github.com/ybkuroki/go-webapp-sample/mycontext"
"github.com/ybkuroki/go-webapp-sample/service"
"github.com/ybkuroki/go-webapp-sample/session"
Expand Down Expand Up @@ -39,14 +40,16 @@ func (controller *AccountController) GetLoginAccount(c echo.Context) error {
return c.JSON(http.StatusOK, session.GetAccount(c))
}

// PostLogin is the method to login using username and password by http post.
func (controller *AccountController) PostLogin(c echo.Context) error {
username := c.FormValue("username")
password := c.FormValue("password")
// Login is the method to login using username and password by http post.
func (controller *AccountController) Login(c echo.Context) error {
dto := dto.NewLoginDto()
if err := c.Bind(dto); err != nil {
return c.JSON(http.StatusBadRequest, dto)
}

account := session.GetAccount(c)
if account == nil {
authenticate, a := controller.service.AuthenticateByUsernameAndPassword(username, password)
authenticate, a := controller.service.AuthenticateByUsernameAndPassword(dto.UserName, dto.Password)
if authenticate {
_ = session.SetAccount(c, a)
_ = session.Save(c)
Expand All @@ -57,8 +60,8 @@ func (controller *AccountController) PostLogin(c echo.Context) error {
return c.JSON(http.StatusOK, account)
}

// PostLogout is the method to logout by http post.
func (controller *AccountController) PostLogout(c echo.Context) error {
// Logout is the method to logout by http post.
func (controller *AccountController) Logout(c echo.Context) error {
_ = session.SetAccount(c, nil)
_ = session.Delete(c)
return c.NoContent(http.StatusOK)
Expand Down
31 changes: 9 additions & 22 deletions controller/api_const.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,18 @@ const (
// API represents the group of API.
API = "/api"
// APIBook represents the group of book management API.
APIBook = API + "/book"
// APIBookList represents the API to get book's list.
APIBookList = APIBook + "/list"
// APIBookSearch represents the API to search book's list.
APIBookSearch = APIBook + "/search"
// APIBookRegist represents the API to register a new book.
APIBookRegist = APIBook + "/new"
// APIBookEdit represents the API to edit the existing book.
APIBookEdit = APIBook + "/edit"
// APIBookDelete represents the API to delete the existing book.
APIBookDelete = APIBook + "/delete"
APIBooks = API + "/books"
// APIBooksID represents the API to get book data using id.
APIBooksID = APIBooks + "/:id"
// APICategory represents the group of category management API.
APICategories = API + "/categories"
// APIFormat represents the group of format management API.
APIFormats = API + "/formats"
)

const (
// APIMaster represents the group of master management API.
APIMaster = API + "/master"
// APIMasterCategory represents the API to get category's list.
APIMasterCategory = APIMaster + "/category"
// APIMasterFormat represents the API to get format's list.
APIMasterFormat = APIMaster + "/format"
)

const (
// APIAccount represents the group of account management API.
APIAccount = API + "/account"
// APIAccount represents the group of auth management API.
APIAccount = API + "/auth"
// APIAccountLoginStatus represents the API to get the status of logged in account.
APIAccountLoginStatus = APIAccount + "/loginStatus"
// APIAccountLoginAccount represents the API to get the logged in account.
Expand Down
35 changes: 13 additions & 22 deletions controller/book.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,52 +22,43 @@ func NewBookController(context mycontext.Context) *BookController {

// GetBook returns one record matched book's id.
func (controller *BookController) GetBook(c echo.Context) error {
return c.JSON(http.StatusOK, controller.service.FindByID(c.QueryParam("id")))
return c.JSON(http.StatusOK, controller.service.FindByID(c.Param("id")))
}

// GetBookList returns the list of all books.
// GetBookList returns the list of matched books by searching.
func (controller *BookController) GetBookList(c echo.Context) error {
return c.JSON(http.StatusOK, controller.service.FindAllBooksByPage(c.QueryParam("page"), c.QueryParam("size")))
}

// GetBookSearch returns the list of matched books by searching.
func (controller *BookController) GetBookSearch(c echo.Context) error {
return c.JSON(http.StatusOK, controller.service.FindBooksByTitle(c.QueryParam("query"), c.QueryParam("page"), c.QueryParam("size")))
}

// PostBookRegist register a new book by http post.
func (controller *BookController) PostBookRegist(c echo.Context) error {
dto := dto.NewRegBookDto()
// CreateBook create a new book by http post.
func (controller *BookController) CreateBook(c echo.Context) error {
dto := dto.NewBookDto()
if err := c.Bind(dto); err != nil {
return c.JSON(http.StatusBadRequest, dto)
}
book, result := controller.service.RegisterBook(dto)
book, result := controller.service.CreateBook(dto)
if result != nil {
return c.JSON(http.StatusBadRequest, result)
}
return c.JSON(http.StatusOK, book)
}

// PostBookEdit edit the existing book by http post.
func (controller *BookController) PostBookEdit(c echo.Context) error {
dto := dto.NewChgBookDto()
// UpdateBook update the existing book by http post.
func (controller *BookController) UpdateBook(c echo.Context) error {
dto := dto.NewBookDto()
if err := c.Bind(dto); err != nil {
return c.JSON(http.StatusBadRequest, dto)
}
book, result := controller.service.EditBook(dto)
book, result := controller.service.UpdateBook(dto, c.Param("id"))
if result != nil {
return c.JSON(http.StatusBadRequest, result)
}
return c.JSON(http.StatusOK, book)
}

// PostBookDelete deletes the existing book by http post.
func (controller *BookController) PostBookDelete(c echo.Context) error {
dto := dto.NewChgBookDto()
if err := c.Bind(dto); err != nil {
return c.JSON(http.StatusBadRequest, dto)
}
book, result := controller.service.DeleteBook(dto)
// DeleteBook deletes the existing book by http post.
func (controller *BookController) DeleteBook(c echo.Context) error {
book, result := controller.service.DeleteBook(c.Param("id"))
if result != nil {
return c.JSON(http.StatusBadRequest, result)
}
Expand Down
63 changes: 21 additions & 42 deletions controller/book_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,11 @@ func TestGetBookList(t *testing.T) {
router, context := test.Prepare()

book := NewBookController(context)
router.GET(APIBookList, func(c echo.Context) error { return book.GetBookList(c) })
router.GET(APIBooks, func(c echo.Context) error { return book.GetBookList(c) })

setUpTestData(context)

uri := test.NewRequestBuilder().URL(APIBookList).Params("page", "0").Params("size", "5").Build().GetRequestURL()
req := httptest.NewRequest("GET", uri, nil)
rec := httptest.NewRecorder()

router.ServeHTTP(rec, req)

entity := &model.Book{}
data, _ := entity.FindAllByPage(context.GetRepository(), "0", "5")

assert.Equal(t, http.StatusOK, rec.Code)
assert.JSONEq(t, test.ConvertToString(data), rec.Body.String())
}

func TestGetBookSearch(t *testing.T) {
router, context := test.Prepare()

book := NewBookController(context)
router.GET(APIBookSearch, func(c echo.Context) error { return book.GetBookSearch(c) })

setUpTestData(context)

uri := test.NewRequestBuilder().URL(APIBookSearch).Params("query", "Test").Params("page", "0").Params("size", "5").Build().GetRequestURL()
uri := test.NewRequestBuilder().URL(APIBooks).RequestParams("query", "Test").RequestParams("page", "0").RequestParams("size", "5").Build().GetRequestURL()
req := httptest.NewRequest("GET", uri, nil)
rec := httptest.NewRecorder()

Expand All @@ -56,14 +35,14 @@ func TestGetBookSearch(t *testing.T) {
assert.JSONEq(t, test.ConvertToString(data), rec.Body.String())
}

func TestPostBookRegist(t *testing.T) {
func TestCreateBook(t *testing.T) {
router, context := test.Prepare()

book := NewBookController(context)
router.POST(APIBookRegist, func(c echo.Context) error { return book.PostBookRegist(c) })
router.POST(APIBooks, func(c echo.Context) error { return book.CreateBook(c) })

param := createRegDto()
req := httptest.NewRequest("POST", APIBookRegist, strings.NewReader(test.ConvertToString(param)))
param := createDto()
req := httptest.NewRequest("POST", APIBooks, strings.NewReader(test.ConvertToString(param)))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
rec := httptest.NewRecorder()
Expand All @@ -77,16 +56,17 @@ func TestPostBookRegist(t *testing.T) {
assert.JSONEq(t, test.ConvertToString(data), rec.Body.String())
}

func TestPostBookEdit(t *testing.T) {
func TestUpdateBook(t *testing.T) {
router, context := test.Prepare()

book := NewBookController(context)
router.POST(APIBookEdit, func(c echo.Context) error { return book.PostBookEdit(c) })
router.PUT(APIBooksID, func(c echo.Context) error { return book.UpdateBook(c) })

setUpTestData(context)

param := createChgDto()
req := httptest.NewRequest("POST", APIBookEdit, strings.NewReader(test.ConvertToString(param)))
param := changeDto()
uri := test.NewRequestBuilder().URL(APIBooks).PathParams("1").Build().GetRequestURL()
req := httptest.NewRequest("PUT", uri, strings.NewReader(test.ConvertToString(param)))
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
rec := httptest.NewRecorder()
Expand All @@ -100,19 +80,19 @@ func TestPostBookEdit(t *testing.T) {
assert.JSONEq(t, test.ConvertToString(data), rec.Body.String())
}

func TestPostBookDelete(t *testing.T) {
func TestDeleteBook(t *testing.T) {
router, context := test.Prepare()

book := NewBookController(context)
router.POST(APIBookDelete, func(c echo.Context) error { return book.PostBookDelete(c) })
router.DELETE(APIBooksID, func(c echo.Context) error { return book.DeleteBook(c) })

setUpTestData(context)

entity := &model.Book{}
data, _ := entity.FindByID(context.GetRepository(), 1)

param := createChgDto()
req := httptest.NewRequest("POST", APIBookDelete, strings.NewReader(test.ConvertToString(param)))
uri := test.NewRequestBuilder().URL(APIBooks).PathParams("1").Build().GetRequestURL()
req := httptest.NewRequest("DELETE", uri, nil)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
rec := httptest.NewRecorder()
Expand All @@ -129,8 +109,8 @@ func setUpTestData(context mycontext.Context) {
_, _ = model.Create(repo)
}

func createRegDto() *dto.RegBookDto {
dto := &dto.RegBookDto{
func createDto() *dto.BookDto {
dto := &dto.BookDto{
Title: "Test1",
Isbn: "123-123-123-1",
CategoryID: 1,
Expand All @@ -139,11 +119,10 @@ func createRegDto() *dto.RegBookDto {
return dto
}

func createChgDto() *dto.ChgBookDto {
dto := &dto.ChgBookDto{
ID: 1,
Title: "EditedTest1",
Isbn: "234-234-234-2",
func changeDto() *dto.BookDto {
dto := &dto.BookDto{
Title: "Test2",
Isbn: "123-123-123-2",
CategoryID: 2,
FormatID: 2,
}
Expand Down
Loading