├── .env.example ├── .github └── workflows │ └── go.yml ├── .gitignore ├── Dockerfile ├── README.md ├── docs ├── docs.go ├── swagger.json └── swagger.yaml ├── go.mod ├── go.sum ├── internal ├── database │ ├── SQLite.go │ ├── interface.go │ └── migrations.go ├── person │ ├── data │ │ ├── dataIface.go │ │ └── dataImplementation.go │ ├── models │ │ └── models.go │ ├── protos │ │ ├── bin │ │ │ ├── person.pb.go │ │ │ └── person_grpc.pb.go │ │ └── person.proto │ └── service.go └── server │ └── server.go ├── main.go ├── pkg └── client │ ├── client.go │ ├── connector.go │ └── http │ ├── handlers.go │ ├── responses.go │ ├── router.go │ └── server.go └── tests ├── grpc_mocks.go └── service_test.go /.env.example: -------------------------------------------------------------------------------- 1 | PORT= 2 | HTTP_PORT= 3 | SERVER_ADDRESS= 4 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | push: 8 | branches: [ "dev" ] 9 | pull_request: 10 | branches: [ "dev" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.20' 23 | 24 | - name: Build 25 | run: go build -v ./... 26 | - name: Test 27 | run: go test -v -coverpkg ./internal/person/ ./tests -cover 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with ? github.com/edmartt/http_test [no test files] 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | 23 | #env 24 | 25 | .env 26 | .envrc 27 | *.db 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19.9-alpine3.18 AS builder 2 | 3 | RUN apk add build-base 4 | 5 | WORKDIR /grpc-crud 6 | 7 | ENV go env -w GO111MODULE=on 8 | ENV CGP_ENABLED=0 9 | ENV GOOS=linux 10 | ENV GOARCH=amd64 11 | 12 | ENV PORT=8080 13 | 14 | COPY . . 15 | 16 | RUN go build ./... && go build 17 | 18 | FROM alpine:3.18 19 | 20 | RUN apk update upgrade 21 | 22 | WORKDIR /grpc-crud 23 | 24 | COPY --from=builder /grpc-crud . 25 | RUN chmod +x grpc-test 26 | 27 | EXPOSE ${PORT} 28 | 29 | CMD ["./grpc-test"] 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gRPC Test Server 2 | 3 | gRPC is an implementation of RPC (remote procedure calls) originally designed by Google. It is open source and is used for communications with client-server architecture. 4 | 5 | gRPC can use protocol buffers as an interface definition language and as a format for message exchange. 6 | 7 | If you want to know more visit: [introduction to gRPC](https://grpc.io/docs/what-is-grpc/introduction/) 8 | 9 | This small project is a simple CRUD in which gRPC is used to register people's data such as name, surname and email, containing the service code, in a separate package the gRPC client and also an HTTP client through which in a simple way and in JSON format the data can be sent to avoid complications configuring clients such as POSTMAN or Insomnia for gRPC. 10 | 11 | ## Requirements 12 | 13 | - Go 1.19+ 14 | - SQLite 15 | - http client: POSTMAN, Insomnia, cURL 16 | 17 | ### Running Locally 18 | 19 | ``` 20 | git clone https://github.com/Edmartt/grpc-crud.git 21 | ``` 22 | 23 | or ssh instead: 24 | 25 | ``` 26 | git clone git@github.com:Edmartt/grpc-crud.git 27 | ``` 28 | 29 | browse into project directory: 30 | 31 | ``` 32 | cd grpc-crud/ 33 | ``` 34 | 35 | download dependencies 36 | 37 | ``` 38 | go mod tidy 39 | ``` 40 | 41 | set environment variables following the [.env.example](https://github.com/Edmartt/grpc-test-server/blob/main/.env.example) file and run 42 | 43 | ``` 44 | go run main.go 45 | ``` 46 | 47 | because concurrency was added for http server, now just with the above command is enough for running the whole project 48 | 49 | #### Note 50 | 51 | You can check api docs in: [api docs](http://localhost:8080/api/v1/swagger/index.html) 52 | -------------------------------------------------------------------------------- /docs/docs.go: -------------------------------------------------------------------------------- 1 | // Package docs Code generated by swaggo/swag. DO NOT EDIT 2 | package docs 3 | 4 | import "github.com/swaggo/swag" 5 | 6 | const docTemplate = `{ 7 | "schemes": {{ marshal .Schemes }}, 8 | "swagger": "2.0", 9 | "info": { 10 | "description": "{{escape .Description}}", 11 | "title": "{{.Title}}", 12 | "termsOfService": "http://swagger.io/terms/", 13 | "contact": {}, 14 | "version": "{{.Version}}" 15 | }, 16 | "host": "{{.Host}}", 17 | "basePath": "{{.BasePath}}", 18 | "paths": { 19 | "/person": { 20 | "post": { 21 | "description": "This endpoint is for creating persons", 22 | "consumes": [ 23 | "application/json" 24 | ], 25 | "produces": [ 26 | "application/json" 27 | ], 28 | "tags": [ 29 | "Persons" 30 | ], 31 | "summary": "Creates new person", 32 | "parameters": [ 33 | { 34 | "description": "Creates person", 35 | "name": "person", 36 | "in": "body", 37 | "required": true, 38 | "schema": { 39 | "$ref": "#/definitions/models.Person" 40 | } 41 | } 42 | ], 43 | "responses": { 44 | "200": { 45 | "description": "OK", 46 | "schema": { 47 | "$ref": "#/definitions/main.httpResponse" 48 | } 49 | }, 50 | "400": { 51 | "description": "Bad Request", 52 | "schema": { 53 | "$ref": "#/definitions/main.httpResponse" 54 | } 55 | } 56 | } 57 | } 58 | }, 59 | "/person/{id}": { 60 | "get": { 61 | "description": "Through a get request the id is sent to gRPC client", 62 | "consumes": [ 63 | "application/json" 64 | ], 65 | "produces": [ 66 | "application/json" 67 | ], 68 | "tags": [ 69 | "Persons" 70 | ], 71 | "summary": "Get persons from DB", 72 | "parameters": [ 73 | { 74 | "type": "string", 75 | "description": "Person ID", 76 | "name": "id", 77 | "in": "path", 78 | "required": true 79 | } 80 | ], 81 | "responses": { 82 | "200": { 83 | "description": "OK", 84 | "schema": { 85 | "$ref": "#/definitions/models.Person" 86 | } 87 | }, 88 | "400": { 89 | "description": "Bad Request", 90 | "schema": { 91 | "$ref": "#/definitions/main.httpResponse" 92 | } 93 | }, 94 | "404": { 95 | "description": "Not Found", 96 | "schema": { 97 | "$ref": "#/definitions/main.httpResponse" 98 | } 99 | } 100 | } 101 | }, 102 | "delete": { 103 | "description": "This endpoint is for deleting person by ID", 104 | "consumes": [ 105 | "application/json" 106 | ], 107 | "produces": [ 108 | "application/json" 109 | ], 110 | "tags": [ 111 | "Persons" 112 | ], 113 | "summary": "Deletes person by ID", 114 | "parameters": [ 115 | { 116 | "type": "string", 117 | "description": "uuid formatted ID", 118 | "name": "id", 119 | "in": "path", 120 | "required": true 121 | } 122 | ], 123 | "responses": { 124 | "200": { 125 | "description": "OK", 126 | "schema": { 127 | "$ref": "#/definitions/main.httpResponse" 128 | } 129 | }, 130 | "400": { 131 | "description": "Bad Request", 132 | "schema": { 133 | "$ref": "#/definitions/main.httpResponse" 134 | } 135 | }, 136 | "404": { 137 | "description": "Not Found", 138 | "schema": { 139 | "$ref": "#/definitions/main.httpResponse" 140 | } 141 | } 142 | } 143 | } 144 | } 145 | }, 146 | "definitions": { 147 | "main.httpResponse": { 148 | "description": "This client handles data for sending data to gRPC client and after that to gRPC server", 149 | "type": "object", 150 | "properties": { 151 | "response": { 152 | "type": "string" 153 | } 154 | } 155 | }, 156 | "models.Person": { 157 | "type": "object", 158 | "properties": { 159 | "email": { 160 | "type": "string" 161 | }, 162 | "first_name": { 163 | "type": "string" 164 | }, 165 | "id": { 166 | "type": "string" 167 | }, 168 | "last_name": { 169 | "type": "string" 170 | } 171 | } 172 | } 173 | } 174 | }` 175 | 176 | // SwaggerInfo holds exported Swagger Info so clients can modify it 177 | var SwaggerInfo = &swag.Spec{ 178 | Version: "1.0", 179 | Host: "localhost:8080", 180 | BasePath: "/api/v1", 181 | Schemes: []string{}, 182 | Title: "HTTP Client for gRPC Client", 183 | Description: "This client handles data for sending data to gRPC client and after that to gRPC server", 184 | InfoInstanceName: "swagger", 185 | SwaggerTemplate: docTemplate, 186 | LeftDelim: "{{", 187 | RightDelim: "}}", 188 | } 189 | 190 | func init() { 191 | swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) 192 | } 193 | -------------------------------------------------------------------------------- /docs/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "description": "This client handles data for sending data to gRPC client and after that to gRPC server", 5 | "title": "HTTP Client for gRPC Client", 6 | "termsOfService": "http://swagger.io/terms/", 7 | "contact": {}, 8 | "version": "1.0" 9 | }, 10 | "host": "localhost:8080", 11 | "basePath": "/api/v1", 12 | "paths": { 13 | "/person": { 14 | "post": { 15 | "description": "This endpoint is for creating persons", 16 | "consumes": [ 17 | "application/json" 18 | ], 19 | "produces": [ 20 | "application/json" 21 | ], 22 | "tags": [ 23 | "Persons" 24 | ], 25 | "summary": "Creates new person", 26 | "parameters": [ 27 | { 28 | "description": "Creates person", 29 | "name": "person", 30 | "in": "body", 31 | "required": true, 32 | "schema": { 33 | "$ref": "#/definitions/models.Person" 34 | } 35 | } 36 | ], 37 | "responses": { 38 | "200": { 39 | "description": "OK", 40 | "schema": { 41 | "$ref": "#/definitions/main.httpResponse" 42 | } 43 | }, 44 | "400": { 45 | "description": "Bad Request", 46 | "schema": { 47 | "$ref": "#/definitions/main.httpResponse" 48 | } 49 | } 50 | } 51 | } 52 | }, 53 | "/person/{id}": { 54 | "get": { 55 | "description": "Through a get request the id is sent to gRPC client", 56 | "consumes": [ 57 | "application/json" 58 | ], 59 | "produces": [ 60 | "application/json" 61 | ], 62 | "tags": [ 63 | "Persons" 64 | ], 65 | "summary": "Get persons from DB", 66 | "parameters": [ 67 | { 68 | "type": "string", 69 | "description": "Person ID", 70 | "name": "id", 71 | "in": "path", 72 | "required": true 73 | } 74 | ], 75 | "responses": { 76 | "200": { 77 | "description": "OK", 78 | "schema": { 79 | "$ref": "#/definitions/models.Person" 80 | } 81 | }, 82 | "400": { 83 | "description": "Bad Request", 84 | "schema": { 85 | "$ref": "#/definitions/main.httpResponse" 86 | } 87 | }, 88 | "404": { 89 | "description": "Not Found", 90 | "schema": { 91 | "$ref": "#/definitions/main.httpResponse" 92 | } 93 | } 94 | } 95 | }, 96 | "delete": { 97 | "description": "This endpoint is for deleting person by ID", 98 | "consumes": [ 99 | "application/json" 100 | ], 101 | "produces": [ 102 | "application/json" 103 | ], 104 | "tags": [ 105 | "Persons" 106 | ], 107 | "summary": "Deletes person by ID", 108 | "parameters": [ 109 | { 110 | "type": "string", 111 | "description": "uuid formatted ID", 112 | "name": "id", 113 | "in": "path", 114 | "required": true 115 | } 116 | ], 117 | "responses": { 118 | "200": { 119 | "description": "OK", 120 | "schema": { 121 | "$ref": "#/definitions/main.httpResponse" 122 | } 123 | }, 124 | "400": { 125 | "description": "Bad Request", 126 | "schema": { 127 | "$ref": "#/definitions/main.httpResponse" 128 | } 129 | }, 130 | "404": { 131 | "description": "Not Found", 132 | "schema": { 133 | "$ref": "#/definitions/main.httpResponse" 134 | } 135 | } 136 | } 137 | } 138 | } 139 | }, 140 | "definitions": { 141 | "main.httpResponse": { 142 | "description": "This client handles data for sending data to gRPC client and after that to gRPC server", 143 | "type": "object", 144 | "properties": { 145 | "response": { 146 | "type": "string" 147 | } 148 | } 149 | }, 150 | "models.Person": { 151 | "type": "object", 152 | "properties": { 153 | "email": { 154 | "type": "string" 155 | }, 156 | "first_name": { 157 | "type": "string" 158 | }, 159 | "id": { 160 | "type": "string" 161 | }, 162 | "last_name": { 163 | "type": "string" 164 | } 165 | } 166 | } 167 | } 168 | } -------------------------------------------------------------------------------- /docs/swagger.yaml: -------------------------------------------------------------------------------- 1 | basePath: /api/v1 2 | definitions: 3 | main.httpResponse: 4 | description: This client handles data for sending data to gRPC client and after 5 | that to gRPC server 6 | properties: 7 | response: 8 | type: string 9 | type: object 10 | models.Person: 11 | properties: 12 | email: 13 | type: string 14 | first_name: 15 | type: string 16 | id: 17 | type: string 18 | last_name: 19 | type: string 20 | type: object 21 | host: localhost:8080 22 | info: 23 | contact: {} 24 | description: This client handles data for sending data to gRPC client and after 25 | that to gRPC server 26 | termsOfService: http://swagger.io/terms/ 27 | title: HTTP Client for gRPC Client 28 | version: "1.0" 29 | paths: 30 | /person: 31 | post: 32 | consumes: 33 | - application/json 34 | description: This endpoint is for creating persons 35 | parameters: 36 | - description: Creates person 37 | in: body 38 | name: person 39 | required: true 40 | schema: 41 | $ref: '#/definitions/models.Person' 42 | produces: 43 | - application/json 44 | responses: 45 | "200": 46 | description: OK 47 | schema: 48 | $ref: '#/definitions/main.httpResponse' 49 | "400": 50 | description: Bad Request 51 | schema: 52 | $ref: '#/definitions/main.httpResponse' 53 | summary: Creates new person 54 | tags: 55 | - Persons 56 | /person/{id}: 57 | delete: 58 | consumes: 59 | - application/json 60 | description: This endpoint is for deleting person by ID 61 | parameters: 62 | - description: uuid formatted ID 63 | in: path 64 | name: id 65 | required: true 66 | type: string 67 | produces: 68 | - application/json 69 | responses: 70 | "200": 71 | description: OK 72 | schema: 73 | $ref: '#/definitions/main.httpResponse' 74 | "400": 75 | description: Bad Request 76 | schema: 77 | $ref: '#/definitions/main.httpResponse' 78 | "404": 79 | description: Not Found 80 | schema: 81 | $ref: '#/definitions/main.httpResponse' 82 | summary: Deletes person by ID 83 | tags: 84 | - Persons 85 | get: 86 | consumes: 87 | - application/json 88 | description: Through a get request the id is sent to gRPC client 89 | parameters: 90 | - description: Person ID 91 | in: path 92 | name: id 93 | required: true 94 | type: string 95 | produces: 96 | - application/json 97 | responses: 98 | "200": 99 | description: OK 100 | schema: 101 | $ref: '#/definitions/models.Person' 102 | "400": 103 | description: Bad Request 104 | schema: 105 | $ref: '#/definitions/main.httpResponse' 106 | "404": 107 | description: Not Found 108 | schema: 109 | $ref: '#/definitions/main.httpResponse' 110 | summary: Get persons from DB 111 | tags: 112 | - Persons 113 | swagger: "2.0" 114 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/edmartt/grpc-test 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.9.1 7 | github.com/google/uuid v1.4.0 8 | github.com/joho/godotenv v1.5.1 9 | github.com/swaggo/files v1.0.1 10 | github.com/swaggo/gin-swagger v1.6.0 11 | github.com/swaggo/swag v1.16.2 12 | google.golang.org/grpc v1.59.0 13 | google.golang.org/protobuf v1.33.0 14 | gorm.io/driver/sqlite v1.5.4 15 | gorm.io/gorm v1.25.5 16 | ) 17 | 18 | require ( 19 | github.com/KyleBanks/depth v1.2.1 // indirect 20 | github.com/bytedance/sonic v1.9.1 // indirect 21 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 22 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 23 | github.com/gin-contrib/sse v0.1.0 // indirect 24 | github.com/go-openapi/jsonpointer v0.20.0 // indirect 25 | github.com/go-openapi/jsonreference v0.20.2 // indirect 26 | github.com/go-openapi/spec v0.20.9 // indirect 27 | github.com/go-openapi/swag v0.22.4 // indirect 28 | github.com/go-playground/locales v0.14.1 // indirect 29 | github.com/go-playground/universal-translator v0.18.1 // indirect 30 | github.com/go-playground/validator/v10 v10.14.0 // indirect 31 | github.com/goccy/go-json v0.10.2 // indirect 32 | github.com/golang/protobuf v1.5.3 // indirect 33 | github.com/jinzhu/inflection v1.0.0 // indirect 34 | github.com/jinzhu/now v1.1.5 // indirect 35 | github.com/josharian/intern v1.0.0 // indirect 36 | github.com/json-iterator/go v1.1.12 // indirect 37 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 38 | github.com/leodido/go-urn v1.2.4 // indirect 39 | github.com/mailru/easyjson v0.7.7 // indirect 40 | github.com/mattn/go-isatty v0.0.19 // indirect 41 | github.com/mattn/go-sqlite3 v1.14.17 // indirect 42 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 43 | github.com/modern-go/reflect2 v1.0.2 // indirect 44 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 45 | github.com/rogpeppe/go-internal v1.12.0 // indirect 46 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 47 | github.com/ugorji/go/codec v1.2.11 // indirect 48 | golang.org/x/arch v0.3.0 // indirect 49 | golang.org/x/crypto v0.21.0 // indirect 50 | golang.org/x/net v0.23.0 // indirect 51 | golang.org/x/sys v0.18.0 // indirect 52 | golang.org/x/text v0.14.0 // indirect 53 | golang.org/x/tools v0.15.0 // indirect 54 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 55 | gopkg.in/yaml.v3 v3.0.1 // indirect 56 | ) 57 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= 2 | github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 3 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 4 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 5 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 6 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 7 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 8 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 9 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 12 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 14 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 15 | github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= 16 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 17 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 18 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 19 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 20 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 21 | github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 22 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 23 | github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= 24 | github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= 25 | github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= 26 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 27 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 28 | github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= 29 | github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= 30 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 31 | github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 32 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 33 | github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= 34 | github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 35 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 36 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 37 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 38 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 39 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 40 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 41 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 42 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 43 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 44 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 45 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 46 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 47 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 48 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 49 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 50 | github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= 51 | github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 52 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 53 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 54 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 55 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 56 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 57 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 58 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 59 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 60 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 61 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 62 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 63 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 64 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 65 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 66 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 67 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 68 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 69 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 70 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 71 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 72 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 73 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 74 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 75 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 76 | github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 77 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 78 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 79 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 80 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 81 | github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= 82 | github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 83 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 84 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 85 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 86 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 87 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 88 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 89 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 90 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 91 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 92 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 93 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 94 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 95 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 96 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 97 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 98 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 99 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 100 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 101 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 102 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 103 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 104 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 105 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 106 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 107 | github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= 108 | github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= 109 | github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= 110 | github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= 111 | github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= 112 | github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= 113 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 114 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 115 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 116 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 117 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 118 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 119 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 120 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 121 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 122 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 123 | golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= 124 | golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= 125 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 126 | golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= 127 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 128 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 129 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 130 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 131 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 132 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 133 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 134 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 135 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 136 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 137 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 138 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 139 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 140 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 141 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 142 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 143 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 144 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 145 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 146 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 147 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 148 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 149 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 150 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 151 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 152 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 153 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 154 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 155 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 156 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 157 | golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= 158 | golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= 159 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 160 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 161 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 162 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 163 | google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= 164 | google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 165 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 166 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 167 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 168 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 169 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 170 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 171 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 172 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 173 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 174 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 175 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 176 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 177 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 178 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 179 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 180 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 181 | gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0= 182 | gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= 183 | gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= 184 | gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 185 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 186 | -------------------------------------------------------------------------------- /internal/database/SQLite.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "log" 5 | 6 | db "gorm.io/driver/sqlite" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type SQLiteDB struct { 11 | } 12 | 13 | func (sqlite SQLiteDB) GetConnection() (*gorm.DB, error) { 14 | connection, conError := gorm.Open(db.Open("data.db")) 15 | 16 | if conError != nil { 17 | log.Fatal("error direct db: ", conError) 18 | return nil, conError 19 | } 20 | return connection, nil 21 | } 22 | -------------------------------------------------------------------------------- /internal/database/interface.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import "gorm.io/gorm" 4 | 5 | type IConnection interface { 6 | GetConnection() (*gorm.DB, error) 7 | } 8 | -------------------------------------------------------------------------------- /internal/database/migrations.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/edmartt/grpc-test/internal/person/models" 7 | ) 8 | 9 | type Migrations struct { 10 | DB IConnection 11 | } 12 | 13 | func (m Migrations) MigrateData() { 14 | connection, conErr := m.DB.GetConnection() 15 | 16 | if conErr != nil { 17 | log.Println("Connection Error Migrations: ", conErr.Error()) 18 | } 19 | 20 | connection.AutoMigrate(&models.Person{}) 21 | } 22 | 23 | func InitMigrations() { 24 | migrations := Migrations{ 25 | DB: SQLiteDB{}, 26 | } 27 | 28 | migrations.MigrateData() 29 | } 30 | -------------------------------------------------------------------------------- /internal/person/data/dataIface.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "github.com/edmartt/grpc-test/internal/person/models" 5 | ) 6 | 7 | var dataAccessIface IUserDataAccess 8 | 9 | type IUserDataAccess interface { 10 | Create(person models.Person) string 11 | Read(id string) (*models.Person, error) 12 | Delete(person *models.Person) (*models.Person, error) 13 | } 14 | -------------------------------------------------------------------------------- /internal/person/data/dataImplementation.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "github.com/edmartt/grpc-test/internal/database" 5 | "github.com/edmartt/grpc-test/internal/person/models" 6 | ) 7 | 8 | var dBConnectionObject database.IConnection 9 | 10 | type UserDataAccess struct { 11 | DB database.IConnection 12 | person models.Person 13 | } 14 | 15 | func init() { 16 | dBConnectionObject = database.SQLiteDB{} 17 | } 18 | 19 | func (u UserDataAccess) Create(person models.Person) string { 20 | connection, err := dBConnectionObject.GetConnection() 21 | 22 | if err != nil { 23 | return "DB ERROR: " + err.Error() 24 | } 25 | 26 | connection.Create(&person) 27 | 28 | return "created" 29 | } 30 | 31 | func (u UserDataAccess) Read(id string) (*models.Person, error) { 32 | connection, err := dBConnectionObject.GetConnection() 33 | 34 | person := models.Person{} 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | connection.First(&person, "id = ?", id) 40 | 41 | return &person, nil 42 | } 43 | 44 | func (u UserDataAccess) Delete(person *models.Person) (*models.Person, error) { 45 | connection, err := dBConnectionObject.GetConnection() 46 | 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | connection.Delete(person) 52 | 53 | return &models.Person{}, nil 54 | } 55 | -------------------------------------------------------------------------------- /internal/person/models/models.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Person struct { 4 | ID string `json:"id,omitempty"` 5 | FirstName string `json:"first_name"` 6 | LastName string `json:"last_name"` 7 | Email string `json:"email"` 8 | } 9 | -------------------------------------------------------------------------------- /internal/person/protos/bin/person.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.28.1 4 | // protoc v3.19.6 5 | // source: internal/person/protos/person.proto 6 | 7 | package bin 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type Person struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 29 | FirstName string `protobuf:"bytes,2,opt,name=firstName,proto3" json:"firstName,omitempty"` 30 | LastName string `protobuf:"bytes,3,opt,name=lastName,proto3" json:"lastName,omitempty"` 31 | Email string `protobuf:"bytes,4,opt,name=email,proto3" json:"email,omitempty"` 32 | } 33 | 34 | func (x *Person) Reset() { 35 | *x = Person{} 36 | if protoimpl.UnsafeEnabled { 37 | mi := &file_internal_person_protos_person_proto_msgTypes[0] 38 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 39 | ms.StoreMessageInfo(mi) 40 | } 41 | } 42 | 43 | func (x *Person) String() string { 44 | return protoimpl.X.MessageStringOf(x) 45 | } 46 | 47 | func (*Person) ProtoMessage() {} 48 | 49 | func (x *Person) ProtoReflect() protoreflect.Message { 50 | mi := &file_internal_person_protos_person_proto_msgTypes[0] 51 | if protoimpl.UnsafeEnabled && x != nil { 52 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 53 | if ms.LoadMessageInfo() == nil { 54 | ms.StoreMessageInfo(mi) 55 | } 56 | return ms 57 | } 58 | return mi.MessageOf(x) 59 | } 60 | 61 | // Deprecated: Use Person.ProtoReflect.Descriptor instead. 62 | func (*Person) Descriptor() ([]byte, []int) { 63 | return file_internal_person_protos_person_proto_rawDescGZIP(), []int{0} 64 | } 65 | 66 | func (x *Person) GetId() string { 67 | if x != nil { 68 | return x.Id 69 | } 70 | return "" 71 | } 72 | 73 | func (x *Person) GetFirstName() string { 74 | if x != nil { 75 | return x.FirstName 76 | } 77 | return "" 78 | } 79 | 80 | func (x *Person) GetLastName() string { 81 | if x != nil { 82 | return x.LastName 83 | } 84 | return "" 85 | } 86 | 87 | func (x *Person) GetEmail() string { 88 | if x != nil { 89 | return x.Email 90 | } 91 | return "" 92 | } 93 | 94 | type CreatePersonResponse struct { 95 | state protoimpl.MessageState 96 | sizeCache protoimpl.SizeCache 97 | unknownFields protoimpl.UnknownFields 98 | 99 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 100 | Response string `protobuf:"bytes,2,opt,name=response,proto3" json:"response,omitempty"` 101 | } 102 | 103 | func (x *CreatePersonResponse) Reset() { 104 | *x = CreatePersonResponse{} 105 | if protoimpl.UnsafeEnabled { 106 | mi := &file_internal_person_protos_person_proto_msgTypes[1] 107 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 108 | ms.StoreMessageInfo(mi) 109 | } 110 | } 111 | 112 | func (x *CreatePersonResponse) String() string { 113 | return protoimpl.X.MessageStringOf(x) 114 | } 115 | 116 | func (*CreatePersonResponse) ProtoMessage() {} 117 | 118 | func (x *CreatePersonResponse) ProtoReflect() protoreflect.Message { 119 | mi := &file_internal_person_protos_person_proto_msgTypes[1] 120 | if protoimpl.UnsafeEnabled && x != nil { 121 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 122 | if ms.LoadMessageInfo() == nil { 123 | ms.StoreMessageInfo(mi) 124 | } 125 | return ms 126 | } 127 | return mi.MessageOf(x) 128 | } 129 | 130 | // Deprecated: Use CreatePersonResponse.ProtoReflect.Descriptor instead. 131 | func (*CreatePersonResponse) Descriptor() ([]byte, []int) { 132 | return file_internal_person_protos_person_proto_rawDescGZIP(), []int{1} 133 | } 134 | 135 | func (x *CreatePersonResponse) GetId() string { 136 | if x != nil { 137 | return x.Id 138 | } 139 | return "" 140 | } 141 | 142 | func (x *CreatePersonResponse) GetResponse() string { 143 | if x != nil { 144 | return x.Response 145 | } 146 | return "" 147 | } 148 | 149 | type GetPersonRequest struct { 150 | state protoimpl.MessageState 151 | sizeCache protoimpl.SizeCache 152 | unknownFields protoimpl.UnknownFields 153 | 154 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 155 | } 156 | 157 | func (x *GetPersonRequest) Reset() { 158 | *x = GetPersonRequest{} 159 | if protoimpl.UnsafeEnabled { 160 | mi := &file_internal_person_protos_person_proto_msgTypes[2] 161 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 162 | ms.StoreMessageInfo(mi) 163 | } 164 | } 165 | 166 | func (x *GetPersonRequest) String() string { 167 | return protoimpl.X.MessageStringOf(x) 168 | } 169 | 170 | func (*GetPersonRequest) ProtoMessage() {} 171 | 172 | func (x *GetPersonRequest) ProtoReflect() protoreflect.Message { 173 | mi := &file_internal_person_protos_person_proto_msgTypes[2] 174 | if protoimpl.UnsafeEnabled && x != nil { 175 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 176 | if ms.LoadMessageInfo() == nil { 177 | ms.StoreMessageInfo(mi) 178 | } 179 | return ms 180 | } 181 | return mi.MessageOf(x) 182 | } 183 | 184 | // Deprecated: Use GetPersonRequest.ProtoReflect.Descriptor instead. 185 | func (*GetPersonRequest) Descriptor() ([]byte, []int) { 186 | return file_internal_person_protos_person_proto_rawDescGZIP(), []int{2} 187 | } 188 | 189 | func (x *GetPersonRequest) GetId() string { 190 | if x != nil { 191 | return x.Id 192 | } 193 | return "" 194 | } 195 | 196 | type GetPersonResponse struct { 197 | state protoimpl.MessageState 198 | sizeCache protoimpl.SizeCache 199 | unknownFields protoimpl.UnknownFields 200 | 201 | Person *Person `protobuf:"bytes,1,opt,name=person,proto3" json:"person,omitempty"` 202 | } 203 | 204 | func (x *GetPersonResponse) Reset() { 205 | *x = GetPersonResponse{} 206 | if protoimpl.UnsafeEnabled { 207 | mi := &file_internal_person_protos_person_proto_msgTypes[3] 208 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 209 | ms.StoreMessageInfo(mi) 210 | } 211 | } 212 | 213 | func (x *GetPersonResponse) String() string { 214 | return protoimpl.X.MessageStringOf(x) 215 | } 216 | 217 | func (*GetPersonResponse) ProtoMessage() {} 218 | 219 | func (x *GetPersonResponse) ProtoReflect() protoreflect.Message { 220 | mi := &file_internal_person_protos_person_proto_msgTypes[3] 221 | if protoimpl.UnsafeEnabled && x != nil { 222 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 223 | if ms.LoadMessageInfo() == nil { 224 | ms.StoreMessageInfo(mi) 225 | } 226 | return ms 227 | } 228 | return mi.MessageOf(x) 229 | } 230 | 231 | // Deprecated: Use GetPersonResponse.ProtoReflect.Descriptor instead. 232 | func (*GetPersonResponse) Descriptor() ([]byte, []int) { 233 | return file_internal_person_protos_person_proto_rawDescGZIP(), []int{3} 234 | } 235 | 236 | func (x *GetPersonResponse) GetPerson() *Person { 237 | if x != nil { 238 | return x.Person 239 | } 240 | return nil 241 | } 242 | 243 | type DeletePersonRequest struct { 244 | state protoimpl.MessageState 245 | sizeCache protoimpl.SizeCache 246 | unknownFields protoimpl.UnknownFields 247 | 248 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 249 | } 250 | 251 | func (x *DeletePersonRequest) Reset() { 252 | *x = DeletePersonRequest{} 253 | if protoimpl.UnsafeEnabled { 254 | mi := &file_internal_person_protos_person_proto_msgTypes[4] 255 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 256 | ms.StoreMessageInfo(mi) 257 | } 258 | } 259 | 260 | func (x *DeletePersonRequest) String() string { 261 | return protoimpl.X.MessageStringOf(x) 262 | } 263 | 264 | func (*DeletePersonRequest) ProtoMessage() {} 265 | 266 | func (x *DeletePersonRequest) ProtoReflect() protoreflect.Message { 267 | mi := &file_internal_person_protos_person_proto_msgTypes[4] 268 | if protoimpl.UnsafeEnabled && x != nil { 269 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 270 | if ms.LoadMessageInfo() == nil { 271 | ms.StoreMessageInfo(mi) 272 | } 273 | return ms 274 | } 275 | return mi.MessageOf(x) 276 | } 277 | 278 | // Deprecated: Use DeletePersonRequest.ProtoReflect.Descriptor instead. 279 | func (*DeletePersonRequest) Descriptor() ([]byte, []int) { 280 | return file_internal_person_protos_person_proto_rawDescGZIP(), []int{4} 281 | } 282 | 283 | func (x *DeletePersonRequest) GetId() string { 284 | if x != nil { 285 | return x.Id 286 | } 287 | return "" 288 | } 289 | 290 | type DeletePersonResponse struct { 291 | state protoimpl.MessageState 292 | sizeCache protoimpl.SizeCache 293 | unknownFields protoimpl.UnknownFields 294 | 295 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 296 | Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` 297 | } 298 | 299 | func (x *DeletePersonResponse) Reset() { 300 | *x = DeletePersonResponse{} 301 | if protoimpl.UnsafeEnabled { 302 | mi := &file_internal_person_protos_person_proto_msgTypes[5] 303 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 304 | ms.StoreMessageInfo(mi) 305 | } 306 | } 307 | 308 | func (x *DeletePersonResponse) String() string { 309 | return protoimpl.X.MessageStringOf(x) 310 | } 311 | 312 | func (*DeletePersonResponse) ProtoMessage() {} 313 | 314 | func (x *DeletePersonResponse) ProtoReflect() protoreflect.Message { 315 | mi := &file_internal_person_protos_person_proto_msgTypes[5] 316 | if protoimpl.UnsafeEnabled && x != nil { 317 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 318 | if ms.LoadMessageInfo() == nil { 319 | ms.StoreMessageInfo(mi) 320 | } 321 | return ms 322 | } 323 | return mi.MessageOf(x) 324 | } 325 | 326 | // Deprecated: Use DeletePersonResponse.ProtoReflect.Descriptor instead. 327 | func (*DeletePersonResponse) Descriptor() ([]byte, []int) { 328 | return file_internal_person_protos_person_proto_rawDescGZIP(), []int{5} 329 | } 330 | 331 | func (x *DeletePersonResponse) GetId() string { 332 | if x != nil { 333 | return x.Id 334 | } 335 | return "" 336 | } 337 | 338 | func (x *DeletePersonResponse) GetStatus() string { 339 | if x != nil { 340 | return x.Status 341 | } 342 | return "" 343 | } 344 | 345 | var File_internal_person_protos_person_proto protoreflect.FileDescriptor 346 | 347 | var file_internal_person_protos_person_proto_rawDesc = []byte{ 348 | 0x0a, 0x23, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x65, 0x72, 0x73, 0x6f, 349 | 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x2e, 350 | 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x68, 0x0a, 351 | 0x06, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 352 | 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x72, 0x73, 0x74, 353 | 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, 0x69, 0x72, 0x73, 354 | 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x4e, 0x61, 0x6d, 355 | 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x4e, 0x61, 0x6d, 356 | 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 357 | 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x42, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 358 | 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 359 | 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 360 | 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 361 | 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x0a, 0x10, 0x47, 362 | 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 363 | 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 364 | 0x3b, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 365 | 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x18, 0x01, 366 | 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x65, 367 | 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x22, 0x25, 0x0a, 0x13, 368 | 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 369 | 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 370 | 0x02, 0x69, 0x64, 0x22, 0x3e, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72, 371 | 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 372 | 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 373 | 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 374 | 0x74, 0x75, 0x73, 0x32, 0xce, 0x01, 0x0a, 0x0d, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x53, 0x65, 375 | 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 376 | 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x1a, 377 | 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 378 | 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 379 | 0x3c, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 380 | 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 381 | 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 382 | 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 383 | 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 384 | 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x71, 385 | 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x44, 0x65, 386 | 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 387 | 0x73, 0x65, 0x22, 0x00, 0x42, 0x0f, 0x5a, 0x0d, 0x2e, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 388 | 0x73, 0x2f, 0x62, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 389 | } 390 | 391 | var ( 392 | file_internal_person_protos_person_proto_rawDescOnce sync.Once 393 | file_internal_person_protos_person_proto_rawDescData = file_internal_person_protos_person_proto_rawDesc 394 | ) 395 | 396 | func file_internal_person_protos_person_proto_rawDescGZIP() []byte { 397 | file_internal_person_protos_person_proto_rawDescOnce.Do(func() { 398 | file_internal_person_protos_person_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_person_protos_person_proto_rawDescData) 399 | }) 400 | return file_internal_person_protos_person_proto_rawDescData 401 | } 402 | 403 | var file_internal_person_protos_person_proto_msgTypes = make([]protoimpl.MessageInfo, 6) 404 | var file_internal_person_protos_person_proto_goTypes = []interface{}{ 405 | (*Person)(nil), // 0: protos.Person 406 | (*CreatePersonResponse)(nil), // 1: protos.CreatePersonResponse 407 | (*GetPersonRequest)(nil), // 2: protos.GetPersonRequest 408 | (*GetPersonResponse)(nil), // 3: protos.GetPersonResponse 409 | (*DeletePersonRequest)(nil), // 4: protos.DeletePersonRequest 410 | (*DeletePersonResponse)(nil), // 5: protos.DeletePersonResponse 411 | } 412 | var file_internal_person_protos_person_proto_depIdxs = []int32{ 413 | 0, // 0: protos.GetPersonResponse.person:type_name -> protos.Person 414 | 0, // 1: protos.PersonService.Create:input_type -> protos.Person 415 | 2, // 2: protos.PersonService.Get:input_type -> protos.GetPersonRequest 416 | 4, // 3: protos.PersonService.Delete:input_type -> protos.DeletePersonRequest 417 | 1, // 4: protos.PersonService.Create:output_type -> protos.CreatePersonResponse 418 | 3, // 5: protos.PersonService.Get:output_type -> protos.GetPersonResponse 419 | 5, // 6: protos.PersonService.Delete:output_type -> protos.DeletePersonResponse 420 | 4, // [4:7] is the sub-list for method output_type 421 | 1, // [1:4] is the sub-list for method input_type 422 | 1, // [1:1] is the sub-list for extension type_name 423 | 1, // [1:1] is the sub-list for extension extendee 424 | 0, // [0:1] is the sub-list for field type_name 425 | } 426 | 427 | func init() { file_internal_person_protos_person_proto_init() } 428 | func file_internal_person_protos_person_proto_init() { 429 | if File_internal_person_protos_person_proto != nil { 430 | return 431 | } 432 | if !protoimpl.UnsafeEnabled { 433 | file_internal_person_protos_person_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 434 | switch v := v.(*Person); i { 435 | case 0: 436 | return &v.state 437 | case 1: 438 | return &v.sizeCache 439 | case 2: 440 | return &v.unknownFields 441 | default: 442 | return nil 443 | } 444 | } 445 | file_internal_person_protos_person_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 446 | switch v := v.(*CreatePersonResponse); i { 447 | case 0: 448 | return &v.state 449 | case 1: 450 | return &v.sizeCache 451 | case 2: 452 | return &v.unknownFields 453 | default: 454 | return nil 455 | } 456 | } 457 | file_internal_person_protos_person_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 458 | switch v := v.(*GetPersonRequest); i { 459 | case 0: 460 | return &v.state 461 | case 1: 462 | return &v.sizeCache 463 | case 2: 464 | return &v.unknownFields 465 | default: 466 | return nil 467 | } 468 | } 469 | file_internal_person_protos_person_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { 470 | switch v := v.(*GetPersonResponse); i { 471 | case 0: 472 | return &v.state 473 | case 1: 474 | return &v.sizeCache 475 | case 2: 476 | return &v.unknownFields 477 | default: 478 | return nil 479 | } 480 | } 481 | file_internal_person_protos_person_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { 482 | switch v := v.(*DeletePersonRequest); i { 483 | case 0: 484 | return &v.state 485 | case 1: 486 | return &v.sizeCache 487 | case 2: 488 | return &v.unknownFields 489 | default: 490 | return nil 491 | } 492 | } 493 | file_internal_person_protos_person_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { 494 | switch v := v.(*DeletePersonResponse); i { 495 | case 0: 496 | return &v.state 497 | case 1: 498 | return &v.sizeCache 499 | case 2: 500 | return &v.unknownFields 501 | default: 502 | return nil 503 | } 504 | } 505 | } 506 | type x struct{} 507 | out := protoimpl.TypeBuilder{ 508 | File: protoimpl.DescBuilder{ 509 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 510 | RawDescriptor: file_internal_person_protos_person_proto_rawDesc, 511 | NumEnums: 0, 512 | NumMessages: 6, 513 | NumExtensions: 0, 514 | NumServices: 1, 515 | }, 516 | GoTypes: file_internal_person_protos_person_proto_goTypes, 517 | DependencyIndexes: file_internal_person_protos_person_proto_depIdxs, 518 | MessageInfos: file_internal_person_protos_person_proto_msgTypes, 519 | }.Build() 520 | File_internal_person_protos_person_proto = out.File 521 | file_internal_person_protos_person_proto_rawDesc = nil 522 | file_internal_person_protos_person_proto_goTypes = nil 523 | file_internal_person_protos_person_proto_depIdxs = nil 524 | } 525 | -------------------------------------------------------------------------------- /internal/person/protos/bin/person_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.2.0 4 | // - protoc v3.19.6 5 | // source: internal/person/protos/person.proto 6 | 7 | package bin 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | // PersonServiceClient is the client API for PersonService service. 22 | // 23 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 24 | type PersonServiceClient interface { 25 | Create(ctx context.Context, in *Person, opts ...grpc.CallOption) (*CreatePersonResponse, error) 26 | Get(ctx context.Context, in *GetPersonRequest, opts ...grpc.CallOption) (*GetPersonResponse, error) 27 | Delete(ctx context.Context, in *DeletePersonRequest, opts ...grpc.CallOption) (*DeletePersonResponse, error) 28 | } 29 | 30 | type personServiceClient struct { 31 | cc grpc.ClientConnInterface 32 | } 33 | 34 | func NewPersonServiceClient(cc grpc.ClientConnInterface) PersonServiceClient { 35 | return &personServiceClient{cc} 36 | } 37 | 38 | func (c *personServiceClient) Create(ctx context.Context, in *Person, opts ...grpc.CallOption) (*CreatePersonResponse, error) { 39 | out := new(CreatePersonResponse) 40 | err := c.cc.Invoke(ctx, "/protos.PersonService/Create", in, out, opts...) 41 | if err != nil { 42 | return nil, err 43 | } 44 | return out, nil 45 | } 46 | 47 | func (c *personServiceClient) Get(ctx context.Context, in *GetPersonRequest, opts ...grpc.CallOption) (*GetPersonResponse, error) { 48 | out := new(GetPersonResponse) 49 | err := c.cc.Invoke(ctx, "/protos.PersonService/Get", in, out, opts...) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return out, nil 54 | } 55 | 56 | func (c *personServiceClient) Delete(ctx context.Context, in *DeletePersonRequest, opts ...grpc.CallOption) (*DeletePersonResponse, error) { 57 | out := new(DeletePersonResponse) 58 | err := c.cc.Invoke(ctx, "/protos.PersonService/Delete", in, out, opts...) 59 | if err != nil { 60 | return nil, err 61 | } 62 | return out, nil 63 | } 64 | 65 | // PersonServiceServer is the server API for PersonService service. 66 | // All implementations must embed UnimplementedPersonServiceServer 67 | // for forward compatibility 68 | type PersonServiceServer interface { 69 | Create(context.Context, *Person) (*CreatePersonResponse, error) 70 | Get(context.Context, *GetPersonRequest) (*GetPersonResponse, error) 71 | Delete(context.Context, *DeletePersonRequest) (*DeletePersonResponse, error) 72 | mustEmbedUnimplementedPersonServiceServer() 73 | } 74 | 75 | // UnimplementedPersonServiceServer must be embedded to have forward compatible implementations. 76 | type UnimplementedPersonServiceServer struct { 77 | } 78 | 79 | func (UnimplementedPersonServiceServer) Create(context.Context, *Person) (*CreatePersonResponse, error) { 80 | return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") 81 | } 82 | func (UnimplementedPersonServiceServer) Get(context.Context, *GetPersonRequest) (*GetPersonResponse, error) { 83 | return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") 84 | } 85 | func (UnimplementedPersonServiceServer) Delete(context.Context, *DeletePersonRequest) (*DeletePersonResponse, error) { 86 | return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") 87 | } 88 | func (UnimplementedPersonServiceServer) mustEmbedUnimplementedPersonServiceServer() {} 89 | 90 | // UnsafePersonServiceServer may be embedded to opt out of forward compatibility for this service. 91 | // Use of this interface is not recommended, as added methods to PersonServiceServer will 92 | // result in compilation errors. 93 | type UnsafePersonServiceServer interface { 94 | mustEmbedUnimplementedPersonServiceServer() 95 | } 96 | 97 | func RegisterPersonServiceServer(s grpc.ServiceRegistrar, srv PersonServiceServer) { 98 | s.RegisterService(&PersonService_ServiceDesc, srv) 99 | } 100 | 101 | func _PersonService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 102 | in := new(Person) 103 | if err := dec(in); err != nil { 104 | return nil, err 105 | } 106 | if interceptor == nil { 107 | return srv.(PersonServiceServer).Create(ctx, in) 108 | } 109 | info := &grpc.UnaryServerInfo{ 110 | Server: srv, 111 | FullMethod: "/protos.PersonService/Create", 112 | } 113 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 114 | return srv.(PersonServiceServer).Create(ctx, req.(*Person)) 115 | } 116 | return interceptor(ctx, in, info, handler) 117 | } 118 | 119 | func _PersonService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 120 | in := new(GetPersonRequest) 121 | if err := dec(in); err != nil { 122 | return nil, err 123 | } 124 | if interceptor == nil { 125 | return srv.(PersonServiceServer).Get(ctx, in) 126 | } 127 | info := &grpc.UnaryServerInfo{ 128 | Server: srv, 129 | FullMethod: "/protos.PersonService/Get", 130 | } 131 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 132 | return srv.(PersonServiceServer).Get(ctx, req.(*GetPersonRequest)) 133 | } 134 | return interceptor(ctx, in, info, handler) 135 | } 136 | 137 | func _PersonService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 138 | in := new(DeletePersonRequest) 139 | if err := dec(in); err != nil { 140 | return nil, err 141 | } 142 | if interceptor == nil { 143 | return srv.(PersonServiceServer).Delete(ctx, in) 144 | } 145 | info := &grpc.UnaryServerInfo{ 146 | Server: srv, 147 | FullMethod: "/protos.PersonService/Delete", 148 | } 149 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 150 | return srv.(PersonServiceServer).Delete(ctx, req.(*DeletePersonRequest)) 151 | } 152 | return interceptor(ctx, in, info, handler) 153 | } 154 | 155 | // PersonService_ServiceDesc is the grpc.ServiceDesc for PersonService service. 156 | // It's only intended for direct use with grpc.RegisterService, 157 | // and not to be introspected or modified (even as a copy) 158 | var PersonService_ServiceDesc = grpc.ServiceDesc{ 159 | ServiceName: "protos.PersonService", 160 | HandlerType: (*PersonServiceServer)(nil), 161 | Methods: []grpc.MethodDesc{ 162 | { 163 | MethodName: "Create", 164 | Handler: _PersonService_Create_Handler, 165 | }, 166 | { 167 | MethodName: "Get", 168 | Handler: _PersonService_Get_Handler, 169 | }, 170 | { 171 | MethodName: "Delete", 172 | Handler: _PersonService_Delete_Handler, 173 | }, 174 | }, 175 | Streams: []grpc.StreamDesc{}, 176 | Metadata: "internal/person/protos/person.proto", 177 | } 178 | -------------------------------------------------------------------------------- /internal/person/protos/person.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package protos; 3 | option go_package = "../protos/bin"; 4 | 5 | message Person{ 6 | string id=1; 7 | string firstName = 2; 8 | string lastName = 3; 9 | string email = 4; 10 | } 11 | 12 | message CreatePersonResponse{ 13 | string id = 1; 14 | string response = 2; 15 | } 16 | 17 | message GetPersonRequest{ 18 | string id = 1; 19 | } 20 | 21 | message GetPersonResponse{ 22 | Person person = 1; 23 | } 24 | 25 | message DeletePersonRequest{ 26 | string id = 1; 27 | } 28 | 29 | message DeletePersonResponse{ 30 | string id = 1; 31 | string status = 2; 32 | } 33 | 34 | service PersonService{ 35 | 36 | rpc Create(Person) returns (CreatePersonResponse) {} 37 | rpc Get(GetPersonRequest) returns (GetPersonResponse) {} 38 | rpc Delete(DeletePersonRequest) returns (DeletePersonResponse) {} 39 | } 40 | -------------------------------------------------------------------------------- /internal/person/service.go: -------------------------------------------------------------------------------- 1 | package person 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/edmartt/grpc-test/internal/person/data" 7 | "github.com/edmartt/grpc-test/internal/person/models" 8 | pb "github.com/edmartt/grpc-test/internal/person/protos/bin" 9 | "github.com/google/uuid" 10 | ) 11 | 12 | var DataAccess data.IUserDataAccess 13 | 14 | func init() { 15 | DataAccess = data.UserDataAccess{} 16 | } 17 | 18 | type Service struct { 19 | pb.UnimplementedPersonServiceServer 20 | } 21 | 22 | func (s *Service) Create(ctx context.Context, person *pb.Person) (*pb.CreatePersonResponse, error) { 23 | id := uuid.New().String() 24 | 25 | newPerson := &models.Person{ 26 | ID: id, 27 | FirstName: person.FirstName, 28 | LastName: person.LastName, 29 | Email: person.Email, 30 | } 31 | 32 | status := DataAccess.Create(*newPerson) 33 | 34 | return &pb.CreatePersonResponse{Id: newPerson.ID, Response: status}, nil 35 | } 36 | 37 | func (s *Service) Get(ctx context.Context, request *pb.GetPersonRequest) (*pb.GetPersonResponse, error) { 38 | id := request.Id 39 | dbResponse, err := DataAccess.Read(id) 40 | 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | person := &pb.Person{ 46 | Id: dbResponse.ID, 47 | FirstName: dbResponse.FirstName, 48 | LastName: dbResponse.LastName, 49 | Email: dbResponse.Email, 50 | } 51 | 52 | return &pb.GetPersonResponse{ 53 | Person: person, 54 | }, nil 55 | } 56 | 57 | func (s *Service) Delete(ctx context.Context, request *pb.DeletePersonRequest) (*pb.DeletePersonResponse, error) { 58 | id := request.Id 59 | queryPerson, err := DataAccess.Read(id) 60 | 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | if queryPerson.ID == "" { 66 | return &pb.DeletePersonResponse{ 67 | Id: id, 68 | Status: "not found", 69 | }, nil 70 | } 71 | 72 | DataAccess.Delete(queryPerson) 73 | 74 | delPersonResponse := &pb.DeletePersonResponse{ 75 | Id: queryPerson.ID, 76 | Status: "deleted", 77 | } 78 | 79 | return delPersonResponse, nil 80 | 81 | } 82 | -------------------------------------------------------------------------------- /internal/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "os" 8 | 9 | "github.com/edmartt/grpc-test/internal/person" 10 | pb "github.com/edmartt/grpc-test/internal/person/protos/bin" 11 | "google.golang.org/grpc" 12 | ) 13 | 14 | func StartServer() { 15 | port := os.Getenv("PORT") 16 | 17 | listener, err := net.Listen("tcp", ":"+port) 18 | 19 | if err != nil { 20 | log.Println("TCP ERROR" + err.Error()) 21 | panic(err) 22 | } 23 | 24 | serve := grpc.NewServer() 25 | fmt.Println("SERVER RUNNING on: ", port) 26 | 27 | pb.RegisterPersonServiceServer(serve, &person.Service{}) 28 | 29 | if err = serve.Serve(listener); err != nil { 30 | log.Println("Server Not Started " + err.Error()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/edmartt/grpc-test/internal/database" 7 | "github.com/edmartt/grpc-test/internal/server" 8 | "github.com/edmartt/grpc-test/pkg/client/http" 9 | "github.com/joho/godotenv" 10 | ) 11 | 12 | func main() { 13 | godotenv.Load() 14 | 15 | port := os.Getenv("HTTP_PORT") 16 | go http.Start(port) 17 | 18 | database.InitMigrations() 19 | server.StartServer() 20 | } 21 | -------------------------------------------------------------------------------- /pkg/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | 6 | pb "github.com/edmartt/grpc-test/internal/person/protos/bin" 7 | ) 8 | 9 | func CreatePerson(person *pb.Person) (string, error) { 10 | connection, err := grpcConnector() 11 | 12 | if err != nil { 13 | return "", err 14 | } 15 | serviceClient := pb.NewPersonServiceClient(connection) 16 | 17 | serverResponse, err := serviceClient.Create(context.Background(), &pb.Person{ 18 | Id: person.Id, 19 | FirstName: person.FirstName, 20 | LastName: person.LastName, 21 | Email: person.Email, 22 | }) 23 | 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | response := serverResponse.Id 29 | serverResponse.Response = response 30 | 31 | return serverResponse.Response, nil 32 | } 33 | 34 | func ReadPerson(request *pb.GetPersonRequest) (*pb.GetPersonResponse, error) { 35 | connection, err := grpcConnector() 36 | 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | serviceClient := pb.NewPersonServiceClient(connection) 42 | 43 | serverResponse, err := serviceClient.Get(context.Background(), request) 44 | 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | return serverResponse, nil 50 | } 51 | 52 | func DeletePerson(request *pb.DeletePersonRequest) (*pb.DeletePersonResponse, error) { 53 | connection, err := grpcConnector() 54 | 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | serviceClient := pb.NewPersonServiceClient(connection) 60 | 61 | serverResponse, err := serviceClient.Delete(context.Background(), request) 62 | 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | return serverResponse, nil 68 | } 69 | -------------------------------------------------------------------------------- /pkg/client/connector.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | func grpcConnector() (*grpc.ClientConn, error) { 11 | serverAddress := os.Getenv("SERVER_ADDRESS") 12 | conn, err := grpc.Dial(serverAddress, grpc.WithInsecure()) 13 | 14 | if err != nil { 15 | log.Println(err.Error()) 16 | return nil, err 17 | } 18 | 19 | return conn, nil 20 | } 21 | -------------------------------------------------------------------------------- /pkg/client/http/handlers.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/edmartt/grpc-test/internal/person/models" 7 | pb "github.com/edmartt/grpc-test/internal/person/protos/bin" 8 | "github.com/edmartt/grpc-test/pkg/client" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | // @title HTTP Client for gRPC Client 13 | // @version 1.0 14 | // @description This client handles data for sending data to gRPC client and after that to gRPC server 15 | // @termsOfService http://swagger.io/terms/ 16 | 17 | // @host localhost:8080 18 | // @BasePath /api/v1 19 | 20 | // getPerson godoc 21 | // @Summary Get persons from DB 22 | // @Description Through a get request the id is sent to gRPC client 23 | // @Tags Persons 24 | // @Accept json 25 | // @Produce json 26 | // @Param id path string true "Person ID" 27 | // @Success 200 {object} models.Person 28 | // @Failure 400 {object} httpResponse 29 | // @Failure 404 {object} httpResponse 30 | // @Router /person/{id} [get] 31 | func getPerson(context *gin.Context) { 32 | id := context.Param("id") 33 | 34 | badReq := httpResponse{ 35 | Response: "ID empty", 36 | } 37 | 38 | if id == "" { 39 | context.JSON(http.StatusBadRequest, badReq) 40 | } 41 | 42 | requestPB := &pb.GetPersonRequest{ 43 | Id: id, 44 | } 45 | 46 | response, err := client.ReadPerson(requestPB) 47 | 48 | if err != nil { 49 | context.JSON(http.StatusInternalServerError, err) 50 | } 51 | 52 | if response.Person.Id == "" { 53 | context.JSON(http.StatusNotFound, httpResponse{ 54 | Response: "Not found", 55 | }) 56 | return 57 | } 58 | 59 | context.JSON(http.StatusOK, response) 60 | } 61 | 62 | // postPerson godoc 63 | // @Summary Creates new person 64 | // @Description This endpoint is for creating persons 65 | // @Tags Persons 66 | // @Accept json 67 | // @Produce json 68 | // @Param person body models.Person true "Creates person" 69 | // @Success 200 {object} httpResponse 70 | // @Failure 400 {object} httpResponse 71 | // @Router /person [post] 72 | func postPerson(context *gin.Context) { 73 | personModel := models.Person{} 74 | personProtoModel := &pb.Person{} 75 | 76 | err := context.BindJSON(&personModel) 77 | 78 | personProtoModel.FirstName = personModel.FirstName 79 | personProtoModel.LastName = personModel.LastName 80 | personProtoModel.Email = personModel.Email 81 | 82 | if err != nil { 83 | context.JSON(http.StatusBadRequest, err.Error()) 84 | return 85 | } 86 | 87 | response, err := client.CreatePerson(personProtoModel) 88 | 89 | if err != nil { 90 | context.JSON(http.StatusInternalServerError, err) 91 | } 92 | 93 | created := httpResponse{ 94 | Response: response, 95 | } 96 | 97 | context.JSON(http.StatusOK, created) 98 | } 99 | 100 | // deletePerson godoc 101 | // @Summary Deletes person by ID 102 | // @Description This endpoint is for deleting person by ID 103 | // @Tags Persons 104 | // @Accept json 105 | // @Produce json 106 | // @Param id path string true "uuid formatted ID" 107 | // @Success 200 {object} httpResponse 108 | // @Failure 400 {object} httpResponse 109 | // @Failure 404 {object} httpResponse 110 | // @Router /person/{id} [delete] 111 | func deletePerson(context *gin.Context) { 112 | id := context.Param("id") 113 | requestPB := &pb.DeletePersonRequest{ 114 | Id: id, 115 | } 116 | 117 | if id == "" { 118 | context.JSON(http.StatusBadRequest, httpResponse{ 119 | Response: "ID is missing", 120 | }) 121 | return 122 | } 123 | 124 | response, err := client.DeletePerson(requestPB) 125 | 126 | if err != nil { 127 | context.JSON(http.StatusInternalServerError, err) 128 | return 129 | } 130 | 131 | if response.Status == "not found" { 132 | context.JSON(http.StatusNotFound, httpResponse{ 133 | Response: "Not found", 134 | }) 135 | return 136 | } 137 | 138 | context.JSON(http.StatusOK, response) 139 | } 140 | -------------------------------------------------------------------------------- /pkg/client/http/responses.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | type httpResponse struct { 4 | Response string `json:"response"` 5 | } 6 | -------------------------------------------------------------------------------- /pkg/client/http/router.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | swaggerFiles "github.com/swaggo/files" 6 | ginSwagger "github.com/swaggo/gin-swagger" 7 | ) 8 | 9 | func setPersonRoutes(router *gin.RouterGroup) { 10 | router.POST("/person", postPerson) 11 | router.GET("/person/:id", getPerson) 12 | router.DELETE("/person/:id", deletePerson) 13 | router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 14 | } 15 | -------------------------------------------------------------------------------- /pkg/client/http/server.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | _ "github.com/edmartt/grpc-test/docs" 7 | ) 8 | 9 | func setRouter() *gin.Engine { 10 | router := gin.Default() 11 | apiGroup := router.Group("/api/v1") 12 | setPersonRoutes(apiGroup) 13 | 14 | return router 15 | } 16 | 17 | func Start(port string) { 18 | router := setRouter() 19 | router.Run(":" + port) 20 | } 21 | -------------------------------------------------------------------------------- /tests/grpc_mocks.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import "github.com/edmartt/grpc-test/internal/person/models" 4 | 5 | type mockDataAccess struct { 6 | } 7 | 8 | func (m mockDataAccess) Create(person models.Person) string { 9 | return "created" 10 | } 11 | 12 | func (m mockDataAccess) Read(id string) (*models.Person, error) { 13 | if id == "2" { 14 | return &models.Person{ 15 | ID: "", 16 | }, nil 17 | } 18 | return &models.Person{ 19 | ID: "1", 20 | FirstName: "ed", 21 | LastName: "mart", 22 | Email: "email", 23 | }, nil 24 | 25 | } 26 | 27 | func (m mockDataAccess) Delete(person *models.Person) (*models.Person, error) { 28 | return &models.Person{}, nil 29 | } 30 | -------------------------------------------------------------------------------- /tests/service_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/edmartt/grpc-test/internal/person" 8 | pb "github.com/edmartt/grpc-test/internal/person/protos/bin" 9 | ) 10 | 11 | func init() { 12 | person.DataAccess = mockDataAccess{} 13 | } 14 | 15 | func TestCreateGRPC(t *testing.T) { 16 | 17 | pbPerson := &pb.Person{ 18 | Id: "1", 19 | FirstName: "protoMock1", 20 | LastName: "protoLastMock1", 21 | Email: "protoMock1Email", 22 | } 23 | 24 | service := person.Service{} 25 | 26 | responseGrpc, err := service.Create(context.Background(), pbPerson) 27 | 28 | if err != nil { 29 | t.Errorf("error: %v", err) 30 | } 31 | 32 | if responseGrpc.Id == "" { 33 | t.Errorf("expected %v and got %v", &pbPerson.Id, responseGrpc.Id) 34 | } 35 | 36 | } 37 | 38 | func TestGetGRPC(t *testing.T) { 39 | request := &pb.GetPersonRequest{ 40 | Id: "1", 41 | } 42 | 43 | rightPerson := &pb.Person{ 44 | Id: "1", 45 | FirstName: "ed", 46 | LastName: "mart", 47 | Email: "email", 48 | } 49 | 50 | wrongPerson := &pb.Person{ 51 | Id: "2", 52 | FirstName: "mock2name", 53 | LastName: "mock2lastname", 54 | Email: "no email", 55 | } 56 | 57 | service := person.Service{} 58 | 59 | result, err := service.Get(context.Background(), request) 60 | 61 | if err != nil { 62 | t.Errorf("Error %s", err) 63 | } 64 | 65 | if result.Person.Id != rightPerson.Id { 66 | t.Errorf("expected %s and got %s", rightPerson.Id, result.Person.Id) 67 | } 68 | 69 | if wrongPerson.Id == result.Person.Id { 70 | t.Errorf("expected %s and got %s", wrongPerson.Id, result.Person.Id) 71 | } 72 | 73 | } 74 | 75 | func TestDeleteGRPC(t *testing.T) { 76 | service := person.Service{} 77 | 78 | request := &pb.DeletePersonRequest{ 79 | Id: "1", 80 | } 81 | expectedResponse := &pb.DeletePersonResponse{ 82 | Id: "1", 83 | Status: "deleted", 84 | } 85 | 86 | resultDelete, err := service.Delete(context.Background(), request) 87 | 88 | if err != nil { 89 | t.Errorf("got %s", err) 90 | } 91 | 92 | if resultDelete.Status != expectedResponse.Status { 93 | t.Errorf("expected %s and got %s", expectedResponse.Status, resultDelete.Status) 94 | } 95 | } 96 | 97 | func TestNotFoundBeforeDelete(t *testing.T) { 98 | service := person.Service{} 99 | 100 | requestFilled := &pb.DeletePersonRequest{ 101 | Id: "2", 102 | } 103 | 104 | expectedResponse := &pb.DeletePersonResponse{ 105 | Id: requestFilled.Id, 106 | Status: "not found", 107 | } 108 | 109 | resultNotFound, err := service.Delete(context.Background(), requestFilled) 110 | 111 | if err != nil { 112 | t.Errorf("got %s", err) 113 | } 114 | 115 | if resultNotFound.Status != expectedResponse.Status { 116 | t.Errorf("expected %s and got %s", expectedResponse.Status, resultNotFound) 117 | } 118 | 119 | } 120 | --------------------------------------------------------------------------------