Skip to content

Commit

Permalink
Merge pull request #74 from ybkuroki/develop
Browse files Browse the repository at this point in the history
#29 Add Swagger
  • Loading branch information
ybkuroki authored Aug 1, 2021
2 parents a8a371f + a569b95 commit ac72ed8
Show file tree
Hide file tree
Showing 34 changed files with 1,169 additions and 226 deletions.
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.24
uses: reviewdog/action-golangci-lint@v2
with:
github_token: ${{ secrets.github_token }}
level: warning
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
with:
go-version: 1.16
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2.6.1
uses: goreleaser/goreleaser-action@v2.7.0
with:
version: latest
args: release --rm-dist
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@

# Dependency directories (remove the comment below to include it)
#vendor/

# Output file of the swaggo/swag
swagger.json
swagger.yaml

# Debug folder
.vscode
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@ There are 2 methods for starting server.
- username : ``test``
- password : ``test``

## Using Swagger
In this sample, Swagger is enabled only when executed this application on the development environment.
Swagger isn't enabled on the another environments in default.
### Accessing to Swagger
1. Start this application according to the 'Starting Application Server' section.
2. Access [http://localhost:8080/swagger/index.html](http://localhost:8080/swagger/index.html) in your browser.
### Updating the existing Swagger document
1. Update some comments of some controllers.
2. Download Swag library. (Only first time)
```bash
go get github.com/swaggo/swag/cmd/swag
```
3. Update ``docs/docs.go``.
```bash
swag init
```
## Build executable file
Build this source code by the following command.
```bash
Expand Down Expand Up @@ -107,6 +126,7 @@ The follwing figure is the map of this sample project.
## Services
This sample provides 3 services: book management, account management, and master management.
Regarding the detail of the API specification, please refer to the 'Using Swagger' section.
### Book Management
There are the following services in the book management.
Expand Down
1 change: 1 addition & 0 deletions application.develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ security:
auth_path:
- /api/.*
exclude_path:
- /swagger/.*
- /api/auth/login$
- /api/auth/logout$
- /api/health$
Expand Down
48 changes: 48 additions & 0 deletions container/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package container

import (
"github.com/ybkuroki/go-webapp-sample/config"
"github.com/ybkuroki/go-webapp-sample/logger"
"github.com/ybkuroki/go-webapp-sample/repository"
)

// Container represents a interface for accessing the data which sharing in overall application.
type Container interface {
GetRepository() repository.Repository
GetConfig() *config.Config
GetLogger() *logger.Logger
GetEnv() string
}

// container struct is for sharing data which such as database setting, the setting of application and logger in overall this application.
type container struct {
rep repository.Repository
config *config.Config
logger *logger.Logger
env string
}

// NewContainer is constructor.
func NewContainer(rep repository.Repository, config *config.Config, logger *logger.Logger, env string) Container {
return &container{rep: rep, config: config, logger: logger, env: env}
}

// GetRepository returns the object of repository.
func (c *container) GetRepository() repository.Repository {
return c.rep
}

// GetConfig returns the object of configuration.
func (c *container) GetConfig() *config.Config {
return c.config
}

// GetLogger returns the object of logger.
func (c *container) GetLogger() *logger.Logger {
return c.logger
}

// GetEnv returns the running environment.
func (c *container) GetEnv() string {
return c.env
}
42 changes: 37 additions & 5 deletions controller/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,51 @@ import (
"net/http"

"github.com/labstack/echo/v4"
"github.com/ybkuroki/go-webapp-sample/container"
"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"
)

// AccountController is a controller for managing user account.
type AccountController struct {
context mycontext.Context
context container.Container
service *service.AccountService
dummyAccount *model.Account
}

// NewAccountController is constructor.
func NewAccountController(context mycontext.Context) *AccountController {
func NewAccountController(container container.Container) *AccountController {
return &AccountController{
context: context,
service: service.NewAccountService(context),
context: container,
service: service.NewAccountService(container),
dummyAccount: model.NewAccountWithPlainPassword("test", "test", 1),
}
}

// GetLoginStatus returns the status of login.
// @Summary Get the login status.
// @Description Get the login status of current logged-in user.
// @Tags Auth
// @Accept json
// @Produce json
// @Success 200 {boolean} bool "The current user have already logged-in. Returns true."
// @Failure 401 {boolean} bool "The current user haven't logged-in yet. Returns false."
// @Router /auth/loginStatus [get]
func (controller *AccountController) GetLoginStatus(c echo.Context) error {
return c.JSON(http.StatusOK, true)
}

// GetLoginAccount returns the account data of logged in user.
// @Summary Get the account data of logged-in user.
// @Description Get the account data of logged-in user.
// @Tags Auth
// @Accept json
// @Produce json
// @Success 200 {object} model.Account "Success to fetch the account data. If the security function is disable, it returns the dummy data."
// @Failure 401 {boolean} bool "The current user haven't logged-in yet. Returns false."
// @Router /auth/loginAccount [get]
func (controller *AccountController) GetLoginAccount(c echo.Context) error {
if !controller.context.GetConfig().Extension.SecurityEnabled {
return c.JSON(http.StatusOK, controller.dummyAccount)
Expand All @@ -41,6 +57,15 @@ func (controller *AccountController) GetLoginAccount(c echo.Context) error {
}

// Login is the method to login using username and password by http post.
// @Summary Login using username and password.
// @Description Login using username and password.
// @Tags Auth
// @Accept json
// @Produce json
// @Param data body dto.LoginDto true "User name and Password for logged-in."
// @Success 200 {object} model.Account "Success to the authentication."
// @Failure 401 {boolean} bool "Failed to the authentication."
// @Router /auth/login [post]
func (controller *AccountController) Login(c echo.Context) error {
dto := dto.NewLoginDto()
if err := c.Bind(dto); err != nil {
Expand All @@ -61,6 +86,13 @@ func (controller *AccountController) Login(c echo.Context) error {
}

// Logout is the method to logout by http post.
// @Summary Logout.
// @Description Logout.
// @Tags Auth
// @Accept json
// @Produce json
// @Success 200
// @Router /auth/logout [post]
func (controller *AccountController) Logout(c echo.Context) error {
_ = session.SetAccount(c, nil)
_ = session.Delete(c)
Expand Down
8 changes: 4 additions & 4 deletions controller/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
)

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

account := NewAccountController(context)
account := NewAccountController(container)
router.GET(APIAccountLoginStatus, func(c echo.Context) error { return account.GetLoginStatus(c) })

req := httptest.NewRequest("GET", APIAccountLoginStatus, nil)
Expand All @@ -27,9 +27,9 @@ func TestGetLoginStatus(t *testing.T) {
}

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

account := NewAccountController(context)
account := NewAccountController(container)
router.GET(APIAccountLoginAccount, func(c echo.Context) error { return account.GetLoginAccount(c) })

req := httptest.NewRequest("GET", APIAccountLoginAccount, nil)
Expand Down
79 changes: 70 additions & 9 deletions controller/book.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,73 @@ import (
"net/http"

"github.com/labstack/echo/v4"
"github.com/ybkuroki/go-webapp-sample/container"
"github.com/ybkuroki/go-webapp-sample/model/dto"
"github.com/ybkuroki/go-webapp-sample/mycontext"
"github.com/ybkuroki/go-webapp-sample/service"
)

// BookController is a controller for managing books.
type BookController struct {
context mycontext.Context
service *service.BookService
container container.Container
service *service.BookService
}

// NewBookController is constructor.
func NewBookController(context mycontext.Context) *BookController {
return &BookController{context: context, service: service.NewBookService(context)}
func NewBookController(container container.Container) *BookController {
return &BookController{container: container, service: service.NewBookService(container)}
}

// GetBook returns one record matched book's id.
// @Summary Get a book
// @Description Get a book
// @Tags Books
// @Accept json
// @Produce json
// @Param book_id path int true "Book ID"
// @Success 200 {object} model.Book "Success to fetch data."
// @Failure 400 {string} message "Failed to fetch data."
// @Failure 401 {boolean} bool "Failed to the authentication. Returns false."
// @Router /books/{book_id} [get]
func (controller *BookController) GetBook(c echo.Context) error {
return c.JSON(http.StatusOK, controller.service.FindByID(c.Param("id")))
book, err := controller.service.FindByID(c.Param("id"))
if err != nil {
return c.JSON(http.StatusBadRequest, err.Error())
}
return c.JSON(http.StatusOK, book)
}

// GetBookList returns the list of matched books by searching.
// @Summary Get a book list
// @Description Get the list of matched books by searching
// @Tags Books
// @Accept json
// @Produce json
// @Param query query string false "Keyword"
// @Param page query int false "Page number"
// @Param size query int false "Item size per page"
// @Success 200 {object} model.Page "Success to fetch a book list."
// @Failure 400 {string} message "Failed to fetch data."
// @Failure 401 {boolean} bool "Failed to the authentication. Returns false."
// @Router /books [get]
func (controller *BookController) GetBookList(c echo.Context) error {
return c.JSON(http.StatusOK, controller.service.FindBooksByTitle(c.QueryParam("query"), c.QueryParam("page"), c.QueryParam("size")))
book, err := controller.service.FindBooksByTitle(c.QueryParam("query"), c.QueryParam("page"), c.QueryParam("size"))
if err != nil {
return c.JSON(http.StatusBadRequest, err.Error())
}
return c.JSON(http.StatusOK, book)
}

// CreateBook create a new book by http post.
// @Summary Create a new book
// @Description Create a new book
// @Tags Books
// @Accept json
// @Produce json
// @Param data body dto.BookDto true "a new book data for creating"
// @Success 200 {object} model.Book "Sccess to create a new book."
// @Failure 400 {string} message "Failed to the registration."
// @Failure 401 {boolean} bool "Failed to the authentication. Returns false."
// @Router /books [post]
func (controller *BookController) CreateBook(c echo.Context) error {
dto := dto.NewBookDto()
if err := c.Bind(dto); err != nil {
Expand All @@ -43,7 +83,18 @@ func (controller *BookController) CreateBook(c echo.Context) error {
return c.JSON(http.StatusOK, book)
}

// UpdateBook update the existing book by http post.
// UpdateBook update the existing book by http put.
// @Summary Update the existing book
// @Description Update the existing book
// @Tags Books
// @Accept json
// @Produce json
// @Param book_id path int true "Book ID"
// @Param data body dto.BookDto true "the book data for updating"
// @Success 200 {object} model.Book "Success to update the existing book."
// @Failure 400 {string} message "Failed to the update."
// @Failure 401 {boolean} bool "Failed to the authentication. Returns false."
// @Router /books/{book_id} [put]
func (controller *BookController) UpdateBook(c echo.Context) error {
dto := dto.NewBookDto()
if err := c.Bind(dto); err != nil {
Expand All @@ -56,7 +107,17 @@ func (controller *BookController) UpdateBook(c echo.Context) error {
return c.JSON(http.StatusOK, book)
}

// DeleteBook deletes the existing book by http post.
// DeleteBook deletes the existing book by http delete.
// @Summary Delete the existing book
// @Description Delete the existing book
// @Tags Books
// @Accept json
// @Produce json
// @Param book_id path int true "Book ID"
// @Success 200 {object} model.Book "Success to delete the existing book."
// @Failure 400 {string} message "Failed to the delete."
// @Failure 401 {boolean} bool "Failed to the authentication. Returns false."
// @Router /books/{book_id} [delete]
func (controller *BookController) DeleteBook(c echo.Context) error {
book, result := controller.service.DeleteBook(c.Param("id"))
if result != nil {
Expand Down
Loading

0 comments on commit ac72ed8

Please sign in to comment.