├── go.mod ├── domain ├── repository │ └── task.go └── model │ └── task.go ├── interface └── handler │ ├── router.go │ └── task.go ├── config └── database.go ├── Dockerfile ├── api └── main.go ├── docker-compose.yml ├── README.md ├── infra └── task.go ├── usecase └── task.go └── go.sum /go.mod: -------------------------------------------------------------------------------- 1 | module sample 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/jinzhu/gorm v1.9.12 7 | github.com/labstack/echo v3.3.10+incompatible 8 | github.com/labstack/echo/v4 v4.1.16 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /domain/repository/task.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "sample/domain/model" 5 | ) 6 | 7 | // TaskRepository task repositoryのinterface 8 | type TaskRepository interface { 9 | Create(task *model.Task) (*model.Task, error) 10 | FindByID(id int) (*model.Task, error) 11 | Update(task *model.Task) (*model.Task, error) 12 | Delete(task *model.Task) error 13 | } 14 | -------------------------------------------------------------------------------- /interface/handler/router.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/labstack/echo" 5 | ) 6 | 7 | // InitRouting routesの初期化 8 | func InitRouting(e *echo.Echo, taskHandler TaskHandler) { 9 | 10 | e.POST("/task", taskHandler.Post()) 11 | e.GET("/task/:id", taskHandler.Get()) 12 | e.PUT("/task/:id", taskHandler.Put()) 13 | e.DELETE("/task/:id", taskHandler.Delete()) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /config/database.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "sample/domain/model" 5 | 6 | "github.com/jinzhu/gorm" 7 | ) 8 | 9 | // NewDB DBと接続する 10 | func NewDB() *gorm.DB { 11 | db, err := gorm.Open("mysql", "user:password@tcp(sample_db)/sample?charset=utf8mb4&parseTime=True&loc=Local") 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | db.AutoMigrate(model.Task{}) 17 | 18 | return db 19 | } 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.13.11-alpine3.11 AS build 2 | 3 | ENV GO111MODULE=on 4 | 5 | WORKDIR / 6 | 7 | COPY . /go/src/github.com/ryokky59/go-layered-architecture-sample 8 | 9 | RUN apk update && apk add --no-cache git 10 | RUN cd /go/src/github.com/ryokky59/go-layered-architecture-sample/api && go build -o bin/sample main.go 11 | 12 | FROM alpine:3.8 13 | 14 | COPY --from=build /go/src/github.com/ryokky59/go-layered-architecture-sample/api/bin/sample /usr/local/bin/ 15 | 16 | CMD ["sample"] 17 | -------------------------------------------------------------------------------- /api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sample/config" 5 | "sample/infra" 6 | "sample/interface/handler" 7 | "sample/usecase" 8 | 9 | _ "github.com/jinzhu/gorm/dialects/mysql" 10 | "github.com/labstack/echo" 11 | ) 12 | 13 | func main() { 14 | taskRepository := infra.NewTaskRepository(config.NewDB()) 15 | taskUsecase := usecase.NewTaskUsecase(taskRepository) 16 | taskHandler := handler.NewTaskHandler(taskUsecase) 17 | 18 | e := echo.New() 19 | handler.InitRouting(e, taskHandler) 20 | e.Logger.Fatal(e.Start(":8080")) 21 | } 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | app: 5 | build: . 6 | ports: 7 | - 8080:8080 8 | container_name: sample_app 9 | volumes: 10 | - ./:/go 11 | tty: true 12 | restart: always 13 | depends_on: 14 | - mysql 15 | 16 | mysql: 17 | image: mysql:5.7 18 | container_name: sample_db 19 | command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci 20 | environment: 21 | MYSQL_DATABASE: sample 22 | MYSQL_USER: user 23 | MYSQL_PASSWORD: password 24 | MYSQL_ROOT_PASSWORD: password 25 | ports: 26 | - "3306:3306" 27 | -------------------------------------------------------------------------------- /domain/model/task.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Task taskの構造体 8 | type Task struct { 9 | ID int 10 | Title string 11 | Content string 12 | } 13 | 14 | // NewTask taskのコンストラクタ 15 | func NewTask(title, content string) (*Task, error) { 16 | if title == "" { 17 | return nil, errors.New("titleを入力してください") 18 | } 19 | 20 | task := &Task{ 21 | Title: title, 22 | Content: content, 23 | } 24 | 25 | return task, nil 26 | } 27 | 28 | // Set taskのセッター 29 | func (t *Task) Set(title, content string) (error) { 30 | if title == "" { 31 | return errors.New("titleを入力してください") 32 | } 33 | 34 | t.Title = title 35 | t.Content = content 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # layered-architecture-go-sample 2 | 3 | ## 概要 4 | このリポジトリは[DDDを意識しながらレイヤードアーキテクチャとGoでAPIサーバーを構築する - Qiita](https://qiita.com/ryokky59/items/6c2b35169fb6acafce15)の解説用です。 5 | 6 | dockerがインストールされていれば動かすことができます。 7 | 8 | ## Setup 9 | 10 | 1. `$ docker-compose build` 11 | 12 | 2. `$ docker-compose up` 13 | 14 | ## Example 15 | 16 | - `$ curl -X POST "http://localhost:8080/task" -H "Content-Type: application/json" -d '{"title": "ほげ", "content": "ふが"}'` 17 | 18 | - `$ curl -X GET "http://localhost:8080/task/1"` 19 | 20 | - `$ curl -X PUT "http://localhost:8080/task/1" -H "Content-Type: application/json" -d '{"title": "hoge", "content": "huga"}'` 21 | 22 | - `$ curl -X DELETE "http://localhost:8080/task/1"` 23 | -------------------------------------------------------------------------------- /infra/task.go: -------------------------------------------------------------------------------- 1 | package infra 2 | 3 | import ( 4 | "sample/domain/model" 5 | "sample/domain/repository" 6 | 7 | "github.com/jinzhu/gorm" 8 | ) 9 | 10 | type taskRepository struct { 11 | Conn *gorm.DB 12 | } 13 | 14 | // NewTaskRepository task repositoryのコンストラクタ 15 | func NewTaskRepository(conn *gorm.DB) repository.TaskRepository { 16 | return &taskRepository{Conn: conn} 17 | } 18 | 19 | // Create taskの保存 20 | func (tr *taskRepository) Create(task *model.Task) (*model.Task, error) { 21 | if err := tr.Conn.Create(&task).Error; err != nil { 22 | return nil, err 23 | } 24 | 25 | return task, nil 26 | } 27 | 28 | // FindByID taskをIDで取得 29 | func (tr *taskRepository) FindByID(id int) (*model.Task, error) { 30 | task := &model.Task{ID: id} 31 | 32 | if err := tr.Conn.First(&task).Error; err != nil { 33 | return nil, err 34 | } 35 | 36 | return task, nil 37 | } 38 | 39 | // Update taskの更新 40 | func (tr *taskRepository) Update(task *model.Task) (*model.Task, error) { 41 | if err := tr.Conn.Model(&task).Update(&task).Error; err != nil { 42 | return nil, err 43 | } 44 | 45 | return task, nil 46 | } 47 | 48 | // Delete taskの削除 49 | func (tr *taskRepository) Delete(task *model.Task) error { 50 | if err := tr.Conn.Delete(&task).Error; err != nil { 51 | return err 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /usecase/task.go: -------------------------------------------------------------------------------- 1 | package usecase 2 | 3 | import ( 4 | "sample/domain/model" 5 | "sample/domain/repository" 6 | ) 7 | 8 | // TaskUsecase task usecaseのinterface 9 | type TaskUsecase interface { 10 | Create(title, content string) (*model.Task, error) 11 | FindByID(id int) (*model.Task, error) 12 | Update(id int, title, content string) (*model.Task, error) 13 | Delete(id int) error 14 | } 15 | 16 | type taskUsecase struct { 17 | taskRepo repository.TaskRepository 18 | } 19 | 20 | // NewTaskUsecase task usecaseのコンストラクタ 21 | func NewTaskUsecase(taskRepo repository.TaskRepository) TaskUsecase { 22 | return &taskUsecase{taskRepo: taskRepo} 23 | } 24 | 25 | // Create taskを保存するときのユースケース 26 | func (tu *taskUsecase) Create(title, content string) (*model.Task, error) { 27 | task, err := model.NewTask(title, content) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | createdTask, err := tu.taskRepo.Create(task) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | return createdTask, nil 38 | } 39 | 40 | // FindByID taskをIDで取得するときのユースケース 41 | func (tu *taskUsecase) FindByID(id int) (*model.Task, error) { 42 | foundTask, err := tu.taskRepo.FindByID(id) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | return foundTask, nil 48 | } 49 | 50 | // Update taskを更新するときのユースケース 51 | func (tu *taskUsecase) Update(id int, title, content string) (*model.Task, error) { 52 | targetTask, err := tu.taskRepo.FindByID(id) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | err = targetTask.Set(title, content) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | updatedTask, err := tu.taskRepo.Update(targetTask) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | return updatedTask, nil 68 | } 69 | 70 | // Delete taskを削除するときのユースケース 71 | func (tu *taskUsecase) Delete(id int) error { 72 | task, err := tu.taskRepo.FindByID(id) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | err = tu.taskRepo.Delete(task) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | return nil 83 | } 84 | -------------------------------------------------------------------------------- /interface/handler/task.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | 7 | "sample/usecase" 8 | 9 | "github.com/labstack/echo" 10 | ) 11 | 12 | // TaskHandler task handlerのinterface 13 | type TaskHandler interface { 14 | Post() echo.HandlerFunc 15 | Get() echo.HandlerFunc 16 | Put() echo.HandlerFunc 17 | Delete() echo.HandlerFunc 18 | } 19 | 20 | type taskHandler struct { 21 | taskUsecase usecase.TaskUsecase 22 | } 23 | 24 | // NewTaskHandler task handlerのコンストラクタ 25 | func NewTaskHandler(taskUsecase usecase.TaskUsecase) TaskHandler { 26 | return &taskHandler{taskUsecase: taskUsecase} 27 | } 28 | 29 | type requestTask struct { 30 | Title string `json:"title"` 31 | Content string `json:"content"` 32 | } 33 | 34 | type responseTask struct { 35 | ID int `json:"id"` 36 | Title string `json:"title"` 37 | Content string `json:"content"` 38 | } 39 | 40 | // Post taskを保存するときのハンドラー 41 | func (th *taskHandler) Post() echo.HandlerFunc { 42 | return func(c echo.Context) error { 43 | var req requestTask 44 | if err := c.Bind(&req); err != nil { 45 | return c.JSON(http.StatusBadRequest, err.Error()) 46 | } 47 | 48 | createdTask, err := th.taskUsecase.Create(req.Title, req.Content) 49 | if err != nil { 50 | return c.JSON(http.StatusBadRequest, err.Error()) 51 | } 52 | 53 | res := responseTask{ 54 | ID: createdTask.ID, 55 | Title: createdTask.Title, 56 | Content: createdTask.Content, 57 | } 58 | 59 | return c.JSON(http.StatusCreated, res) 60 | } 61 | } 62 | 63 | // Get taskを取得するときのハンドラー 64 | func (th *taskHandler) Get() echo.HandlerFunc { 65 | return func(c echo.Context) error { 66 | id, err := strconv.Atoi((c.Param("id"))) 67 | if err != nil { 68 | return c.JSON(http.StatusBadRequest, err.Error()) 69 | } 70 | 71 | foundTask, err := th.taskUsecase.FindByID(id) 72 | if err != nil { 73 | return c.JSON(http.StatusBadRequest, err.Error()) 74 | } 75 | 76 | res := responseTask{ 77 | ID: foundTask.ID, 78 | Title: foundTask.Title, 79 | Content: foundTask.Content, 80 | } 81 | 82 | return c.JSON(http.StatusOK, res) 83 | } 84 | } 85 | 86 | // Put taskを更新するときのハンドラー 87 | func (th *taskHandler) Put() echo.HandlerFunc { 88 | return func(c echo.Context) error { 89 | id, err := strconv.Atoi(c.Param("id")) 90 | if err != nil { 91 | return c.JSON(http.StatusBadRequest, err.Error()) 92 | } 93 | 94 | var req requestTask 95 | if err := c.Bind(&req); err != nil { 96 | return c.JSON(http.StatusBadRequest, err.Error()) 97 | } 98 | 99 | updatedTask, err := th.taskUsecase.Update(id, req.Title, req.Content) 100 | if err != nil { 101 | return c.JSON(http.StatusBadRequest, err.Error()) 102 | } 103 | 104 | res := responseTask{ 105 | ID: updatedTask.ID, 106 | Title: updatedTask.Title, 107 | Content: updatedTask.Content, 108 | } 109 | 110 | return c.JSON(http.StatusOK, res) 111 | } 112 | } 113 | 114 | // Delete taskを削除するときのハンドラー 115 | func (th *taskHandler) Delete() echo.HandlerFunc { 116 | return func(c echo.Context) error { 117 | id, err := strconv.Atoi(c.Param("id")) 118 | if err != nil { 119 | return c.JSON(http.StatusBadRequest, err.Error()) 120 | } 121 | 122 | err = th.taskUsecase.Delete(id) 123 | if err != nil { 124 | return c.JSON(http.StatusBadRequest, err.Error()) 125 | } 126 | 127 | return c.NoContent(http.StatusNoContent) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= 4 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 5 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 6 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= 7 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 8 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 9 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 10 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= 11 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 12 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 13 | github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= 14 | github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= 15 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 16 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 17 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= 18 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 19 | github.com/jojoarianto/go-ddd-api v0.0.0-20190121220328-274691421aff h1:oRwzyy08Q/pbMfwoqZd33TdrMqxgeA56pT81UXHAW6Y= 20 | github.com/jojoarianto/go-ddd-api v0.0.0-20190121220328-274691421aff/go.mod h1:wC/iC5zAbZUjDbgeUDVpdbppiowoDWLz3oe/RNF+EgM= 21 | github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= 22 | github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= 23 | github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o= 24 | github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI= 25 | github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= 26 | github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 27 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= 28 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 29 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 30 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 31 | github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= 32 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 33 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 34 | github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= 35 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 36 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 37 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 38 | github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= 39 | github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 40 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 41 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 42 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 43 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 44 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 45 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 46 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 47 | github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= 48 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 49 | github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= 50 | github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 51 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 52 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 53 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 54 | golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= 55 | golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 56 | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= 57 | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 58 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 59 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= 60 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 61 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= 62 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 63 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 64 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 65 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 66 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= 67 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 68 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= 70 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 71 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 72 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 73 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 74 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 75 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 76 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= 77 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 78 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 79 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 80 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 81 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 82 | --------------------------------------------------------------------------------