Repository: techschool/pcbook-go Branch: master Commit: aa74912a982c Files: 78 Total size: 311.7 KB Directory structure: gitextract_gwqh_f95/ ├── .github/ │ └── workflows/ │ └── ci.yml ├── Makefile ├── README.md ├── cert/ │ ├── ca-cert.pem │ ├── ca-cert.srl │ ├── ca-key.pem │ ├── client-cert.pem │ ├── client-ext.cnf │ ├── client-key.pem │ ├── client-req.pem │ ├── gen.sh │ ├── server-cert.pem │ ├── server-ext.cnf │ ├── server-key.pem │ └── server-req.pem ├── client/ │ ├── auth_client.go │ ├── auth_interceptor.go │ └── laptop_client.go ├── cmd/ │ ├── client/ │ │ └── main.go │ └── server/ │ └── main.go ├── go.mod ├── go.sum ├── img/ │ └── .gitkeep ├── nginx.conf ├── pb/ │ ├── auth_service.pb.go │ ├── auth_service.pb.gw.go │ ├── auth_service_grpc.pb.go │ ├── filter_message.pb.go │ ├── keyboard_message.pb.go │ ├── laptop_message.pb.go │ ├── laptop_service.pb.go │ ├── laptop_service.pb.gw.go │ ├── laptop_service_grpc.pb.go │ ├── memory_message.pb.go │ ├── processor_message.pb.go │ ├── screen_message.pb.go │ └── storage_message.pb.go ├── proto/ │ ├── auth_service.proto │ ├── filter_message.proto │ ├── google/ │ │ ├── api/ │ │ │ ├── annotations.proto │ │ │ ├── http.proto │ │ │ └── httpbody.proto │ │ └── rpc/ │ │ ├── code.proto │ │ ├── error_details.proto │ │ └── status.proto │ ├── keyboard_message.proto │ ├── laptop_message.proto │ ├── laptop_service.proto │ ├── memory_message.proto │ ├── processor_message.proto │ ├── screen_message.proto │ └── storage_message.proto ├── sample/ │ ├── laptop.go │ └── random.go ├── serializer/ │ ├── file.go │ ├── file_test.go │ └── json.go ├── service/ │ ├── auth_interceptor.go │ ├── auth_server.go │ ├── image_store.go │ ├── jwt_manager.go │ ├── laptop_client_test.go │ ├── laptop_server.go │ ├── laptop_server_test.go │ ├── laptop_store.go │ ├── rating_store.go │ ├── user.go │ └── user_store.go ├── swagger/ │ ├── auth_service.swagger.json │ ├── filter_message.swagger.json │ ├── keyboard_message.swagger.json │ ├── laptop_message.swagger.json │ ├── laptop_service.swagger.json │ ├── memory_message.swagger.json │ ├── processor_message.swagger.json │ ├── screen_message.swagger.json │ └── storage_message.swagger.json └── tmp/ └── laptop.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/ci.yml ================================================ name: ci-test on: push: branches: [ master ] pull_request: branches: [ master ] jobs: test: name: Test runs-on: ubuntu-latest steps: - name: Set up Go 1.x uses: actions/setup-go@v2 with: go-version: ^1.15 id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Test run: make test ================================================ FILE: Makefile ================================================ clean: rm pb/* rm swagger/* gen: protoc --proto_path=proto proto/*.proto --go_out=:pb --go-grpc_out=:pb --grpc-gateway_out=:pb --openapiv2_out=:swagger server1: go run cmd/server/main.go -port 50051 server2: go run cmd/server/main.go -port 50052 server1-tls: go run cmd/server/main.go -port 50051 -tls server2-tls: go run cmd/server/main.go -port 50052 -tls server: go run cmd/server/main.go -port 8080 server-tls: go run cmd/server/main.go -port 8080 -tls rest: go run cmd/server/main.go -port 8081 -type rest -endpoint 0.0.0.0:8080 client: go run cmd/client/main.go -address 0.0.0.0:8080 client-tls: go run cmd/client/main.go -address 0.0.0.0:8080 -tls test: go test -cover -race ./... cert: cd cert; ./gen.sh; cd .. .PHONY: clean gen server client test cert ================================================ FILE: README.md ================================================ # PC Book - Go This repository contains the Golang codes for the hands-on section of [The complete gRPC course](http://bit.ly/grpccourse) by [TECH SCHOOL](https://dev.to/techschoolguru). ![The complete gRPC course](https://dev-to-uploads.s3.amazonaws.com/i/11r59di6zlyxf6g8o4s9.png) ## The complete gRPC course If you're building APIs for your microservices or mobile applications, you definitely want to try gRPC. It is super-fast, strongly-typed, and you no longer need to write a lot of boilerplate codes for services communication. Thanks to awesome HTTP/2 and Protocol Buffer! This is a 4-in-1 course, where you will learn not only gRPC, but also protocol-buffer and backend development with Go and Java. The codes in this course are production-grade, with well-organised structure and unit tests. ### What you’ll learn - What gRPC is, how it works, why we should use it, and where it is suitable to. - The amazing HTTP/2 protocol that gRPC is built on. - Compare gRPC with REST. - Write and serialise protocol-buffer messages using Go + Java. - Define gRPC services with protocol-buffer and generate Go + Java codes. - Implement 4 types of gRPC using Go + Java: unary, server-streaming, client-streaming, bidirectional streaming. - Handle context deadline, gRPC errors and status codes. - Write production-grade application with interfaces and unit tests for gRPC services. - Use gRPC interceptors to authenticate & authorise users with JWT. - Secure gRPC connection with sever-side & mutual SSL/TLS. - Enable gRPC reflections for service discovery. ### Are there any course requirements or prerequisites? - You only need to have basic programming skills in Go or Java. ## The PC book application PC book is an application to manage and search laptop configurations. It provides 4 gRPC APIs: 1. Create a new laptop: [unary gRPC](https://youtu.be/LOE_tkVFtb0) This is a unary RPC API that allows client to create a new laptop with some specific configurations. The input of the API is a laptop, and it returns the unique ID of the created laptop. The laptop ID is a UUID, and can be set by the client, or randomly generated by the server if it's not provided. 2. Search laptops with some filtering conditions: [server-streaming gRPC](https://youtu.be/SBPjEbZcgf8) This is a server-streaming RPC API that allows client to search for laptops that satisfies some filtering conditions, such as the maximum price, minimum cores, minimum CPU frequency, and minimum RAM. The input of the API is the filtering conditions, and it returns a stream of laptops that satisfy the conditions. 3. Upload a laptop image file in chunks: [client-streaming gRPC](https://youtu.be/i9H3BaRGLEc) This is a client-streaming RPC API that allows client to upload 1 laptop image file to the server. The file will be split into multiple chunks of 1 KB, and they will be sent to the server as a stream. The input of the API is a stream of request, which can either be: - Metadata of the image (only the 1st request): which contains the laptop ID, and the image type (or file extension) such as `.jpg` or `.png`. - Or a binary data chunk of the image. The total size of the image should not exceed 1 MB. The API will returns a response that contains the uploaded image ID (random UUID generated by the server) and the total size of the image. 4. Rate multiple laptops and get back average rating for each of them: [bidirectional-streaming gRPC](https://youtu.be/hjTI35iKMyQ) This is a bidirectional-streaming RPC API that allows client to rate multiple laptops, each with a score between 1 to 10, and get back the average rating score for each of them. The input of the API is a stream of requests, each with a laptop ID and a score. The API will returns a stream of responses, each contains a laptop ID, the number of times that laptop was rated, and the average rated score. ## Setup development environment - Install `protoc`: ```bash brew install protobuf ``` - Install `protoc-gen-go` and `protoc-gen-go-grpc` ```bash go get google.golang.org/protobuf/cmd/protoc-gen-go go get google.golang.org/grpc/cmd/protoc-gen-go-grpc ``` - Install `protoc-gen-grpc-gateway` and `protoc-gen-openapiv2` ```bash go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 ``` ## How to run - Generate codes from proto files: ```bash make gen ``` - Clean up generated codes in `pb` and `swagger` folders: ```bash make clean ``` - Run unit tests: ```bash make test ``` - Run server and client: ```bash make server make client ``` - Generate SSL/TLS certificates: ```bash make cert ``` ## TECH SCHOOL - From noob to pro At Tech School, we believe that everyone deserves a good and free education. The purpose of Tech School is to give everyone a chance to learn IT by giving free, high-quality tutorials and coding courses on Youtube. New videos are uploaded every week. The video topics are wide-ranging, and suitable for many different levels of tech knowledge: from noob to pro. The most important thing is: all content created by Tech School is free and will always be free. If you like the videos and want to support us with this vision, please share, subscribe, or [donate to us](https://donorbox.org/techschool). That would give us a lot of motivation to make more useful stuffs for the community. Thank you! ================================================ FILE: cert/ca-cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIFxjCCA64CCQDY35xYDFXtHjANBgkqhkiG9w0BAQsFADCBpDELMAkGA1UEBhMC RlIxEjAQBgNVBAgMCU9jY2l0YW5pZTERMA8GA1UEBwwIVG91bG91c2UxFDASBgNV BAoMC1RlY2ggU2Nob29sMRIwEAYDVQQLDAlFZHVjYXRpb24xGjAYBgNVBAMMESou dGVjaHNjaG9vbC5ndXJ1MSgwJgYJKoZIhvcNAQkBFhl0ZWNoc2Nob29sLmd1cnVA Z21haWwuY29tMB4XDTIwMDQxNTEwMjMwNVoXDTIxMDQxNTEwMjMwNVowgaQxCzAJ BgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxETAPBgNVBAcMCFRvdWxvdXNl MRQwEgYDVQQKDAtUZWNoIFNjaG9vbDESMBAGA1UECwwJRWR1Y2F0aW9uMRowGAYD VQQDDBEqLnRlY2hzY2hvb2wuZ3VydTEoMCYGCSqGSIb3DQEJARYZdGVjaHNjaG9v bC5ndXJ1QGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB AMlfsjdzwr9baKYRiL1VmbTSMxnCH7kQFYULl1073UqfkYrWSu3P8yiVSVu/EzUG wlN3C8wncRMn7JtBy3GuIjHnPC7t8E6ztcDWgear2RjzDQZmHVkcdAFji6hW2qEG LDIKUA2wfRXbjXnRqMj4ZZyIN6cHUJgo4+ZKwMus39WnwuWxQ/QNQhH432lfCHjj RiyA9AdJvObZVSUuHxfchbRR6aFFr8FL6/9hp5XTVfhz7iOEqWwXDwBQuBG8DHVt zp7i4BXx0DuTRhV31KAjz1nEdUFFZZB7rwcySAeL9OquObD8x1hehqkFU8F5z5JA O7nxWYOjTiRIVzLSwERi9TYAQbl8760jHlrn+0NDPAFXhsXmqYMYkNSJ486dNkT3 yDr6kDeb4+r3bsN6g4GnXkXSRFcYUGMZA2k5tDJZbNd0DPMRV1QKy0Hz8R3JIJnN 2bD+tQ0iS+iLpLO2kybQqnrU/1iJGju1Sg4VM5R9xOtMBlewpYOu+3gPimsDq+uP jZg3GxQiBrfMBUJ35IlAHBw+yB/ObeEbcq3nZBQcetsVFIN3Qws6SlCxU3yFj+xk /gDOSJ0pPIuNlC5x9Rf9tV4LHZizE16hnf9Z4GzBW3Wu9hFw7ESqI11c++q1Lvq0 b0Gou/TESjDlyoYE6ucKFz9I6ylqbjddCQFZl5u6AAF1AgMBAAEwDQYJKoZIhvcN AQELBQADggIBAKOD7Peqs5+BgBtX9EWk08rt29ALuO9lxxffcsAKSOYYCUOlwom4 Uz2+l8hZgrLG631iQR+l9KzpbIt4V8c+kX+NUONGG6evSxLYev77hv2Z9cdOAfWF sP4jgigcUsH6Zs6gEQ6PeBf/XqNnghu4i9dcmjdOiYj2hx7e9baBwvrqnC5foqcZ NxqWkXZSidtD1iECIEJSIJYLwMdPy5cHVdAqyiFSjSWFDMccqvUIMOjIqyRbxEch 5UxGky7+3iS3r+UvTiloOkDDDOayYclc7CCBvknbLMnzWYcDoh+RiK4qQ/GWeEpr Yq02Mg5h9C/phE6mZln3A0AgFwJ2mSzXJHy2zAbSbXybCzDmojcykq+44TxuFP6z SXGzo6nkQ6om2BF0Z9GNlPFkaqMrBIo/6kctZlG1sptswlr5BkowbZEwLUAcji7k kFzquWkZeOU1zQ1ZBDllajh6zWzJCnyR0AmRz8p7iI35XA1vHRYXsZlfVSjC3P5d 0AiyoyyuInAzAtqPZUKSRAKUFkwyVvC48p4T/GxhgViNM/mn+OS905dt9DtBzTAw NkZ/SLYnRPpwGB7csw2EJgsTYRm5pg/QtA9pCmPib1IGT9SeLACJOMe+w0VAKuun I4pOaolfVC5dclS0rMxwC1S1wBp4YWDbnrxNa6lHRb+FDZBNwBGbfjry -----END CERTIFICATE----- ================================================ FILE: cert/ca-cert.srl ================================================ B141E873FD7B8575 ================================================ FILE: cert/ca-key.pem ================================================ -----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDJX7I3c8K/W2im EYi9VZm00jMZwh+5EBWFC5ddO91Kn5GK1krtz/MolUlbvxM1BsJTdwvMJ3ETJ+yb QctxriIx5zwu7fBOs7XA1oHmq9kY8w0GZh1ZHHQBY4uoVtqhBiwyClANsH0V2415 0ajI+GWciDenB1CYKOPmSsDLrN/Vp8LlsUP0DUIR+N9pXwh440YsgPQHSbzm2VUl Lh8X3IW0UemhRa/BS+v/YaeV01X4c+4jhKlsFw8AULgRvAx1bc6e4uAV8dA7k0YV d9SgI89ZxHVBRWWQe68HMkgHi/Tqrjmw/MdYXoapBVPBec+SQDu58VmDo04kSFcy 0sBEYvU2AEG5fO+tIx5a5/tDQzwBV4bF5qmDGJDUiePOnTZE98g6+pA3m+Pq927D eoOBp15F0kRXGFBjGQNpObQyWWzXdAzzEVdUCstB8/EdySCZzdmw/rUNIkvoi6Sz tpMm0Kp61P9YiRo7tUoOFTOUfcTrTAZXsKWDrvt4D4prA6vrj42YNxsUIga3zAVC d+SJQBwcPsgfzm3hG3Kt52QUHHrbFRSDd0MLOkpQsVN8hY/sZP4AzkidKTyLjZQu cfUX/bVeCx2YsxNeoZ3/WeBswVt1rvYRcOxEqiNdXPvqtS76tG9BqLv0xEow5cqG BOrnChc/SOspam43XQkBWZebugABdQIDAQABAoICAQCVxKyhfWEsPOnaCVRvrIiC 6YrD75L0arf2maZb2zg8Ve1DGxnjQTQRzOYgbD32xC4nMXT+w57fpmPdHNQYmnAo OViTdrexcQsOfvth+hGe8rWPOsc9DWJh3g1yiBZWiGa6WN0tMUP2y7GvFnW38rZv 8wehHFmesVq+Xn6BfPOEzh6wAmUN0AaBo11V2y5L6oy4cLgN65OpBZ7D5keN0Z9H e1yNa2zKEJNW/uRLFEDuZhqJJBN1prirfV1JI1kIxUBU/1u2NoCurlwDf3oOGFQQ 6YJjpx9gk/ybF5RmuHrRR/70WSxR1wvEDYg7b0Mn/MnvA0eWFhD5/yuLSx9gPVEt I5BIF/neUsEv5g9/T5H5hgLV5vs1wBOmeliGNBVT/harXtiztDgpd+Kja2f0B/+c QkliGo3Iazxxo48cpKt+F3nVKxUtcMTKUgBFGWaS+uKR2dMA908oxh9vjL2aOEF7 YhtaXkl7vIAMouVWK0WXx5Mc6ks3gurUFMflmLslPvRmqAURYqz/kZNRqxZE0OZ2 rI/mVzck0zhp4PgKJIbRchL9Y8CgasgCPs2EF1y74Sw+cmN0PL9mk4keRDL+JnbZ 2a+57zHMqd3n1/w/lSy5BQZ0qvyJpc6wXlU+qKTbNp1FzODBcEE9Rj2j4i7yElvK xPzkVnOWfite2Tf8+XOMwQKCAQEA90tOIaZNLyN/xJLS1au7u9q84zfjEOMvpqPK BniPRshR27EKX11gOCi2F5qcdEgvV4TOdg/n++Xck7MZeXcrSvcCoiCDe0JjMC34 FW/o8376fma1mDQ7mlAIoYB22XfZidqany25pTxOJKZqEcsFYmUrJGUNZHoqjp3K vnD8Xl7JYLTZBVDU8m//GSO2Zco1lp/wttzF/vg4I6nW8uj1v8Ery6YC32Q7U9cz VhqeKnHOfXUOmb0YG9PCljudK1mJJ74ldpJ0uhIcKdMQQi5ccWu1kYgOu4JsAbCO WlCS6bptG82ysv3iqr+Mg7vnckbGk0UMl3w4g4M29ua9KOgMuQKCAQEA0HaKvfbP PiHa4TEt7iPJ010mAZmJnhpUPFz5sVRyJDJa5SPMXx3rwQb8+nq6sw/YzzGr2DEm aYz0Csn66EHgilBUQyr2jhLGdtC2iWmDA9vxew9VRNOYyerkTwpEtQMGycIitv1F 1MomEWCh56T3JN58JwgjnG/O0D4IRSRsfJsBERlHlQObJ7hEdd+cuoRIJEsrQzxg y3Z5HmFDAqLO0WOwoJV1SaNSxNwEa3GiEFydNuxgDpdUrCAl3y8wYXQQNqyQaZQw lu78hk1rMf/jGkUS/y59CKrDbS9qBYjT55yS3czfsIodg7TsP8ePtHszyW4jh3jt up6mHHG/hN/UnQKCAQEA15/Z6I1RD6EsbwJ2w8iSUSJRQO4iFz+A9RQru6izhUx1 09Fy8eRBWbZlz/8IHHw0i2NJgrgr1wB+bGrl83ttTFhE+4jOHFOumPv8LPT/chFt 6Xk1LhmdPtg6LlgNSWGvVPw/hjwge2sx19Mi+ZDEiR8dlwZlvw6mvOPpPFTYOJD9 lk0aTgBFLX9qN0lkaSz/vO6IvWmTWtakXLRisDtgzGpq/Y8rQg3rjRc/s/xRnUDO g6XlrTesJddm/AfO61WOuhCaKeFZ1kSkOfPHYW17PYplLxgrgGQgOPjxpt5Ku3HK YUviXR/y3F9Y7iSRkpsT2qWCbTPrMwDelDptBZYQQQKCAQAbXWRLGYoM8u7DnuwT lbkZuOGTVi9dhMFIB0Bzyc0N+Vo7OB7M4aWf+iXdT50QgmUIldGkGJedRXaHsAny 0SsDnRXil1I0RjytPiqoESS0rfueFt4vocMtxlrgEU4BoPsUIxrhgI/ZJgwnPdMj EGGtAlOz3/qkv3ybk3kMcoKXPNXAA9yEsCt+5E6AVrFBProloYR1WAiwzMWLemMM hoi1retyuQvjdcAYvXULEaifkzjEC/V0FON1kObHzG8Ca/Tw6Ggwo9ZZCdg2XRVU Q+3w9d5Phy+8ooXy4EV/on2GquQQn6NBjm/faTGWEcFIhN/AcmfRkctLMyZFF513 eNZ1AoIBADG8nW3c3sIWSIokw6NdI9TLMeQVqVSW2LC8K6L7ZDjl3Ypiu4fT1hBt CvF3hdvAed+n8Hgb8+rclmubbIdhAVPYKvsuyMGP7fhb7vw1HOsfwSm8ovx+AGXK siMDXDZKAvOAUcCnO5kPosTwfl3HJhbLArzl9AcU8wvSUphbL8NiJ2pdujs3863E E9NqBbjd9AHak0EZ29CEI0MLX6/9ndjROjm4Hd1FpoQexm4mh+JnPhC++8aD65qA pOnVnS+TOw3nGP6KHyorYEipzPjg+jFzsgQhup01Eqv8jZ++msHrgnj507Tf73Cp uwawX4qaay9IGjuXdE8Py3mM3xpDXPA= -----END PRIVATE KEY----- ================================================ FILE: cert/client-cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIF4jCCA8qgAwIBAgIJALFB6HP9e4V1MA0GCSqGSIb3DQEBBQUAMIGkMQswCQYD VQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMREwDwYDVQQHDAhUb3Vsb3VzZTEU MBIGA1UECgwLVGVjaCBTY2hvb2wxEjAQBgNVBAsMCUVkdWNhdGlvbjEaMBgGA1UE AwwRKi50ZWNoc2Nob29sLmd1cnUxKDAmBgkqhkiG9w0BCQEWGXRlY2hzY2hvb2wu Z3VydUBnbWFpbC5jb20wHhcNMjAwNDE1MTAyMzA4WhcNMjAwNjE0MTAyMzA4WjCB ljELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkFsc2FjZTETMBEGA1UEBwwKU3RyYXNi b3VyZzESMBAGA1UECgwJUEMgQ2xpZW50MREwDwYDVQQLDAhDb21wdXRlcjEXMBUG A1UEAwwOKi5wY2NsaWVudC5jb20xITAfBgkqhkiG9w0BCQEWEnBjY2xpZW50QGdt YWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAN1B/FRa+rT/ AQH6s5ON5GF3SnwyCv1ALnzwWb6AXFBKI8vx38JheqX8igqWyjOMzITO86YNQFhu Ix18XsRRCOPG3lcCeR6n8/uu2VyEPUcNfRHGhs+JdmT3jZeokafwsCQfwK5A2ifY qU3klElTwwVwD9HgTQOnqOgXEwzpC01EzIvQtR6/49ANOJUURfO8ae2TglXv90yT BI8u4/lV2dKPWjmxsnUpadKhKSHGU5eoT3Dyg0KTig1pXYEI6aW+qBXW0B/loone fnN2W/TvJ9u0E9ZOWEr8Ekpz7Cg4psZuGTf6PMvjEELCcm9pT/jAjOOqmKeZYU7m zecsuzmEi4d3X5XsnDXd2LuZ3VFiW9sdp0RZDgWTF7J1MJ9kg7Lhc46zhtrLTqlB FisSVMWGLvhpqjVTBUIWCuAeKxVkZgStVitzrkxk4Av5dYI5XJqDCswmEIWnPKSX t4B8S0F1sYCWYd4+GD9zkCgxPSvCNsGMKraR4E3M+EcBREJUezxAvJN5yPur+08+ r0hsQC7p4LMTZlVLsBOACGALCfQkJhM1ycBNBC1AQsRkqnsVuydlqYTef8pSCDUp qGoLkokoC2BfAVEzuHRS8PoNEX60lwU+pprCmGtbKQuFwQUX3FzyMWhtA9OSWrfD zShb4C3slKu2pk/pw6J2fAdkxAe2wBBpAgMBAAGjIzAhMB8GA1UdEQQYMBaCDiou cGNjbGllbnQuY29thwQAAAAAMA0GCSqGSIb3DQEBBQUAA4ICAQBsxrzqLj6WWqrn QFr6fEsOiprIilwW1/de8ATnubbkItjJmRsdIrZHVDgnahVr+gPnggwS8R99h4gr jalG11Fa3narO0CXldG2MGrnkU8AKuSu6dmwCBmKmZH5IhajcbhdRDuR3JOQBAn7 Yj4LZylG652Gq48hFY3bmM4/P4bKi+z7ZkoUq6iitt57Y66blfXsKUNwjtFxJEzv figvXKq1ZetAuVNcmOROvYaFisyw5Yvk7XXBw/7DwCUScKBV71MOS6FrkQ/IQ/Xq qrwD7qBacozUZ37sWULihyjJNuikDcUAVg74jcFSgohluSpbigxgXeDdWgwh8Q3Q n3ts/ZOb0XDeUp4QAqOxUShUvVEsJGi8NCOWjpp/enzeaDJ85QgCVvK8McIxQgbu i2IXmnHHzgc6PMpE79qOffeugtUCN/6odvveFq0rN3QJMKFU5mFjitWFfko2OVLv /jMuJ00cISCGJFXIDij/XzetvwD0hGME9jUhp68xdfZDX1Tnc60gqSyJqaFg6iKa 0iSvy3ShR1k/VATsDP1DTQlon39dbOYPhsSkk1Lc4RSMJBgJ6lFum+9vd9/gVZEG Jno5YIiGZphbVjE5/KQ3jtFL5WZOZHPk+FYMGxwey5h5nXN2veWhVCkwx9K4YaCB 9BhkA0M0G3zQoGqSv94DTeI32qaxNg== -----END CERTIFICATE----- ================================================ FILE: cert/client-ext.cnf ================================================ subjectAltName=DNS:*.pcclient.com,IP:0.0.0.0 ================================================ FILE: cert/client-key.pem ================================================ -----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDdQfxUWvq0/wEB +rOTjeRhd0p8Mgr9QC588Fm+gFxQSiPL8d/CYXql/IoKlsozjMyEzvOmDUBYbiMd fF7EUQjjxt5XAnkep/P7rtlchD1HDX0RxobPiXZk942XqJGn8LAkH8CuQNon2KlN 5JRJU8MFcA/R4E0Dp6joFxMM6QtNRMyL0LUev+PQDTiVFEXzvGntk4JV7/dMkwSP LuP5VdnSj1o5sbJ1KWnSoSkhxlOXqE9w8oNCk4oNaV2BCOmlvqgV1tAf5aKJ3n5z dlv07yfbtBPWTlhK/BJKc+woOKbGbhk3+jzL4xBCwnJvaU/4wIzjqpinmWFO5s3n LLs5hIuHd1+V7Jw13di7md1RYlvbHadEWQ4FkxeydTCfZIOy4XOOs4bay06pQRYr ElTFhi74aao1UwVCFgrgHisVZGYErVYrc65MZOAL+XWCOVyagwrMJhCFpzykl7eA fEtBdbGAlmHePhg/c5AoMT0rwjbBjCq2keBNzPhHAURCVHs8QLyTecj7q/tPPq9I bEAu6eCzE2ZVS7ATgAhgCwn0JCYTNcnATQQtQELEZKp7FbsnZamE3n/KUgg1Kahq C5KJKAtgXwFRM7h0UvD6DRF+tJcFPqaawphrWykLhcEFF9xc8jFobQPTklq3w80o W+At7JSrtqZP6cOidnwHZMQHtsAQaQIDAQABAoICAH2jIINN/hqUyp+zGhFpewuV T2hiijbwIPW1DWDNRp4Y22bNe7/G1nw2gLQul7bZ9rBbS6M41xbfw3TU0IMteJzO qiZCM0CjIjoCOU79kEYudJyJXLewWNhQcchyYfM5CuwYU7MfBEGoF8sxRrq0o4MM 9Q66DUFMDO9tWtXz5wUDUhr6cj55vATB3SVaE7apgIT1RAdEceq7eNVNTQqiI0Qb PqKQMsOwtnRyKwcQtRri6ek67Cn72WJwODYzN2l0b8Gm7xuNq9QZ0TgDN4hH3Rw2 jyUb66r4o/I/DRRxxtHaZtuQbsFfuDYQcCavaEfaHqaQkopo4AaLrNPeZJnul8NP whnKwydsrF40bajjIgQ7hFas/fy5toQPI501gTIGkXAWg8T2+qmFuxGqIAwGajBt 3bPJ+jbo+c3VZg4xS7yTLaa1EJcVKR60wkGxiJH0qDI0ly8WgUlxe1S+Q4WrloaK y4kchI83zKH2HplFF2YdeUoP2taxWpZAfWOoC0YgTR5BmxVoK6lVpd/zZ3QWP0rh 0Lx4XTtlVphgYC3EV3ue5dmFo7Q0hnYIgbdRyW9J4ODmszcrRwqwyTx/v4oTmHb0 FKrsjSpVkvxB3ZMZP5En5bKzsTMneu0IaLhQrXNeUEVwTH9vDSnXX0ugCdDL7vKV GaFcl9Ns8i6hW9X2Kk3ZAoIBAQD9oAmjo8mA8jStVAo3PvzpR2TjQFg/m5QqhmhB ju1ZJhBlcW+NRUBKv3FxgtdsRp+eSLVtc5BeryMSHvN1ertXfmfUfdihMrKmJ9Gn MjxbyqXnBREe8ALrNNjYNgXzviBNunxNaNjS7F/niAvHG93S7Bl3jy7nOy4VyxPs 7CkkcaYIJ3octTMkC+t00MPba6Ya4doUmpmgPWtiX3rHktn1xYVmXueWs6/Fyvfk cAXsMxnjsFhw3LneILiYBW32d4+p4L7X3vhL4j7mbMYXf5XqwBOg3eWPVODpqVGU iGFSOAC4CUtvrGIzFQ0tCbjA9w8OXdd6xu4JzdxAyge0JAFXAoIBAQDfVFxGykNW QbX1PwP+v/n/3nmgSM07zF8xFy2tSu3YhF7eIje56zGDhaGVlN6wU0Gqm5bgENeL FFBSLIzBw9Qi2mgRxd2tXB79v3swukhhGmCFVwSOWbPGQrDuOXHU1EEHmq8xRMo4 mc8s9nCJotOe1TN5BcbUVuPRu+9zW2lEhuxoMn9ZhtiHOUop+TJqb3wMICr4K137 dfICtb25DK6M5r5Es9DP1G6e2OjiH40Kkd+/vsxZu2gzIxyGcOcqde20yS+ihm76 yjPKkwV5s2HLFRv1hiOc0PUNqzLWC1pMEKiEqPmBtIh3mj5vq6ZmL7UXedT4V2my tm0n5PtqOKQ/AoIBAQCY/any4UETHIe0KqbC7qcHXT65ar4RGJtHD67iJQJ9rV1k pAnDcQu4S0V2UJP8R5nPlFKExJpI02LXcn4v1qodvC2L26IKkxd67TgloEMSp+pt sfvC6ssH8OgBfI0YnA7GdIC4/U8V5OpxMvrPz7p+mlc+bMvBRkylbswFNewXhMq/ znh1ysQfsWUGIUyUFpqrSqQPm7aiF4qoW6onqyj5fX3b49HVcWzNZoMkdILOGYE7 fMvMwQkJujk/0r6jVzn9Iopck665r697tg/Eav0XD2iHuHLahDvsF2wTqjTysL+W RF0R7y2JXOCG0390P1QAuZDbChbbKSf8mSIOg02fAoIBAQDAo9ExEvmApw/gq2mz zj9EsdAyLXozEbgu7TJuX8rIUG5QqC1vhuvf6l4WXCK28CodkzZSstRqWKxsJZeI 8HXFVqYcZpQwHN1yvj/yKU2TzR/jBMueSswiwZZC93Q0RJ6Pg6OJGTBiIHKv8yfh 4X0vbfKHey8mLIk5eiYzWG92N/gmbSCixglyoz1Q9W7ClsXm47yM80OPTA7kvYYY 4FKUodkQBBejnjeJd8tyegq8SlY53MgCwwA/1BKf+TW9z5mqrzwSsml6lP6Vx7oa X1yExAGpCPshIrGvB7TDI2nRYTErtWH7uxFYMcmXo/XWAWLxDBtj2GsJSAjiN8eS uacbAoIBAFTcrGjSRo/v8DT+r8z5BClwuBmnsRWzTNx3ay/EMnLdGv2Jcg5I+p7l VjioNmz/mF7Ff94nH0VohDrsbmKlVWs8zOxiotG/UUhgHfp2MX1OTxXzxwbxx+sB Ekb7WuZ8yxWI7WboH5r4ENla3SeQb1HETNDhJoqVCi67XGkq5rY4DlNXtZQZdkzB SH0c0JVykXk265qsvpthKObTakb92azMCdToipl0bdwRWwzjLlNV1cqJHdtaD40Z 0u8CfN8S2w7nwGIcLLWQVbN1nx7nspFmbo2kezu62YHpGh+hCvwGWTLelK6lAQWS ovQGWC8qf6sde8JLYDVa3WC36B1OwTQ= -----END PRIVATE KEY----- ================================================ FILE: cert/client-req.pem ================================================ -----BEGIN CERTIFICATE REQUEST----- MIIE3DCCAsQCAQAwgZYxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZBbHNhY2UxEzAR BgNVBAcMClN0cmFzYm91cmcxEjAQBgNVBAoMCVBDIENsaWVudDERMA8GA1UECwwI Q29tcHV0ZXIxFzAVBgNVBAMMDioucGNjbGllbnQuY29tMSEwHwYJKoZIhvcNAQkB FhJwY2NsaWVudEBnbWFpbC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK AoICAQDdQfxUWvq0/wEB+rOTjeRhd0p8Mgr9QC588Fm+gFxQSiPL8d/CYXql/IoK lsozjMyEzvOmDUBYbiMdfF7EUQjjxt5XAnkep/P7rtlchD1HDX0RxobPiXZk942X qJGn8LAkH8CuQNon2KlN5JRJU8MFcA/R4E0Dp6joFxMM6QtNRMyL0LUev+PQDTiV FEXzvGntk4JV7/dMkwSPLuP5VdnSj1o5sbJ1KWnSoSkhxlOXqE9w8oNCk4oNaV2B COmlvqgV1tAf5aKJ3n5zdlv07yfbtBPWTlhK/BJKc+woOKbGbhk3+jzL4xBCwnJv aU/4wIzjqpinmWFO5s3nLLs5hIuHd1+V7Jw13di7md1RYlvbHadEWQ4FkxeydTCf ZIOy4XOOs4bay06pQRYrElTFhi74aao1UwVCFgrgHisVZGYErVYrc65MZOAL+XWC OVyagwrMJhCFpzykl7eAfEtBdbGAlmHePhg/c5AoMT0rwjbBjCq2keBNzPhHAURC VHs8QLyTecj7q/tPPq9IbEAu6eCzE2ZVS7ATgAhgCwn0JCYTNcnATQQtQELEZKp7 FbsnZamE3n/KUgg1KahqC5KJKAtgXwFRM7h0UvD6DRF+tJcFPqaawphrWykLhcEF F9xc8jFobQPTklq3w80oW+At7JSrtqZP6cOidnwHZMQHtsAQaQIDAQABoAAwDQYJ KoZIhvcNAQELBQADggIBAHUPxGmovTlpVp6d9tOv2RbrqOq/9XDrul6O21/wsU0A ZWlDyIHCrxjlTg8YrrHl1tiJYpi03pb707j6pjX6Q/AWLrTHuLo8l2cDnDu+xL08 SWZ20DtmM5GnYdmJzjY6mR6b5lYDrGlqFaynKCBn2IgB0AJeraGSsh/+cnhOgzya 6nKm8bOM9DSkcuki5b9JTYjtnmSUlwaaup0rlkKtnt0XLV4jKcQL4DurYfSXUXgs sazbKj/N2xkRiSyk40lf5ASPFAyXDOr57c+IMKokhcWZvZunZc+reqWXkwvJQwZ9 8UNsrHmNopDbQI1mLk4Ky8cPRWbtdyjnJlwPc59TbGmdVNWm6KMmCwXqiegORivo fwhOPEXvTusrcwgyREtS+bYOG0sFVGce6nxcIjBf9sY+igXGUTm9UyQVGO0YDLpJ hJLL5bb9iv4nurokFWuoGI0H3XpeiLXBO6oPA/PXSEru6deP9/HwtGADVcoxQFa0 fLn9bA7lyyIXm6UpQXIAMv/Ptqv7v0SoesrwpxloJQKa43Iv0CkaFrWfLedCdayR BgqOMvFcN6pzfCnaEpFb/CppBPP7Q/wVhAuIlmVq7M75q6/rW819RLxZFaNF0Yyx 0P5n1b8sSlUuQPPG0JNHGDZbM8Jz7iKMhcJuH8CQGI3EqPJVM3onvlYV1AWDKWI3 -----END CERTIFICATE REQUEST----- ================================================ FILE: cert/gen.sh ================================================ rm *.pem # 1. Generate CA's private key and self-signed certificate openssl req -x509 -newkey rsa:4096 -days 365 -nodes -keyout ca-key.pem -out ca-cert.pem -subj "/C=FR/ST=Occitanie/L=Toulouse/O=Tech School/OU=Education/CN=*.techschool.guru/emailAddress=techschool.guru@gmail.com" echo "CA's self-signed certificate" openssl x509 -in ca-cert.pem -noout -text # 2. Generate web server's private key and certificate signing request (CSR) openssl req -newkey rsa:4096 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=FR/ST=Ile de France/L=Paris/O=PC Book/OU=Computer/CN=*.pcbook.com/emailAddress=pcbook@gmail.com" # 3. Use CA's private key to sign web server's CSR and get back the signed certificate openssl x509 -req -in server-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile server-ext.cnf echo "Server's signed certificate" openssl x509 -in server-cert.pem -noout -text # 4. Generate client's private key and certificate signing request (CSR) openssl req -newkey rsa:4096 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=FR/ST=Alsace/L=Strasbourg/O=PC Client/OU=Computer/CN=*.pcclient.com/emailAddress=pcclient@gmail.com" # 5. Use CA's private key to sign client's CSR and get back the signed certificate openssl x509 -req -in client-req.pem -days 60 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -extfile client-ext.cnf echo "Client's signed certificate" openssl x509 -in client-cert.pem -noout -text ================================================ FILE: cert/server-cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIF6jCCA9KgAwIBAgIJALFB6HP9e4V0MA0GCSqGSIb3DQEBBQUAMIGkMQswCQYD VQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMREwDwYDVQQHDAhUb3Vsb3VzZTEU MBIGA1UECgwLVGVjaCBTY2hvb2wxEjAQBgNVBAsMCUVkdWNhdGlvbjEaMBgGA1UE AwwRKi50ZWNoc2Nob29sLmd1cnUxKDAmBgkqhkiG9w0BCQEWGXRlY2hzY2hvb2wu Z3VydUBnbWFpbC5jb20wHhcNMjAwNDE1MTAyMzA3WhcNMjAwNjE0MTAyMzA3WjCB kjELMAkGA1UEBhMCRlIxFjAUBgNVBAgMDUlsZSBkZSBGcmFuY2UxDjAMBgNVBAcM BVBhcmlzMRAwDgYDVQQKDAdQQyBCb29rMREwDwYDVQQLDAhDb21wdXRlcjEVMBMG A1UEAwwMKi5wY2Jvb2suY29tMR8wHQYJKoZIhvcNAQkBFhBwY2Jvb2tAZ21haWwu Y29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwOsO3r/3ul2O2W1k ka9j7VYLmXcBOY3Lkoo+k4N6dwLXH9NADO5cBQsLFyljHZ+gwbeV0pRDTs/PFwNA ovHToNA6I2Uw9TI3Yz/MzUlZ4PpWkhdpYEnXWOgccXeKTFyYX4oKCGiTYLyDV7DH E9kdMPX+sz7aiayloUKzOEVdLFtUIH2V8e9U5j7zKWAGoPCqbWYRGoAG6y8i3udi 6pS/7ETq0MVI+QXcQ8+eOrzNAZjkagMhEKB1czLFh7bb3YtHz/fl/Sujqn21oKWI Y8CAHuMxYFMZO6im0QkztiL35vRPyozhi+Kr2LIaN4mfCKBjps13uGvYS8fDZbDD +F8ix/Avhbz4PWEm2S5QdDSz44/69sbFKLk3qqoz0tO4byu5ADjIsnba9/9ZeqNx 3IiIXkPKMfNqfgq1Ti3M90aWW2uPF96AXWc/MKKHErA/AqgOn8XTBjzi/VBenoHH NrsNoz0j5wV7i5SBPAGEOu5s7jV7hc5B5geWIpLOklUzryGiv5IRsM3U/dV9qYry 5GPywW0usxyyUKvTCe/GLt+63h+0xdGTbPs4Rd0twX0V1ZOpzMjnpaYtmczl+x8S R0ZsTzyLhXsCzUPY5yNuGBNKIXBtwedZCEIZ3vT23lzvEgagUhqkymocVi9lXL+Z U8I6Ketb4FHIjVtvCCZj8FVvHE0CAwEAAaMvMC0wKwYDVR0RBCQwIoIMKi5wY2Jv b2suY29tggwqLnBjYm9vay5vcmeHBAAAAAAwDQYJKoZIhvcNAQEFBQADggIBABmv Ce/pzmsilW9rFKPnZyr4zjdevi3zK4hn78iT2eCMoH48UigoFERphtqtnoinmHQD qMFRfJ7ZBx6wPYo/fgxp+sXjMKT+hWUtU2CNQoVbeH2dpKU7xeyh82Q7fesajIYi UrpuBlYBjZywNPJrpSKJyaq4H37Yt60SCA8GUrXb5LHcurRSJNtIQ8muOVgwvjAY 1mzADfOW2wlVX84oyXBHFo+3iXvjKhp8AahTCiQak4MvJwMPuiUJf8qIaqNi1w0s Ot5p4QtLECYrfJxtsmyGRCOrCWdXpj2oPY85bz0CMdf7IuufFoTarhBm1E71YhOl Sgrnj3w89HxIYDG/DvaybXO4/bs62oyQzcLzgeyoJeRLp23LpSR+8o4OV9koKUyQ DBMNikKIqk+pZWEiJ6uILYUH1Ay8BBkUt/XY5Eeuy5RPtBrPJENHNAwgN5HU+h5i glOc/rCpgYWWIVeHeickMxr3lZXVel9viNAucmLu9OuKi/jHksJcqimMDAwZGlnj waeLHv1Ea/DX24lUkuA2m5w9RCcNI2ZmTmL7svwZrkdNmLLkcqz2nWBFyNZC5+P3 pN4Qe/thzJWsss1JB/Kn6V/2NX2/yr6eKrjt8jo5rucUbdtysOYrjCMBm0RiCJbK 3I3Nv1KsekkLnFiQIW7uXwLl5FJcaXHLfvYZlusV -----END CERTIFICATE----- ================================================ FILE: cert/server-ext.cnf ================================================ subjectAltName=DNS:*.pcbook.com,DNS:*.pcbook.org,IP:0.0.0.0 ================================================ FILE: cert/server-key.pem ================================================ -----BEGIN PRIVATE KEY----- MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDA6w7ev/e6XY7Z bWSRr2PtVguZdwE5jcuSij6Tg3p3Atcf00AM7lwFCwsXKWMdn6DBt5XSlENOz88X A0Ci8dOg0DojZTD1MjdjP8zNSVng+laSF2lgSddY6Bxxd4pMXJhfigoIaJNgvINX sMcT2R0w9f6zPtqJrKWhQrM4RV0sW1QgfZXx71TmPvMpYAag8KptZhEagAbrLyLe 52LqlL/sROrQxUj5BdxDz546vM0BmORqAyEQoHVzMsWHttvdi0fP9+X9K6OqfbWg pYhjwIAe4zFgUxk7qKbRCTO2Ivfm9E/KjOGL4qvYsho3iZ8IoGOmzXe4a9hLx8Nl sMP4XyLH8C+FvPg9YSbZLlB0NLPjj/r2xsUouTeqqjPS07hvK7kAOMiydtr3/1l6 o3HciIheQ8ox82p+CrVOLcz3RpZba48X3oBdZz8woocSsD8CqA6fxdMGPOL9UF6e gcc2uw2jPSPnBXuLlIE8AYQ67mzuNXuFzkHmB5Yiks6SVTOvIaK/khGwzdT91X2p ivLkY/LBbS6zHLJQq9MJ78Yu37reH7TF0ZNs+zhF3S3BfRXVk6nMyOelpi2ZzOX7 HxJHRmxPPIuFewLNQ9jnI24YE0ohcG3B51kIQhne9PbeXO8SBqBSGqTKahxWL2Vc v5lTwjop61vgUciNW28IJmPwVW8cTQIDAQABAoICAQCQuYZdSuxAbmF08aEJvecc LHnlNibAE4TNuVI6fd8ImyPhpywcx1BXJDK8vHqzxYXm7Z/C6yEXZcR5AiKiWwKl WLDUztwMhhCRL1KoCsgXhBYf4NpXtu2LsA1uffxNTwWsXrUqG7G8V4+84Exosm84 xMK/m3582/0hXhVvOHIujZEuEqjDaAVr+XuX5Ybzg7iG+5QHKlaGZsUlLbbCPrdA O29hES+uSVw1rvKIJA0zjoyEjzZl78pMkqEnL+H/cLZ96P4rkGpmw4nXK3eHRemX wl7PYWfDnsEOfnXBxLfvFgcp78hglbrPhMUwNtkMsq4ve2K+AoGwT/thNVu+3zgK Mj/L+QSOKW7uY+E5oaETy7lg5P+65++4/iGyuszp2zX/iPMJcYwDoYcIwmIYkyIZ nM5RNNCFoPAShKmkoboRh9KmVnzvoYPIpvYnNKGmQ/UCFxRm+l7dF9hrY+ZEYGcn bF1nhKW8/i/GsVVvWOSN1NUdqZG80DwXNBruiKI171xPDoeEF+tlBaxz4Vk8F+yc 01gwAZnMMdF/3DPODmEEE40WFzSRy/HgzSQoLFFRpMU945S+LrR/1nzOFHgHubgz stlp/oeV5IXZXbe3hnfWmfGSgEw7WEAlEvsofna/6REigNrBWsaZzKTlGlYuc1JK ZgHoPjrwrbZgkYWH5fQfIQKCAQEA65PIIW4RhuvaWXPtuojNm0RHN4W0tvrkfhrf KwnUM7atCqLHpf7doBJzpGSo3Q8vcaAhkvmINKVXogWV/mKulFE3QepcTjEpQNiQ STrRm/Qg/Jv9zmpDYddtuVm3sHqfmONmsIBuMv/hPGxbuKr2x4lQZPI68wth0/0b gxkNVF3JAksTyDpe7Zr0QEAKFiBwptTDnnZ5YdtiN3E1IwWle2FIa0AD8GAk/LH4 l/1aavgh9imEzbGqvlu8qU86LrPNfVjRUMe1Hk9Mpe8D9BuL6kk/Gx7b4bl+ObLG wiIH+AVJa8RE3k7joqgmrVDGfL8q3SFyiNV/msyo0yLSY4bORQKCAQEA0aSIt14/ 642yzO2TTtO9+EThG2PMV14sAWWWp6XRWtPUACmaCuI7Daz/taeymT2EiHx6x7pU Rw4LZrhXPbjI0X4GuH0VSxYWnP1rgNqiqkTBMe9JNmNmweGHr3Ok7HMDX5QMKhxX nFRYpRczrkSa87E5Js+jPRwiUUizOpfl5TsEAxS1kgcg+FJLqh2SfSOA8dBPArch 4W3O0L/E1AuLhfVUQLkTfUjQQJVVaZ2E3pWBLyGya10UbAcOlfi5MxFrK1M7gSRv yMrk4l7N+68rZEk9DR93/p0C3B7kKB8lVQDfsaqnlMo7VXwG0oHTAsExV8JgPwQm DMkkU2JPI0OaaQKCAQBkc0JuBsoQdvdHF2iyFm1dnJKleSzirT7LCthIOMu0NVu5 4kkxXejQva1z1rwubrAzSi2mxyIuGKayXqFjtF5uvebLA4zShqHpla6Imz0Pu9xo +ncSEjujN8IAu+HYraDqB2UdM9ZJhtRa+HVv2+6YjNOsB6HdSugvBYk6sG7/n3H7 uVm5EjKyLFWkI+ppHvIKIUU8h5YghPRvYaVfxqOWZZgEq2pCkCyVV6oB3TU10ZJh rbiEIRMGUoWyyCauDVs87KdsQ4vWXcf4JV/RMgHKJ+txvAnUSU0qezHHS82ME20I N8uJ46erDvpXAs9wF+/GFOIKuMbNkiEWzo1ZhPzlAoIBAEL8rQbSoeAVlfVvUGuW sxP6hmdnGysrlyoXGO2WyW+ZUht/L46cvTvgdJDJ9gKLKqcmB2F8g2N09GWtL4s0 WU6/U0xuA3jLpQwi2dABjIqVj5nyGNW9K192PhHtBNzc304SE1T9W21DclPGNyhP GagWj+l73XAwZjLM5SAq2zXFBsIpQt9XUcynFzBTZLSBvLkH08dNVxEeMkB3lmAf FEUIoBRSTwzwUELitLkbsRIieXXi8Yzm4BiopJt9L0hHH5RncxMP3nwtgLdoja8H SPkxgcWIsaH0764AXO0JDre7oL63hfbAK/djuxZWj2NI8ghVvsVEARiCyQ2v0xO0 jUkCggEBALfx/VF1oYXVdpdHLfFZ0A7qgrVOpFa+mwzoSUx20/x9KIPsaKU0Rpc4 rRcdyGOqOm8ScQacpxkXqyh/akhG7WQysxp0EGof5IReWJJTTdlzZO0L5FN/EIrH mLEojmA5xs54De6ywWPxmOsnk2jxYaGfUzVMzvBaJSkTLfIr/JGIsZMP590L4MCC q10rQqmt+I5HZlsdDW908ThT1VwglRW6kQskxaEIsY1vOqi8q9AvrVNjSlga8Jp3 jrNe+80SA3nglFU16x7t7BkcrjTeE8zeuwRzzt98xHJSto6QRS2PWwCwGeGnyO6s mQ46mZhKvL1F0M1v7dIRGpBcXSwHnIQ= -----END PRIVATE KEY----- ================================================ FILE: cert/server-req.pem ================================================ -----BEGIN CERTIFICATE REQUEST----- MIIE2DCCAsACAQAwgZIxCzAJBgNVBAYTAkZSMRYwFAYDVQQIDA1JbGUgZGUgRnJh bmNlMQ4wDAYDVQQHDAVQYXJpczEQMA4GA1UECgwHUEMgQm9vazERMA8GA1UECwwI Q29tcHV0ZXIxFTATBgNVBAMMDCoucGNib29rLmNvbTEfMB0GCSqGSIb3DQEJARYQ cGNib29rQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB AMDrDt6/97pdjtltZJGvY+1WC5l3ATmNy5KKPpODencC1x/TQAzuXAULCxcpYx2f oMG3ldKUQ07PzxcDQKLx06DQOiNlMPUyN2M/zM1JWeD6VpIXaWBJ11joHHF3ikxc mF+KCghok2C8g1ewxxPZHTD1/rM+2omspaFCszhFXSxbVCB9lfHvVOY+8ylgBqDw qm1mERqABusvIt7nYuqUv+xE6tDFSPkF3EPPnjq8zQGY5GoDIRCgdXMyxYe2292L R8/35f0ro6p9taCliGPAgB7jMWBTGTuoptEJM7Yi9+b0T8qM4Yviq9iyGjeJnwig Y6bNd7hr2EvHw2Www/hfIsfwL4W8+D1hJtkuUHQ0s+OP+vbGxSi5N6qqM9LTuG8r uQA4yLJ22vf/WXqjcdyIiF5DyjHzan4KtU4tzPdGlltrjxfegF1nPzCihxKwPwKo Dp/F0wY84v1QXp6Bxza7DaM9I+cFe4uUgTwBhDrubO41e4XOQeYHliKSzpJVM68h or+SEbDN1P3VfamK8uRj8sFtLrMcslCr0wnvxi7fut4ftMXRk2z7OEXdLcF9FdWT qczI56WmLZnM5fsfEkdGbE88i4V7As1D2OcjbhgTSiFwbcHnWQhCGd709t5c7xIG oFIapMpqHFYvZVy/mVPCOinrW+BRyI1bbwgmY/BVbxxNAgMBAAGgADANBgkqhkiG 9w0BAQsFAAOCAgEAH+8i1ADYuh3KgEFmEOgmhBe2waanpC7bBOaPCuLlXOhJcZnb TPTPVx9zBShPuaQS+xgobpRxBlataHKw2a/YoO8lPklglAcR188uRToIHjD/oeEa nAwOE+FZHcxj7lkEWTjKTFlCNkEuWYm2QqEpYZ9jTRw23JyU6zHHZU9TRPRBNReL hudzJOB4ZN+DZOOvhEUEOxNhVJDvPo9jldS7Ha0Xg8Q7AMxbmc7LYFx11/f2Oejv PJL3HYi2WGRp2Ie81Heu8CgX5KEt6gu8EbLmTopQsVcU9CirO5klgFtJf3YzAMYz 4jZqbwR3N2WYGvloT2DrMvMhX4m+d0eC8I5M7JIW+GzUO7h4szvhSJ2Vw9c1rcGP g6Y6RxEyG/GJtaG3SII8M+nXbPG4D/ZDYlh4YP0wWxeQZimifpAPmVpYlUG1nKgC erHoOIw1INI9M5jgbFNEcTE/GLqficWjypq6lBeV5aZo/rXgyQFoxHI1N2lRbVfN dEA+2D89le6VCizjHIsTDyY9tqGkRopsvm78DEwSmCBHnxRiky9A4MTRApAKoJDJ Q/7AxbY7rhcWmwn+GZDoP5aY1aLe86Y1w3QR2VWKeXOKQeepRsOs8QTAPJoowpdy fWlZKRZdDjhiwTCNztFL72KV7x0DcLVowYRJfrrK0MQdoosBs2DR74bztEg= -----END CERTIFICATE REQUEST----- ================================================ FILE: client/auth_client.go ================================================ package client import ( "context" "time" "gitlab.com/techschool/pcbook/pb" "google.golang.org/grpc" ) // AuthClient is a client to call authentication RPC type AuthClient struct { service pb.AuthServiceClient username string password string } // NewAuthClient returns a new auth client func NewAuthClient(cc *grpc.ClientConn, username string, password string) *AuthClient { service := pb.NewAuthServiceClient(cc) return &AuthClient{service, username, password} } // Login login user and returns the access token func (client *AuthClient) Login() (string, error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() req := &pb.LoginRequest{ Username: client.username, Password: client.password, } res, err := client.service.Login(ctx, req) if err != nil { return "", err } return res.GetAccessToken(), nil } ================================================ FILE: client/auth_interceptor.go ================================================ package client import ( "context" "log" "time" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) // AuthInterceptor is a client interceptor for authentication type AuthInterceptor struct { authClient *AuthClient authMethods map[string]bool accessToken string } // NewAuthInterceptor returns a new auth interceptor func NewAuthInterceptor( authClient *AuthClient, authMethods map[string]bool, refreshDuration time.Duration, ) (*AuthInterceptor, error) { interceptor := &AuthInterceptor{ authClient: authClient, authMethods: authMethods, } err := interceptor.scheduleRefreshToken(refreshDuration) if err != nil { return nil, err } return interceptor, nil } // Unary returns a client interceptor to authenticate unary RPC func (interceptor *AuthInterceptor) Unary() grpc.UnaryClientInterceptor { return func( ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { log.Printf("--> unary interceptor: %s", method) if interceptor.authMethods[method] { return invoker(interceptor.attachToken(ctx), method, req, reply, cc, opts...) } return invoker(ctx, method, req, reply, cc, opts...) } } // Stream returns a client interceptor to authenticate stream RPC func (interceptor *AuthInterceptor) Stream() grpc.StreamClientInterceptor { return func( ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption, ) (grpc.ClientStream, error) { log.Printf("--> stream interceptor: %s", method) if interceptor.authMethods[method] { return streamer(interceptor.attachToken(ctx), desc, cc, method, opts...) } return streamer(ctx, desc, cc, method, opts...) } } func (interceptor *AuthInterceptor) attachToken(ctx context.Context) context.Context { return metadata.AppendToOutgoingContext(ctx, "authorization", interceptor.accessToken) } func (interceptor *AuthInterceptor) scheduleRefreshToken(refreshDuration time.Duration) error { err := interceptor.refreshToken() if err != nil { return err } go func() { wait := refreshDuration for { time.Sleep(wait) err := interceptor.refreshToken() if err != nil { wait = time.Second } else { wait = refreshDuration } } }() return nil } func (interceptor *AuthInterceptor) refreshToken() error { accessToken, err := interceptor.authClient.Login() if err != nil { return err } interceptor.accessToken = accessToken log.Printf("token refreshed: %v", accessToken) return nil } ================================================ FILE: client/laptop_client.go ================================================ package client import ( "bufio" "context" "fmt" "io" "log" "os" "path/filepath" "time" "gitlab.com/techschool/pcbook/pb" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // LaptopClient is a client to call laptop service RPCs type LaptopClient struct { service pb.LaptopServiceClient } // NewLaptopClient returns a new laptop client func NewLaptopClient(cc *grpc.ClientConn) *LaptopClient { service := pb.NewLaptopServiceClient(cc) return &LaptopClient{service} } // CreateLaptop calls create laptop RPC func (laptopClient *LaptopClient) CreateLaptop(laptop *pb.Laptop) { req := &pb.CreateLaptopRequest{ Laptop: laptop, } // set timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() res, err := laptopClient.service.CreateLaptop(ctx, req) if err != nil { st, ok := status.FromError(err) if ok && st.Code() == codes.AlreadyExists { // not a big deal log.Print("laptop already exists") } else { log.Fatal("cannot create laptop: ", err) } return } log.Printf("created laptop with id: %s", res.Id) } // SearchLaptop calls search laptop RPC func (laptopClient *LaptopClient) SearchLaptop(filter *pb.Filter) { log.Print("search filter: ", filter) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() req := &pb.SearchLaptopRequest{Filter: filter} stream, err := laptopClient.service.SearchLaptop(ctx, req) if err != nil { log.Fatal("cannot search laptop: ", err) } for { res, err := stream.Recv() if err == io.EOF { return } if err != nil { log.Fatal("cannot receive response: ", err) } laptop := res.GetLaptop() log.Print("- found: ", laptop.GetId()) log.Print(" + brand: ", laptop.GetBrand()) log.Print(" + name: ", laptop.GetName()) log.Print(" + cpu cores: ", laptop.GetCpu().GetNumberCores()) log.Print(" + cpu min ghz: ", laptop.GetCpu().GetMinGhz()) log.Print(" + ram: ", laptop.GetRam()) log.Print(" + price: ", laptop.GetPriceUsd()) } } // UploadImage calls upload image RPC func (laptopClient *LaptopClient) UploadImage(laptopID string, imagePath string) { file, err := os.Open(imagePath) if err != nil { log.Fatal("cannot open image file: ", err) } defer file.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() stream, err := laptopClient.service.UploadImage(ctx) if err != nil { log.Fatal("cannot upload image: ", err) } req := &pb.UploadImageRequest{ Data: &pb.UploadImageRequest_Info{ Info: &pb.ImageInfo{ LaptopId: laptopID, ImageType: filepath.Ext(imagePath), }, }, } err = stream.Send(req) if err != nil { log.Fatal("cannot send image info to server: ", err, stream.RecvMsg(nil)) } reader := bufio.NewReader(file) buffer := make([]byte, 1024) for { n, err := reader.Read(buffer) if err == io.EOF { break } if err != nil { log.Fatal("cannot read chunk to buffer: ", err) } req := &pb.UploadImageRequest{ Data: &pb.UploadImageRequest_ChunkData{ ChunkData: buffer[:n], }, } err = stream.Send(req) if err != nil { log.Fatal("cannot send chunk to server: ", err, stream.RecvMsg(nil)) } } res, err := stream.CloseAndRecv() if err != nil { log.Fatal("cannot receive response: ", err) } log.Printf("image uploaded with id: %s, size: %d", res.GetId(), res.GetSize()) } // RateLaptop calls rate laptop RPC func (laptopClient *LaptopClient) RateLaptop(laptopIDs []string, scores []float64) error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() stream, err := laptopClient.service.RateLaptop(ctx) if err != nil { return fmt.Errorf("cannot rate laptop: %v", err) } waitResponse := make(chan error) // go routine to receive responses go func() { for { res, err := stream.Recv() if err == io.EOF { log.Print("no more responses") waitResponse <- nil return } if err != nil { waitResponse <- fmt.Errorf("cannot receive stream response: %v", err) return } log.Print("received response: ", res) } }() // send requests for i, laptopID := range laptopIDs { req := &pb.RateLaptopRequest{ LaptopId: laptopID, Score: scores[i], } err := stream.Send(req) if err != nil { return fmt.Errorf("cannot send stream request: %v - %v", err, stream.RecvMsg(nil)) } log.Print("sent request: ", req) } err = stream.CloseSend() if err != nil { return fmt.Errorf("cannot close send: %v", err) } err = <-waitResponse return err } ================================================ FILE: cmd/client/main.go ================================================ package main import ( "crypto/tls" "crypto/x509" "flag" "fmt" "io/ioutil" "log" "strings" "time" "gitlab.com/techschool/pcbook/client" "gitlab.com/techschool/pcbook/pb" "gitlab.com/techschool/pcbook/sample" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) func testCreateLaptop(laptopClient *client.LaptopClient) { laptopClient.CreateLaptop(sample.NewLaptop()) } func testSearchLaptop(laptopClient *client.LaptopClient) { for i := 0; i < 10; i++ { laptopClient.CreateLaptop(sample.NewLaptop()) } filter := &pb.Filter{ MaxPriceUsd: 3000, MinCpuCores: 4, MinCpuGhz: 2.5, MinRam: &pb.Memory{Value: 8, Unit: pb.Memory_GIGABYTE}, } laptopClient.SearchLaptop(filter) } func testUploadImage(laptopClient *client.LaptopClient) { laptop := sample.NewLaptop() laptopClient.CreateLaptop(laptop) laptopClient.UploadImage(laptop.GetId(), "tmp/laptop.jpg") } func testRateLaptop(laptopClient *client.LaptopClient) { n := 3 laptopIDs := make([]string, n) for i := 0; i < n; i++ { laptop := sample.NewLaptop() laptopIDs[i] = laptop.GetId() laptopClient.CreateLaptop(laptop) } scores := make([]float64, n) for { fmt.Print("rate laptop (y/n)? ") var answer string fmt.Scan(&answer) if strings.ToLower(answer) != "y" { break } for i := 0; i < n; i++ { scores[i] = sample.RandomLaptopScore() } err := laptopClient.RateLaptop(laptopIDs, scores) if err != nil { log.Fatal(err) } } } const ( username = "admin1" password = "secret" refreshDuration = 30 * time.Second ) func authMethods() map[string]bool { const laptopServicePath = "/techschool.pcbook.LaptopService/" return map[string]bool{ laptopServicePath + "CreateLaptop": true, laptopServicePath + "UploadImage": true, laptopServicePath + "RateLaptop": true, } } func loadTLSCredentials() (credentials.TransportCredentials, error) { // Load certificate of the CA who signed server's certificate pemServerCA, err := ioutil.ReadFile("cert/ca-cert.pem") if err != nil { return nil, err } certPool := x509.NewCertPool() if !certPool.AppendCertsFromPEM(pemServerCA) { return nil, fmt.Errorf("failed to add server CA's certificate") } // Load client's certificate and private key clientCert, err := tls.LoadX509KeyPair("cert/client-cert.pem", "cert/client-key.pem") if err != nil { return nil, err } // Create the credentials and return it config := &tls.Config{ Certificates: []tls.Certificate{clientCert}, RootCAs: certPool, } return credentials.NewTLS(config), nil } func main() { serverAddress := flag.String("address", "", "the server address") enableTLS := flag.Bool("tls", false, "enable SSL/TLS") flag.Parse() log.Printf("dial server %s, TLS = %t", *serverAddress, *enableTLS) transportOption := grpc.WithInsecure() if *enableTLS { tlsCredentials, err := loadTLSCredentials() if err != nil { log.Fatal("cannot load TLS credentials: ", err) } transportOption = grpc.WithTransportCredentials(tlsCredentials) } cc1, err := grpc.Dial(*serverAddress, transportOption) if err != nil { log.Fatal("cannot dial server: ", err) } authClient := client.NewAuthClient(cc1, username, password) interceptor, err := client.NewAuthInterceptor(authClient, authMethods(), refreshDuration) if err != nil { log.Fatal("cannot create auth interceptor: ", err) } cc2, err := grpc.Dial( *serverAddress, transportOption, grpc.WithUnaryInterceptor(interceptor.Unary()), grpc.WithStreamInterceptor(interceptor.Stream()), ) if err != nil { log.Fatal("cannot dial server: ", err) } laptopClient := client.NewLaptopClient(cc2) testRateLaptop(laptopClient) } ================================================ FILE: cmd/server/main.go ================================================ package main import ( "context" "crypto/tls" "crypto/x509" "flag" "fmt" "io/ioutil" "log" "net" "net/http" "time" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "gitlab.com/techschool/pcbook/pb" "gitlab.com/techschool/pcbook/service" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/reflection" ) func seedUsers(userStore service.UserStore) error { err := createUser(userStore, "admin1", "secret", "admin") if err != nil { return err } return createUser(userStore, "user1", "secret", "user") } func createUser(userStore service.UserStore, username, password, role string) error { user, err := service.NewUser(username, password, role) if err != nil { return err } return userStore.Save(user) } const ( secretKey = "secret" tokenDuration = 15 * time.Minute ) const ( serverCertFile = "cert/server-cert.pem" serverKeyFile = "cert/server-key.pem" clientCACertFile = "cert/ca-cert.pem" ) func accessibleRoles() map[string][]string { const laptopServicePath = "/techschool.pcbook.LaptopService/" return map[string][]string{ laptopServicePath + "CreateLaptop": {"admin"}, laptopServicePath + "UploadImage": {"admin"}, laptopServicePath + "RateLaptop": {"admin", "user"}, } } func loadTLSCredentials() (credentials.TransportCredentials, error) { // Load certificate of the CA who signed client's certificate pemClientCA, err := ioutil.ReadFile(clientCACertFile) if err != nil { return nil, err } certPool := x509.NewCertPool() if !certPool.AppendCertsFromPEM(pemClientCA) { return nil, fmt.Errorf("failed to add client CA's certificate") } // Load server's certificate and private key serverCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile) if err != nil { return nil, err } // Create the credentials and return it config := &tls.Config{ Certificates: []tls.Certificate{serverCert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: certPool, } return credentials.NewTLS(config), nil } func runGRPCServer( authServer pb.AuthServiceServer, laptopServer pb.LaptopServiceServer, jwtManager *service.JWTManager, enableTLS bool, listener net.Listener, ) error { interceptor := service.NewAuthInterceptor(jwtManager, accessibleRoles()) serverOptions := []grpc.ServerOption{ grpc.UnaryInterceptor(interceptor.Unary()), grpc.StreamInterceptor(interceptor.Stream()), } if enableTLS { tlsCredentials, err := loadTLSCredentials() if err != nil { return fmt.Errorf("cannot load TLS credentials: %w", err) } serverOptions = append(serverOptions, grpc.Creds(tlsCredentials)) } grpcServer := grpc.NewServer(serverOptions...) pb.RegisterAuthServiceServer(grpcServer, authServer) pb.RegisterLaptopServiceServer(grpcServer, laptopServer) reflection.Register(grpcServer) log.Printf("Start GRPC server at %s, TLS = %t", listener.Addr().String(), enableTLS) return grpcServer.Serve(listener) } func runRESTServer( authServer pb.AuthServiceServer, laptopServer pb.LaptopServiceServer, jwtManager *service.JWTManager, enableTLS bool, listener net.Listener, grpcEndpoint string, ) error { mux := runtime.NewServeMux() dialOptions := []grpc.DialOption{grpc.WithInsecure()} ctx, cancel := context.WithCancel(context.Background()) defer cancel() // in-process handler // err := pb.RegisterAuthServiceHandlerServer(ctx, mux, authServer) err := pb.RegisterAuthServiceHandlerFromEndpoint(ctx, mux, grpcEndpoint, dialOptions) if err != nil { return err } // in-process handler // err = pb.RegisterLaptopServiceHandlerServer(ctx, mux, laptopServer) err = pb.RegisterLaptopServiceHandlerFromEndpoint(ctx, mux, grpcEndpoint, dialOptions) if err != nil { return err } log.Printf("Start REST server at %s, TLS = %t", listener.Addr().String(), enableTLS) if enableTLS { return http.ServeTLS(listener, mux, serverCertFile, serverKeyFile) } return http.Serve(listener, mux) } func main() { port := flag.Int("port", 0, "the server port") enableTLS := flag.Bool("tls", false, "enable SSL/TLS") serverType := flag.String("type", "grpc", "type of server (grpc/rest)") endPoint := flag.String("endpoint", "", "gRPC endpoint") flag.Parse() userStore := service.NewInMemoryUserStore() err := seedUsers(userStore) if err != nil { log.Fatal("cannot seed users: ", err) } jwtManager := service.NewJWTManager(secretKey, tokenDuration) authServer := service.NewAuthServer(userStore, jwtManager) laptopStore := service.NewInMemoryLaptopStore() imageStore := service.NewDiskImageStore("img") ratingStore := service.NewInMemoryRatingStore() laptopServer := service.NewLaptopServer(laptopStore, imageStore, ratingStore) address := fmt.Sprintf("0.0.0.0:%d", *port) listener, err := net.Listen("tcp", address) if err != nil { log.Fatal("cannot start server: ", err) } if *serverType == "grpc" { err = runGRPCServer(authServer, laptopServer, jwtManager, *enableTLS, listener) } else { err = runRESTServer(authServer, laptopServer, jwtManager, *enableTLS, listener, *endPoint) } if err != nil { log.Fatal("cannot start server: ", err) } } ================================================ FILE: go.mod ================================================ module gitlab.com/techschool/pcbook go 1.15 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/golang/protobuf v1.4.3 github.com/google/uuid v1.2.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.1.0 github.com/jinzhu/copier v0.2.3 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119 google.golang.org/grpc v1.35.0 google.golang.org/protobuf v1.25.0 ) ================================================ FILE: go.sum ================================================ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.1.0 h1:EhTvIsn53GrBLl45YVHk25cUHQHwlJfq2y8b7W5IpVY= github.com/grpc-ecosystem/grpc-gateway/v2 v2.1.0/go.mod h1:ly5QWKtiqC7tGfzgXYtpoZYmEWx5Z82/b18ASEL+yGc= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jinzhu/copier v0.2.3 h1:Oe09ju+9qft7TffZ7l/04AB2f8u1+V4ZMxmp/nnqeOs= github.com/jinzhu/copier v0.2.3/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24 h1:R8bzl0244nw47n1xKs1MUMAaTNgjavKcN/aX2Ss3+Fo= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210106152847-07624b53cd92 h1:jOTk2Z6KYaWoptUFqZ167cS8peoUPjFEXrsqfVkkCGc= google.golang.org/genproto v0.0.0-20210106152847-07624b53cd92/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119 h1:m9+RjTMas6brUP8DBxSAa/WIPFy7FIhKpvk+9Ppce8E= google.golang.org/genproto v0.0.0-20210202153253-cf70463f6119/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= ================================================ FILE: img/.gitkeep ================================================ ================================================ FILE: nginx.conf ================================================ worker_processes 1; error_log /usr/local/var/log/nginx/error.log; events { worker_connections 10; } http { access_log /usr/local/var/log/nginx/access.log; upstream auth_services { server 0.0.0.0:50051; server 0.0.0.0:50052; } upstream laptop_services { server 0.0.0.0:50051; server 0.0.0.0:50052; } server { listen 8080 ssl http2; # Mutual TLS between gRPC client and nginx ssl_certificate cert/server-cert.pem; ssl_certificate_key cert/server-key.pem; ssl_client_certificate cert/ca-cert.pem; ssl_verify_client on; location /techschool.pcbook.AuthService { grpc_pass grpcs://auth_services; # Mutual TLS between nginx and gRPC server grpc_ssl_certificate cert/server-cert.pem; grpc_ssl_certificate_key cert/server-key.pem; } location /techschool.pcbook.LaptopService { grpc_pass grpcs://laptop_services; # Mutual TLS between nginx and gRPC server grpc_ssl_certificate cert/server-cert.pem; grpc_ssl_certificate_key cert/server-key.pem; } } } ================================================ FILE: pb/auth_service.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: auth_service.proto package pb import ( proto "github.com/golang/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type LoginRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` } func (x *LoginRequest) Reset() { *x = LoginRequest{} if protoimpl.UnsafeEnabled { mi := &file_auth_service_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *LoginRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*LoginRequest) ProtoMessage() {} func (x *LoginRequest) ProtoReflect() protoreflect.Message { mi := &file_auth_service_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead. func (*LoginRequest) Descriptor() ([]byte, []int) { return file_auth_service_proto_rawDescGZIP(), []int{0} } func (x *LoginRequest) GetUsername() string { if x != nil { return x.Username } return "" } func (x *LoginRequest) GetPassword() string { if x != nil { return x.Password } return "" } type LoginResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` } func (x *LoginResponse) Reset() { *x = LoginResponse{} if protoimpl.UnsafeEnabled { mi := &file_auth_service_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *LoginResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*LoginResponse) ProtoMessage() {} func (x *LoginResponse) ProtoReflect() protoreflect.Message { mi := &file_auth_service_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead. func (*LoginResponse) Descriptor() ([]byte, []int) { return file_auth_service_proto_rawDescGZIP(), []int{1} } func (x *LoginResponse) GetAccessToken() string { if x != nil { return x.AccessToken } return "" } var File_auth_service_proto protoreflect.FileDescriptor var file_auth_service_proto_rawDesc = []byte{ 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x46, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x32, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x74, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x65, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1f, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x3a, 0x01, 0x2a, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_auth_service_proto_rawDescOnce sync.Once file_auth_service_proto_rawDescData = file_auth_service_proto_rawDesc ) func file_auth_service_proto_rawDescGZIP() []byte { file_auth_service_proto_rawDescOnce.Do(func() { file_auth_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_auth_service_proto_rawDescData) }) return file_auth_service_proto_rawDescData } var file_auth_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_auth_service_proto_goTypes = []interface{}{ (*LoginRequest)(nil), // 0: techschool.pcbook.LoginRequest (*LoginResponse)(nil), // 1: techschool.pcbook.LoginResponse } var file_auth_service_proto_depIdxs = []int32{ 0, // 0: techschool.pcbook.AuthService.Login:input_type -> techschool.pcbook.LoginRequest 1, // 1: techschool.pcbook.AuthService.Login:output_type -> techschool.pcbook.LoginResponse 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_auth_service_proto_init() } func file_auth_service_proto_init() { if File_auth_service_proto != nil { return } if !protoimpl.UnsafeEnabled { file_auth_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LoginRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_auth_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LoginResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_auth_service_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_auth_service_proto_goTypes, DependencyIndexes: file_auth_service_proto_depIdxs, MessageInfos: file_auth_service_proto_msgTypes, }.Build() File_auth_service_proto = out.File file_auth_service_proto_rawDesc = nil file_auth_service_proto_goTypes = nil file_auth_service_proto_depIdxs = nil } ================================================ FILE: pb/auth_service.pb.gw.go ================================================ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. // source: auth_service.proto /* Package pb is a reverse proxy. It translates gRPC into RESTful JSON APIs. */ package pb import ( "context" "io" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" ) // Suppress "imported and not used" errors var _ codes.Code var _ io.Reader var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = metadata.Join func request_AuthService_Login_0(ctx context.Context, marshaler runtime.Marshaler, client AuthServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq LoginRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.Login(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func local_request_AuthService_Login_0(ctx context.Context, marshaler runtime.Marshaler, server AuthServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq LoginRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.Login(ctx, &protoReq) return msg, metadata, err } // RegisterAuthServiceHandlerServer registers the http handlers for service AuthService to "mux". // UnaryRPC :call AuthServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterAuthServiceHandlerFromEndpoint instead. func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server AuthServiceServer) error { mux.Handle("POST", pattern_AuthService_Login_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/techschool.pcbook.AuthService/Login") if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_AuthService_Login_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_AuthService_Login_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } // RegisterAuthServiceHandlerFromEndpoint is same as RegisterAuthServiceHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterAuthServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { conn, err := grpc.Dial(endpoint, opts...) if err != nil { return err } defer func() { if err != nil { if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } return } go func() { <-ctx.Done() if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } }() }() return RegisterAuthServiceHandler(ctx, mux, conn) } // RegisterAuthServiceHandler registers the http handlers for service AuthService to "mux". // The handlers forward requests to the grpc endpoint over "conn". func RegisterAuthServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { return RegisterAuthServiceHandlerClient(ctx, mux, NewAuthServiceClient(conn)) } // RegisterAuthServiceHandlerClient registers the http handlers for service AuthService // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "AuthServiceClient". // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AuthServiceClient" // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in // "AuthServiceClient" to call the correct interceptors. func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AuthServiceClient) error { mux.Handle("POST", pattern_AuthService_Login_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req, "/techschool.pcbook.AuthService/Login") if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_AuthService_Login_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_AuthService_Login_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } var ( pattern_AuthService_Login_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "auth", "login"}, "")) ) var ( forward_AuthService_Login_0 = runtime.ForwardResponseMessage ) ================================================ FILE: pb/auth_service_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. package pb import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // AuthServiceClient is the client API for AuthService service. // // 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. type AuthServiceClient interface { Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) } type authServiceClient struct { cc grpc.ClientConnInterface } func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient { return &authServiceClient{cc} } func (c *authServiceClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { out := new(LoginResponse) err := c.cc.Invoke(ctx, "/techschool.pcbook.AuthService/Login", in, out, opts...) if err != nil { return nil, err } return out, nil } // AuthServiceServer is the server API for AuthService service. // All implementations must embed UnimplementedAuthServiceServer // for forward compatibility type AuthServiceServer interface { Login(context.Context, *LoginRequest) (*LoginResponse, error) mustEmbedUnimplementedAuthServiceServer() } // UnimplementedAuthServiceServer must be embedded to have forward compatible implementations. type UnimplementedAuthServiceServer struct { } func (UnimplementedAuthServiceServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") } func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {} // UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to AuthServiceServer will // result in compilation errors. type UnsafeAuthServiceServer interface { mustEmbedUnimplementedAuthServiceServer() } func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) { s.RegisterService(&AuthService_ServiceDesc, srv) } func _AuthService_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(LoginRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(AuthServiceServer).Login(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/techschool.pcbook.AuthService/Login", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(AuthServiceServer).Login(ctx, req.(*LoginRequest)) } return interceptor(ctx, in, info, handler) } // AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var AuthService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "techschool.pcbook.AuthService", HandlerType: (*AuthServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "Login", Handler: _AuthService_Login_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "auth_service.proto", } ================================================ FILE: pb/filter_message.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: filter_message.proto package pb import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type Filter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields MaxPriceUsd float64 `protobuf:"fixed64,1,opt,name=max_price_usd,json=maxPriceUsd,proto3" json:"max_price_usd,omitempty"` MinCpuCores uint32 `protobuf:"varint,2,opt,name=min_cpu_cores,json=minCpuCores,proto3" json:"min_cpu_cores,omitempty"` MinCpuGhz float64 `protobuf:"fixed64,3,opt,name=min_cpu_ghz,json=minCpuGhz,proto3" json:"min_cpu_ghz,omitempty"` MinRam *Memory `protobuf:"bytes,4,opt,name=min_ram,json=minRam,proto3" json:"min_ram,omitempty"` } func (x *Filter) Reset() { *x = Filter{} if protoimpl.UnsafeEnabled { mi := &file_filter_message_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Filter) String() string { return protoimpl.X.MessageStringOf(x) } func (*Filter) ProtoMessage() {} func (x *Filter) ProtoReflect() protoreflect.Message { mi := &file_filter_message_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Filter.ProtoReflect.Descriptor instead. func (*Filter) Descriptor() ([]byte, []int) { return file_filter_message_proto_rawDescGZIP(), []int{0} } func (x *Filter) GetMaxPriceUsd() float64 { if x != nil { return x.MaxPriceUsd } return 0 } func (x *Filter) GetMinCpuCores() uint32 { if x != nil { return x.MinCpuCores } return 0 } func (x *Filter) GetMinCpuGhz() float64 { if x != nil { return x.MinCpuGhz } return 0 } func (x *Filter) GetMinRam() *Memory { if x != nil { return x.MinRam } return nil } var File_filter_message_proto protoreflect.FileDescriptor var file_filter_message_proto_rawDesc = []byte{ 0x0a, 0x14, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x1a, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa4, 0x01, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x73, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x69, 0x63, 0x65, 0x55, 0x73, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x43, 0x70, 0x75, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x67, 0x68, 0x7a, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x43, 0x70, 0x75, 0x47, 0x68, 0x7a, 0x12, 0x32, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x61, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x52, 0x61, 0x6d, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_filter_message_proto_rawDescOnce sync.Once file_filter_message_proto_rawDescData = file_filter_message_proto_rawDesc ) func file_filter_message_proto_rawDescGZIP() []byte { file_filter_message_proto_rawDescOnce.Do(func() { file_filter_message_proto_rawDescData = protoimpl.X.CompressGZIP(file_filter_message_proto_rawDescData) }) return file_filter_message_proto_rawDescData } var file_filter_message_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_filter_message_proto_goTypes = []interface{}{ (*Filter)(nil), // 0: techschool.pcbook.Filter (*Memory)(nil), // 1: techschool.pcbook.Memory } var file_filter_message_proto_depIdxs = []int32{ 1, // 0: techschool.pcbook.Filter.min_ram:type_name -> techschool.pcbook.Memory 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_filter_message_proto_init() } func file_filter_message_proto_init() { if File_filter_message_proto != nil { return } file_memory_message_proto_init() if !protoimpl.UnsafeEnabled { file_filter_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Filter); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_filter_message_proto_rawDesc, NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_filter_message_proto_goTypes, DependencyIndexes: file_filter_message_proto_depIdxs, MessageInfos: file_filter_message_proto_msgTypes, }.Build() File_filter_message_proto = out.File file_filter_message_proto_rawDesc = nil file_filter_message_proto_goTypes = nil file_filter_message_proto_depIdxs = nil } ================================================ FILE: pb/keyboard_message.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: keyboard_message.proto package pb import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type Keyboard_Layout int32 const ( Keyboard_UNKNOWN Keyboard_Layout = 0 Keyboard_QWERTY Keyboard_Layout = 1 Keyboard_QWERTZ Keyboard_Layout = 2 Keyboard_AZERTY Keyboard_Layout = 3 ) // Enum value maps for Keyboard_Layout. var ( Keyboard_Layout_name = map[int32]string{ 0: "UNKNOWN", 1: "QWERTY", 2: "QWERTZ", 3: "AZERTY", } Keyboard_Layout_value = map[string]int32{ "UNKNOWN": 0, "QWERTY": 1, "QWERTZ": 2, "AZERTY": 3, } ) func (x Keyboard_Layout) Enum() *Keyboard_Layout { p := new(Keyboard_Layout) *p = x return p } func (x Keyboard_Layout) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Keyboard_Layout) Descriptor() protoreflect.EnumDescriptor { return file_keyboard_message_proto_enumTypes[0].Descriptor() } func (Keyboard_Layout) Type() protoreflect.EnumType { return &file_keyboard_message_proto_enumTypes[0] } func (x Keyboard_Layout) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Keyboard_Layout.Descriptor instead. func (Keyboard_Layout) EnumDescriptor() ([]byte, []int) { return file_keyboard_message_proto_rawDescGZIP(), []int{0, 0} } type Keyboard struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Layout Keyboard_Layout `protobuf:"varint,1,opt,name=layout,proto3,enum=techschool.pcbook.Keyboard_Layout" json:"layout,omitempty"` Backlit bool `protobuf:"varint,2,opt,name=backlit,proto3" json:"backlit,omitempty"` } func (x *Keyboard) Reset() { *x = Keyboard{} if protoimpl.UnsafeEnabled { mi := &file_keyboard_message_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Keyboard) String() string { return protoimpl.X.MessageStringOf(x) } func (*Keyboard) ProtoMessage() {} func (x *Keyboard) ProtoReflect() protoreflect.Message { mi := &file_keyboard_message_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Keyboard.ProtoReflect.Descriptor instead. func (*Keyboard) Descriptor() ([]byte, []int) { return file_keyboard_message_proto_rawDescGZIP(), []int{0} } func (x *Keyboard) GetLayout() Keyboard_Layout { if x != nil { return x.Layout } return Keyboard_UNKNOWN } func (x *Keyboard) GetBacklit() bool { if x != nil { return x.Backlit } return false } var File_keyboard_message_proto protoreflect.FileDescriptor var file_keyboard_message_proto_rawDesc = []byte{ 0x0a, 0x16, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x22, 0x9b, 0x01, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x12, 0x3a, 0x0a, 0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x2e, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x06, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6c, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x6c, 0x69, 0x74, 0x22, 0x39, 0x0a, 0x06, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x51, 0x57, 0x45, 0x52, 0x54, 0x5a, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x5a, 0x45, 0x52, 0x54, 0x59, 0x10, 0x03, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_keyboard_message_proto_rawDescOnce sync.Once file_keyboard_message_proto_rawDescData = file_keyboard_message_proto_rawDesc ) func file_keyboard_message_proto_rawDescGZIP() []byte { file_keyboard_message_proto_rawDescOnce.Do(func() { file_keyboard_message_proto_rawDescData = protoimpl.X.CompressGZIP(file_keyboard_message_proto_rawDescData) }) return file_keyboard_message_proto_rawDescData } var file_keyboard_message_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_keyboard_message_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_keyboard_message_proto_goTypes = []interface{}{ (Keyboard_Layout)(0), // 0: techschool.pcbook.Keyboard.Layout (*Keyboard)(nil), // 1: techschool.pcbook.Keyboard } var file_keyboard_message_proto_depIdxs = []int32{ 0, // 0: techschool.pcbook.Keyboard.layout:type_name -> techschool.pcbook.Keyboard.Layout 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_keyboard_message_proto_init() } func file_keyboard_message_proto_init() { if File_keyboard_message_proto != nil { return } if !protoimpl.UnsafeEnabled { file_keyboard_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Keyboard); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_keyboard_message_proto_rawDesc, NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_keyboard_message_proto_goTypes, DependencyIndexes: file_keyboard_message_proto_depIdxs, EnumInfos: file_keyboard_message_proto_enumTypes, MessageInfos: file_keyboard_message_proto_msgTypes, }.Build() File_keyboard_message_proto = out.File file_keyboard_message_proto_rawDesc = nil file_keyboard_message_proto_goTypes = nil file_keyboard_message_proto_depIdxs = nil } ================================================ FILE: pb/laptop_message.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: laptop_message.proto package pb import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type Laptop struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Brand string `protobuf:"bytes,2,opt,name=brand,proto3" json:"brand,omitempty"` Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` Cpu *CPU `protobuf:"bytes,4,opt,name=cpu,proto3" json:"cpu,omitempty"` Ram *Memory `protobuf:"bytes,5,opt,name=ram,proto3" json:"ram,omitempty"` Gpus []*GPU `protobuf:"bytes,6,rep,name=gpus,proto3" json:"gpus,omitempty"` Storages []*Storage `protobuf:"bytes,7,rep,name=storages,proto3" json:"storages,omitempty"` Screen *Screen `protobuf:"bytes,8,opt,name=screen,proto3" json:"screen,omitempty"` Keyboard *Keyboard `protobuf:"bytes,9,opt,name=keyboard,proto3" json:"keyboard,omitempty"` // Types that are assignable to Weight: // *Laptop_WeightKg // *Laptop_WeightLb Weight isLaptop_Weight `protobuf_oneof:"weight"` PriceUsd float64 `protobuf:"fixed64,12,opt,name=price_usd,json=priceUsd,proto3" json:"price_usd,omitempty"` ReleaseYear uint32 `protobuf:"varint,13,opt,name=release_year,json=releaseYear,proto3" json:"release_year,omitempty"` UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` } func (x *Laptop) Reset() { *x = Laptop{} if protoimpl.UnsafeEnabled { mi := &file_laptop_message_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Laptop) String() string { return protoimpl.X.MessageStringOf(x) } func (*Laptop) ProtoMessage() {} func (x *Laptop) ProtoReflect() protoreflect.Message { mi := &file_laptop_message_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Laptop.ProtoReflect.Descriptor instead. func (*Laptop) Descriptor() ([]byte, []int) { return file_laptop_message_proto_rawDescGZIP(), []int{0} } func (x *Laptop) GetId() string { if x != nil { return x.Id } return "" } func (x *Laptop) GetBrand() string { if x != nil { return x.Brand } return "" } func (x *Laptop) GetName() string { if x != nil { return x.Name } return "" } func (x *Laptop) GetCpu() *CPU { if x != nil { return x.Cpu } return nil } func (x *Laptop) GetRam() *Memory { if x != nil { return x.Ram } return nil } func (x *Laptop) GetGpus() []*GPU { if x != nil { return x.Gpus } return nil } func (x *Laptop) GetStorages() []*Storage { if x != nil { return x.Storages } return nil } func (x *Laptop) GetScreen() *Screen { if x != nil { return x.Screen } return nil } func (x *Laptop) GetKeyboard() *Keyboard { if x != nil { return x.Keyboard } return nil } func (m *Laptop) GetWeight() isLaptop_Weight { if m != nil { return m.Weight } return nil } func (x *Laptop) GetWeightKg() float64 { if x, ok := x.GetWeight().(*Laptop_WeightKg); ok { return x.WeightKg } return 0 } func (x *Laptop) GetWeightLb() float64 { if x, ok := x.GetWeight().(*Laptop_WeightLb); ok { return x.WeightLb } return 0 } func (x *Laptop) GetPriceUsd() float64 { if x != nil { return x.PriceUsd } return 0 } func (x *Laptop) GetReleaseYear() uint32 { if x != nil { return x.ReleaseYear } return 0 } func (x *Laptop) GetUpdatedAt() *timestamppb.Timestamp { if x != nil { return x.UpdatedAt } return nil } type isLaptop_Weight interface { isLaptop_Weight() } type Laptop_WeightKg struct { WeightKg float64 `protobuf:"fixed64,10,opt,name=weight_kg,json=weightKg,proto3,oneof"` } type Laptop_WeightLb struct { WeightLb float64 `protobuf:"fixed64,11,opt,name=weight_lb,json=weightLb,proto3,oneof"` } func (*Laptop_WeightKg) isLaptop_Weight() {} func (*Laptop_WeightLb) isLaptop_Weight() {} var File_laptop_message_proto protoreflect.FileDescriptor var file_laptop_message_proto_rawDesc = []byte{ 0x0a, 0x14, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x1a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xac, 0x04, 0x0a, 0x06, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x43, 0x50, 0x55, 0x52, 0x03, 0x63, 0x70, 0x75, 0x12, 0x2b, 0x0a, 0x03, 0x72, 0x61, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x03, 0x72, 0x61, 0x6d, 0x12, 0x2a, 0x0a, 0x04, 0x67, 0x70, 0x75, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x47, 0x50, 0x55, 0x52, 0x04, 0x67, 0x70, 0x75, 0x73, 0x12, 0x36, 0x0a, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x52, 0x06, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x12, 0x37, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x12, 0x1d, 0x0a, 0x09, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6b, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x08, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4b, 0x67, 0x12, 0x1d, 0x0a, 0x09, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x08, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x62, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x73, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x70, 0x72, 0x69, 0x63, 0x65, 0x55, 0x73, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_laptop_message_proto_rawDescOnce sync.Once file_laptop_message_proto_rawDescData = file_laptop_message_proto_rawDesc ) func file_laptop_message_proto_rawDescGZIP() []byte { file_laptop_message_proto_rawDescOnce.Do(func() { file_laptop_message_proto_rawDescData = protoimpl.X.CompressGZIP(file_laptop_message_proto_rawDescData) }) return file_laptop_message_proto_rawDescData } var file_laptop_message_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_laptop_message_proto_goTypes = []interface{}{ (*Laptop)(nil), // 0: techschool.pcbook.Laptop (*CPU)(nil), // 1: techschool.pcbook.CPU (*Memory)(nil), // 2: techschool.pcbook.Memory (*GPU)(nil), // 3: techschool.pcbook.GPU (*Storage)(nil), // 4: techschool.pcbook.Storage (*Screen)(nil), // 5: techschool.pcbook.Screen (*Keyboard)(nil), // 6: techschool.pcbook.Keyboard (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp } var file_laptop_message_proto_depIdxs = []int32{ 1, // 0: techschool.pcbook.Laptop.cpu:type_name -> techschool.pcbook.CPU 2, // 1: techschool.pcbook.Laptop.ram:type_name -> techschool.pcbook.Memory 3, // 2: techschool.pcbook.Laptop.gpus:type_name -> techschool.pcbook.GPU 4, // 3: techschool.pcbook.Laptop.storages:type_name -> techschool.pcbook.Storage 5, // 4: techschool.pcbook.Laptop.screen:type_name -> techschool.pcbook.Screen 6, // 5: techschool.pcbook.Laptop.keyboard:type_name -> techschool.pcbook.Keyboard 7, // 6: techschool.pcbook.Laptop.updated_at:type_name -> google.protobuf.Timestamp 7, // [7:7] is the sub-list for method output_type 7, // [7:7] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name } func init() { file_laptop_message_proto_init() } func file_laptop_message_proto_init() { if File_laptop_message_proto != nil { return } file_processor_message_proto_init() file_memory_message_proto_init() file_storage_message_proto_init() file_screen_message_proto_init() file_keyboard_message_proto_init() if !protoimpl.UnsafeEnabled { file_laptop_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Laptop); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } file_laptop_message_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Laptop_WeightKg)(nil), (*Laptop_WeightLb)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_laptop_message_proto_rawDesc, NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_laptop_message_proto_goTypes, DependencyIndexes: file_laptop_message_proto_depIdxs, MessageInfos: file_laptop_message_proto_msgTypes, }.Build() File_laptop_message_proto = out.File file_laptop_message_proto_rawDesc = nil file_laptop_message_proto_goTypes = nil file_laptop_message_proto_depIdxs = nil } ================================================ FILE: pb/laptop_service.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: laptop_service.proto package pb import ( proto "github.com/golang/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type CreateLaptopRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Laptop *Laptop `protobuf:"bytes,1,opt,name=laptop,proto3" json:"laptop,omitempty"` } func (x *CreateLaptopRequest) Reset() { *x = CreateLaptopRequest{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CreateLaptopRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CreateLaptopRequest) ProtoMessage() {} func (x *CreateLaptopRequest) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CreateLaptopRequest.ProtoReflect.Descriptor instead. func (*CreateLaptopRequest) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{0} } func (x *CreateLaptopRequest) GetLaptop() *Laptop { if x != nil { return x.Laptop } return nil } type CreateLaptopResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` } func (x *CreateLaptopResponse) Reset() { *x = CreateLaptopResponse{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CreateLaptopResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*CreateLaptopResponse) ProtoMessage() {} func (x *CreateLaptopResponse) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CreateLaptopResponse.ProtoReflect.Descriptor instead. func (*CreateLaptopResponse) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{1} } func (x *CreateLaptopResponse) GetId() string { if x != nil { return x.Id } return "" } type SearchLaptopRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Filter *Filter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` } func (x *SearchLaptopRequest) Reset() { *x = SearchLaptopRequest{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SearchLaptopRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*SearchLaptopRequest) ProtoMessage() {} func (x *SearchLaptopRequest) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SearchLaptopRequest.ProtoReflect.Descriptor instead. func (*SearchLaptopRequest) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{2} } func (x *SearchLaptopRequest) GetFilter() *Filter { if x != nil { return x.Filter } return nil } type SearchLaptopResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Laptop *Laptop `protobuf:"bytes,1,opt,name=laptop,proto3" json:"laptop,omitempty"` } func (x *SearchLaptopResponse) Reset() { *x = SearchLaptopResponse{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SearchLaptopResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*SearchLaptopResponse) ProtoMessage() {} func (x *SearchLaptopResponse) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SearchLaptopResponse.ProtoReflect.Descriptor instead. func (*SearchLaptopResponse) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{3} } func (x *SearchLaptopResponse) GetLaptop() *Laptop { if x != nil { return x.Laptop } return nil } type UploadImageRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Data: // *UploadImageRequest_Info // *UploadImageRequest_ChunkData Data isUploadImageRequest_Data `protobuf_oneof:"data"` } func (x *UploadImageRequest) Reset() { *x = UploadImageRequest{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *UploadImageRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UploadImageRequest) ProtoMessage() {} func (x *UploadImageRequest) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UploadImageRequest.ProtoReflect.Descriptor instead. func (*UploadImageRequest) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{4} } func (m *UploadImageRequest) GetData() isUploadImageRequest_Data { if m != nil { return m.Data } return nil } func (x *UploadImageRequest) GetInfo() *ImageInfo { if x, ok := x.GetData().(*UploadImageRequest_Info); ok { return x.Info } return nil } func (x *UploadImageRequest) GetChunkData() []byte { if x, ok := x.GetData().(*UploadImageRequest_ChunkData); ok { return x.ChunkData } return nil } type isUploadImageRequest_Data interface { isUploadImageRequest_Data() } type UploadImageRequest_Info struct { Info *ImageInfo `protobuf:"bytes,1,opt,name=info,proto3,oneof"` } type UploadImageRequest_ChunkData struct { ChunkData []byte `protobuf:"bytes,2,opt,name=chunk_data,json=chunkData,proto3,oneof"` } func (*UploadImageRequest_Info) isUploadImageRequest_Data() {} func (*UploadImageRequest_ChunkData) isUploadImageRequest_Data() {} type ImageInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LaptopId string `protobuf:"bytes,1,opt,name=laptop_id,json=laptopId,proto3" json:"laptop_id,omitempty"` ImageType string `protobuf:"bytes,2,opt,name=image_type,json=imageType,proto3" json:"image_type,omitempty"` } func (x *ImageInfo) Reset() { *x = ImageInfo{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ImageInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageInfo) ProtoMessage() {} func (x *ImageInfo) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageInfo.ProtoReflect.Descriptor instead. func (*ImageInfo) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{5} } func (x *ImageInfo) GetLaptopId() string { if x != nil { return x.LaptopId } return "" } func (x *ImageInfo) GetImageType() string { if x != nil { return x.ImageType } return "" } type UploadImageResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Size uint32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` } func (x *UploadImageResponse) Reset() { *x = UploadImageResponse{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *UploadImageResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*UploadImageResponse) ProtoMessage() {} func (x *UploadImageResponse) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UploadImageResponse.ProtoReflect.Descriptor instead. func (*UploadImageResponse) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{6} } func (x *UploadImageResponse) GetId() string { if x != nil { return x.Id } return "" } func (x *UploadImageResponse) GetSize() uint32 { if x != nil { return x.Size } return 0 } type RateLaptopRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LaptopId string `protobuf:"bytes,1,opt,name=laptop_id,json=laptopId,proto3" json:"laptop_id,omitempty"` Score float64 `protobuf:"fixed64,2,opt,name=score,proto3" json:"score,omitempty"` } func (x *RateLaptopRequest) Reset() { *x = RateLaptopRequest{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RateLaptopRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RateLaptopRequest) ProtoMessage() {} func (x *RateLaptopRequest) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RateLaptopRequest.ProtoReflect.Descriptor instead. func (*RateLaptopRequest) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{7} } func (x *RateLaptopRequest) GetLaptopId() string { if x != nil { return x.LaptopId } return "" } func (x *RateLaptopRequest) GetScore() float64 { if x != nil { return x.Score } return 0 } type RateLaptopResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LaptopId string `protobuf:"bytes,1,opt,name=laptop_id,json=laptopId,proto3" json:"laptop_id,omitempty"` RatedCount uint32 `protobuf:"varint,2,opt,name=rated_count,json=ratedCount,proto3" json:"rated_count,omitempty"` AverageScore float64 `protobuf:"fixed64,3,opt,name=average_score,json=averageScore,proto3" json:"average_score,omitempty"` } func (x *RateLaptopResponse) Reset() { *x = RateLaptopResponse{} if protoimpl.UnsafeEnabled { mi := &file_laptop_service_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RateLaptopResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*RateLaptopResponse) ProtoMessage() {} func (x *RateLaptopResponse) ProtoReflect() protoreflect.Message { mi := &file_laptop_service_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RateLaptopResponse.ProtoReflect.Descriptor instead. func (*RateLaptopResponse) Descriptor() ([]byte, []int) { return file_laptop_service_proto_rawDescGZIP(), []int{8} } func (x *RateLaptopResponse) GetLaptopId() string { if x != nil { return x.LaptopId } return "" } func (x *RateLaptopResponse) GetRatedCount() uint32 { if x != nil { return x.RatedCount } return 0 } func (x *RateLaptopResponse) GetAverageScore() float64 { if x != nil { return x.AverageScore } return 0 } var File_laptop_service_proto protoreflect.FileDescriptor var file_laptop_service_proto_rawDesc = []byte{ 0x0a, 0x14, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x1a, 0x14, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x48, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x06, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x22, 0x26, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x48, 0x0a, 0x13, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x49, 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x06, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x22, 0x71, 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x44, 0x61, 0x74, 0x61, 0x42, 0x06, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x47, 0x0a, 0x09, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x39, 0x0a, 0x13, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x46, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x77, 0x0a, 0x12, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x32, 0x8c, 0x04, 0x0a, 0x0d, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7d, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x12, 0x26, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x2f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x7c, 0x0a, 0x0c, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x12, 0x26, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x30, 0x01, 0x12, 0x82, 0x01, 0x0a, 0x0b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x28, 0x01, 0x12, 0x79, 0x0a, 0x0a, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x12, 0x24, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x2f, 0x72, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x28, 0x01, 0x30, 0x01, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_laptop_service_proto_rawDescOnce sync.Once file_laptop_service_proto_rawDescData = file_laptop_service_proto_rawDesc ) func file_laptop_service_proto_rawDescGZIP() []byte { file_laptop_service_proto_rawDescOnce.Do(func() { file_laptop_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_laptop_service_proto_rawDescData) }) return file_laptop_service_proto_rawDescData } var file_laptop_service_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_laptop_service_proto_goTypes = []interface{}{ (*CreateLaptopRequest)(nil), // 0: techschool.pcbook.CreateLaptopRequest (*CreateLaptopResponse)(nil), // 1: techschool.pcbook.CreateLaptopResponse (*SearchLaptopRequest)(nil), // 2: techschool.pcbook.SearchLaptopRequest (*SearchLaptopResponse)(nil), // 3: techschool.pcbook.SearchLaptopResponse (*UploadImageRequest)(nil), // 4: techschool.pcbook.UploadImageRequest (*ImageInfo)(nil), // 5: techschool.pcbook.ImageInfo (*UploadImageResponse)(nil), // 6: techschool.pcbook.UploadImageResponse (*RateLaptopRequest)(nil), // 7: techschool.pcbook.RateLaptopRequest (*RateLaptopResponse)(nil), // 8: techschool.pcbook.RateLaptopResponse (*Laptop)(nil), // 9: techschool.pcbook.Laptop (*Filter)(nil), // 10: techschool.pcbook.Filter } var file_laptop_service_proto_depIdxs = []int32{ 9, // 0: techschool.pcbook.CreateLaptopRequest.laptop:type_name -> techschool.pcbook.Laptop 10, // 1: techschool.pcbook.SearchLaptopRequest.filter:type_name -> techschool.pcbook.Filter 9, // 2: techschool.pcbook.SearchLaptopResponse.laptop:type_name -> techschool.pcbook.Laptop 5, // 3: techschool.pcbook.UploadImageRequest.info:type_name -> techschool.pcbook.ImageInfo 0, // 4: techschool.pcbook.LaptopService.CreateLaptop:input_type -> techschool.pcbook.CreateLaptopRequest 2, // 5: techschool.pcbook.LaptopService.SearchLaptop:input_type -> techschool.pcbook.SearchLaptopRequest 4, // 6: techschool.pcbook.LaptopService.UploadImage:input_type -> techschool.pcbook.UploadImageRequest 7, // 7: techschool.pcbook.LaptopService.RateLaptop:input_type -> techschool.pcbook.RateLaptopRequest 1, // 8: techschool.pcbook.LaptopService.CreateLaptop:output_type -> techschool.pcbook.CreateLaptopResponse 3, // 9: techschool.pcbook.LaptopService.SearchLaptop:output_type -> techschool.pcbook.SearchLaptopResponse 6, // 10: techschool.pcbook.LaptopService.UploadImage:output_type -> techschool.pcbook.UploadImageResponse 8, // 11: techschool.pcbook.LaptopService.RateLaptop:output_type -> techschool.pcbook.RateLaptopResponse 8, // [8:12] is the sub-list for method output_type 4, // [4:8] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_laptop_service_proto_init() } func file_laptop_service_proto_init() { if File_laptop_service_proto != nil { return } file_laptop_message_proto_init() file_filter_message_proto_init() if !protoimpl.UnsafeEnabled { file_laptop_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateLaptopRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_laptop_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateLaptopResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_laptop_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchLaptopRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_laptop_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchLaptopResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_laptop_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UploadImageRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_laptop_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImageInfo); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_laptop_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UploadImageResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_laptop_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RateLaptopRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_laptop_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RateLaptopResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } file_laptop_service_proto_msgTypes[4].OneofWrappers = []interface{}{ (*UploadImageRequest_Info)(nil), (*UploadImageRequest_ChunkData)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_laptop_service_proto_rawDesc, NumEnums: 0, NumMessages: 9, NumExtensions: 0, NumServices: 1, }, GoTypes: file_laptop_service_proto_goTypes, DependencyIndexes: file_laptop_service_proto_depIdxs, MessageInfos: file_laptop_service_proto_msgTypes, }.Build() File_laptop_service_proto = out.File file_laptop_service_proto_rawDesc = nil file_laptop_service_proto_goTypes = nil file_laptop_service_proto_depIdxs = nil } ================================================ FILE: pb/laptop_service.pb.gw.go ================================================ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. // source: laptop_service.proto /* Package pb is a reverse proxy. It translates gRPC into RESTful JSON APIs. */ package pb import ( "context" "io" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" ) // Suppress "imported and not used" errors var _ codes.Code var _ io.Reader var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray var _ = metadata.Join func request_LaptopService_CreateLaptop_0(ctx context.Context, marshaler runtime.Marshaler, client LaptopServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq CreateLaptopRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.CreateLaptop(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func local_request_LaptopService_CreateLaptop_0(ctx context.Context, marshaler runtime.Marshaler, server LaptopServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq CreateLaptopRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) if berr != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.CreateLaptop(ctx, &protoReq) return msg, metadata, err } var ( filter_LaptopService_SearchLaptop_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) func request_LaptopService_SearchLaptop_0(ctx context.Context, marshaler runtime.Marshaler, client LaptopServiceClient, req *http.Request, pathParams map[string]string) (LaptopService_SearchLaptopClient, runtime.ServerMetadata, error) { var protoReq SearchLaptopRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_LaptopService_SearchLaptop_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } stream, err := client.SearchLaptop(ctx, &protoReq) if err != nil { return nil, metadata, err } header, err := stream.Header() if err != nil { return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } func request_LaptopService_UploadImage_0(ctx context.Context, marshaler runtime.Marshaler, client LaptopServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var metadata runtime.ServerMetadata stream, err := client.UploadImage(ctx) if err != nil { grpclog.Infof("Failed to start streaming: %v", err) return nil, metadata, err } dec := marshaler.NewDecoder(req.Body) for { var protoReq UploadImageRequest err = dec.Decode(&protoReq) if err == io.EOF { break } if err != nil { grpclog.Infof("Failed to decode request: %v", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } if err = stream.Send(&protoReq); err != nil { if err == io.EOF { break } grpclog.Infof("Failed to send request: %v", err) return nil, metadata, err } } if err := stream.CloseSend(); err != nil { grpclog.Infof("Failed to terminate client stream: %v", err) return nil, metadata, err } header, err := stream.Header() if err != nil { grpclog.Infof("Failed to get header from client: %v", err) return nil, metadata, err } metadata.HeaderMD = header msg, err := stream.CloseAndRecv() metadata.TrailerMD = stream.Trailer() return msg, metadata, err } func request_LaptopService_RateLaptop_0(ctx context.Context, marshaler runtime.Marshaler, client LaptopServiceClient, req *http.Request, pathParams map[string]string) (LaptopService_RateLaptopClient, runtime.ServerMetadata, error) { var metadata runtime.ServerMetadata stream, err := client.RateLaptop(ctx) if err != nil { grpclog.Infof("Failed to start streaming: %v", err) return nil, metadata, err } dec := marshaler.NewDecoder(req.Body) handleSend := func() error { var protoReq RateLaptopRequest err := dec.Decode(&protoReq) if err == io.EOF { return err } if err != nil { grpclog.Infof("Failed to decode request: %v", err) return err } if err := stream.Send(&protoReq); err != nil { grpclog.Infof("Failed to send request: %v", err) return err } return nil } if err := handleSend(); err != nil { if cerr := stream.CloseSend(); cerr != nil { grpclog.Infof("Failed to terminate client stream: %v", cerr) } if err == io.EOF { return stream, metadata, nil } return nil, metadata, err } go func() { for { if err := handleSend(); err != nil { break } } if err := stream.CloseSend(); err != nil { grpclog.Infof("Failed to terminate client stream: %v", err) } }() header, err := stream.Header() if err != nil { grpclog.Infof("Failed to get header from client: %v", err) return nil, metadata, err } metadata.HeaderMD = header return stream, metadata, nil } // RegisterLaptopServiceHandlerServer registers the http handlers for service LaptopService to "mux". // UnaryRPC :call LaptopServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterLaptopServiceHandlerFromEndpoint instead. func RegisterLaptopServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server LaptopServiceServer) error { mux.Handle("POST", pattern_LaptopService_CreateLaptop_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/techschool.pcbook.LaptopService/CreateLaptop") if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_LaptopService_CreateLaptop_0(rctx, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_LaptopService_CreateLaptop_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("GET", pattern_LaptopService_SearchLaptop_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_LaptopService_UploadImage_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) mux.Handle("POST", pattern_LaptopService_RateLaptop_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return }) return nil } // RegisterLaptopServiceHandlerFromEndpoint is same as RegisterLaptopServiceHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterLaptopServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { conn, err := grpc.Dial(endpoint, opts...) if err != nil { return err } defer func() { if err != nil { if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } return } go func() { <-ctx.Done() if cerr := conn.Close(); cerr != nil { grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) } }() }() return RegisterLaptopServiceHandler(ctx, mux, conn) } // RegisterLaptopServiceHandler registers the http handlers for service LaptopService to "mux". // The handlers forward requests to the grpc endpoint over "conn". func RegisterLaptopServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { return RegisterLaptopServiceHandlerClient(ctx, mux, NewLaptopServiceClient(conn)) } // RegisterLaptopServiceHandlerClient registers the http handlers for service LaptopService // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "LaptopServiceClient". // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "LaptopServiceClient" // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in // "LaptopServiceClient" to call the correct interceptors. func RegisterLaptopServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client LaptopServiceClient) error { mux.Handle("POST", pattern_LaptopService_CreateLaptop_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req, "/techschool.pcbook.LaptopService/CreateLaptop") if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_LaptopService_CreateLaptop_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_LaptopService_CreateLaptop_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("GET", pattern_LaptopService_SearchLaptop_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req, "/techschool.pcbook.LaptopService/SearchLaptop") if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_LaptopService_SearchLaptop_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_LaptopService_SearchLaptop_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_LaptopService_UploadImage_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req, "/techschool.pcbook.LaptopService/UploadImage") if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_LaptopService_UploadImage_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_LaptopService_UploadImage_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle("POST", pattern_LaptopService_RateLaptop_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) rctx, err := runtime.AnnotateContext(ctx, mux, req, "/techschool.pcbook.LaptopService/RateLaptop") if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_LaptopService_RateLaptop_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } forward_LaptopService_RateLaptop_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) return nil } var ( pattern_LaptopService_CreateLaptop_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "laptop", "create"}, "")) pattern_LaptopService_SearchLaptop_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "laptop", "search"}, "")) pattern_LaptopService_UploadImage_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "laptop", "upload_image"}, "")) pattern_LaptopService_RateLaptop_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "laptop", "rate"}, "")) ) var ( forward_LaptopService_CreateLaptop_0 = runtime.ForwardResponseMessage forward_LaptopService_SearchLaptop_0 = runtime.ForwardResponseStream forward_LaptopService_UploadImage_0 = runtime.ForwardResponseMessage forward_LaptopService_RateLaptop_0 = runtime.ForwardResponseStream ) ================================================ FILE: pb/laptop_service_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. package pb import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // LaptopServiceClient is the client API for LaptopService service. // // 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. type LaptopServiceClient interface { CreateLaptop(ctx context.Context, in *CreateLaptopRequest, opts ...grpc.CallOption) (*CreateLaptopResponse, error) SearchLaptop(ctx context.Context, in *SearchLaptopRequest, opts ...grpc.CallOption) (LaptopService_SearchLaptopClient, error) UploadImage(ctx context.Context, opts ...grpc.CallOption) (LaptopService_UploadImageClient, error) RateLaptop(ctx context.Context, opts ...grpc.CallOption) (LaptopService_RateLaptopClient, error) } type laptopServiceClient struct { cc grpc.ClientConnInterface } func NewLaptopServiceClient(cc grpc.ClientConnInterface) LaptopServiceClient { return &laptopServiceClient{cc} } func (c *laptopServiceClient) CreateLaptop(ctx context.Context, in *CreateLaptopRequest, opts ...grpc.CallOption) (*CreateLaptopResponse, error) { out := new(CreateLaptopResponse) err := c.cc.Invoke(ctx, "/techschool.pcbook.LaptopService/CreateLaptop", in, out, opts...) if err != nil { return nil, err } return out, nil } func (c *laptopServiceClient) SearchLaptop(ctx context.Context, in *SearchLaptopRequest, opts ...grpc.CallOption) (LaptopService_SearchLaptopClient, error) { stream, err := c.cc.NewStream(ctx, &LaptopService_ServiceDesc.Streams[0], "/techschool.pcbook.LaptopService/SearchLaptop", opts...) if err != nil { return nil, err } x := &laptopServiceSearchLaptopClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } type LaptopService_SearchLaptopClient interface { Recv() (*SearchLaptopResponse, error) grpc.ClientStream } type laptopServiceSearchLaptopClient struct { grpc.ClientStream } func (x *laptopServiceSearchLaptopClient) Recv() (*SearchLaptopResponse, error) { m := new(SearchLaptopResponse) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } func (c *laptopServiceClient) UploadImage(ctx context.Context, opts ...grpc.CallOption) (LaptopService_UploadImageClient, error) { stream, err := c.cc.NewStream(ctx, &LaptopService_ServiceDesc.Streams[1], "/techschool.pcbook.LaptopService/UploadImage", opts...) if err != nil { return nil, err } x := &laptopServiceUploadImageClient{stream} return x, nil } type LaptopService_UploadImageClient interface { Send(*UploadImageRequest) error CloseAndRecv() (*UploadImageResponse, error) grpc.ClientStream } type laptopServiceUploadImageClient struct { grpc.ClientStream } func (x *laptopServiceUploadImageClient) Send(m *UploadImageRequest) error { return x.ClientStream.SendMsg(m) } func (x *laptopServiceUploadImageClient) CloseAndRecv() (*UploadImageResponse, error) { if err := x.ClientStream.CloseSend(); err != nil { return nil, err } m := new(UploadImageResponse) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } func (c *laptopServiceClient) RateLaptop(ctx context.Context, opts ...grpc.CallOption) (LaptopService_RateLaptopClient, error) { stream, err := c.cc.NewStream(ctx, &LaptopService_ServiceDesc.Streams[2], "/techschool.pcbook.LaptopService/RateLaptop", opts...) if err != nil { return nil, err } x := &laptopServiceRateLaptopClient{stream} return x, nil } type LaptopService_RateLaptopClient interface { Send(*RateLaptopRequest) error Recv() (*RateLaptopResponse, error) grpc.ClientStream } type laptopServiceRateLaptopClient struct { grpc.ClientStream } func (x *laptopServiceRateLaptopClient) Send(m *RateLaptopRequest) error { return x.ClientStream.SendMsg(m) } func (x *laptopServiceRateLaptopClient) Recv() (*RateLaptopResponse, error) { m := new(RateLaptopResponse) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil } // LaptopServiceServer is the server API for LaptopService service. // All implementations must embed UnimplementedLaptopServiceServer // for forward compatibility type LaptopServiceServer interface { CreateLaptop(context.Context, *CreateLaptopRequest) (*CreateLaptopResponse, error) SearchLaptop(*SearchLaptopRequest, LaptopService_SearchLaptopServer) error UploadImage(LaptopService_UploadImageServer) error RateLaptop(LaptopService_RateLaptopServer) error mustEmbedUnimplementedLaptopServiceServer() } // UnimplementedLaptopServiceServer must be embedded to have forward compatible implementations. type UnimplementedLaptopServiceServer struct { } func (UnimplementedLaptopServiceServer) CreateLaptop(context.Context, *CreateLaptopRequest) (*CreateLaptopResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateLaptop not implemented") } func (UnimplementedLaptopServiceServer) SearchLaptop(*SearchLaptopRequest, LaptopService_SearchLaptopServer) error { return status.Errorf(codes.Unimplemented, "method SearchLaptop not implemented") } func (UnimplementedLaptopServiceServer) UploadImage(LaptopService_UploadImageServer) error { return status.Errorf(codes.Unimplemented, "method UploadImage not implemented") } func (UnimplementedLaptopServiceServer) RateLaptop(LaptopService_RateLaptopServer) error { return status.Errorf(codes.Unimplemented, "method RateLaptop not implemented") } func (UnimplementedLaptopServiceServer) mustEmbedUnimplementedLaptopServiceServer() {} // UnsafeLaptopServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to LaptopServiceServer will // result in compilation errors. type UnsafeLaptopServiceServer interface { mustEmbedUnimplementedLaptopServiceServer() } func RegisterLaptopServiceServer(s grpc.ServiceRegistrar, srv LaptopServiceServer) { s.RegisterService(&LaptopService_ServiceDesc, srv) } func _LaptopService_CreateLaptop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateLaptopRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(LaptopServiceServer).CreateLaptop(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: "/techschool.pcbook.LaptopService/CreateLaptop", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(LaptopServiceServer).CreateLaptop(ctx, req.(*CreateLaptopRequest)) } return interceptor(ctx, in, info, handler) } func _LaptopService_SearchLaptop_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(SearchLaptopRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(LaptopServiceServer).SearchLaptop(m, &laptopServiceSearchLaptopServer{stream}) } type LaptopService_SearchLaptopServer interface { Send(*SearchLaptopResponse) error grpc.ServerStream } type laptopServiceSearchLaptopServer struct { grpc.ServerStream } func (x *laptopServiceSearchLaptopServer) Send(m *SearchLaptopResponse) error { return x.ServerStream.SendMsg(m) } func _LaptopService_UploadImage_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(LaptopServiceServer).UploadImage(&laptopServiceUploadImageServer{stream}) } type LaptopService_UploadImageServer interface { SendAndClose(*UploadImageResponse) error Recv() (*UploadImageRequest, error) grpc.ServerStream } type laptopServiceUploadImageServer struct { grpc.ServerStream } func (x *laptopServiceUploadImageServer) SendAndClose(m *UploadImageResponse) error { return x.ServerStream.SendMsg(m) } func (x *laptopServiceUploadImageServer) Recv() (*UploadImageRequest, error) { m := new(UploadImageRequest) if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err } return m, nil } func _LaptopService_RateLaptop_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(LaptopServiceServer).RateLaptop(&laptopServiceRateLaptopServer{stream}) } type LaptopService_RateLaptopServer interface { Send(*RateLaptopResponse) error Recv() (*RateLaptopRequest, error) grpc.ServerStream } type laptopServiceRateLaptopServer struct { grpc.ServerStream } func (x *laptopServiceRateLaptopServer) Send(m *RateLaptopResponse) error { return x.ServerStream.SendMsg(m) } func (x *laptopServiceRateLaptopServer) Recv() (*RateLaptopRequest, error) { m := new(RateLaptopRequest) if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err } return m, nil } // LaptopService_ServiceDesc is the grpc.ServiceDesc for LaptopService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var LaptopService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "techschool.pcbook.LaptopService", HandlerType: (*LaptopServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "CreateLaptop", Handler: _LaptopService_CreateLaptop_Handler, }, }, Streams: []grpc.StreamDesc{ { StreamName: "SearchLaptop", Handler: _LaptopService_SearchLaptop_Handler, ServerStreams: true, }, { StreamName: "UploadImage", Handler: _LaptopService_UploadImage_Handler, ClientStreams: true, }, { StreamName: "RateLaptop", Handler: _LaptopService_RateLaptop_Handler, ServerStreams: true, ClientStreams: true, }, }, Metadata: "laptop_service.proto", } ================================================ FILE: pb/memory_message.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: memory_message.proto package pb import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type Memory_Unit int32 const ( Memory_UNKNOWN Memory_Unit = 0 Memory_BIT Memory_Unit = 1 Memory_BYTE Memory_Unit = 2 Memory_KILOBYTE Memory_Unit = 3 Memory_MEGABYTE Memory_Unit = 4 Memory_GIGABYTE Memory_Unit = 5 Memory_TERABYTE Memory_Unit = 6 ) // Enum value maps for Memory_Unit. var ( Memory_Unit_name = map[int32]string{ 0: "UNKNOWN", 1: "BIT", 2: "BYTE", 3: "KILOBYTE", 4: "MEGABYTE", 5: "GIGABYTE", 6: "TERABYTE", } Memory_Unit_value = map[string]int32{ "UNKNOWN": 0, "BIT": 1, "BYTE": 2, "KILOBYTE": 3, "MEGABYTE": 4, "GIGABYTE": 5, "TERABYTE": 6, } ) func (x Memory_Unit) Enum() *Memory_Unit { p := new(Memory_Unit) *p = x return p } func (x Memory_Unit) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Memory_Unit) Descriptor() protoreflect.EnumDescriptor { return file_memory_message_proto_enumTypes[0].Descriptor() } func (Memory_Unit) Type() protoreflect.EnumType { return &file_memory_message_proto_enumTypes[0] } func (x Memory_Unit) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Memory_Unit.Descriptor instead. func (Memory_Unit) EnumDescriptor() ([]byte, []int) { return file_memory_message_proto_rawDescGZIP(), []int{0, 0} } type Memory struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` Unit Memory_Unit `protobuf:"varint,2,opt,name=unit,proto3,enum=techschool.pcbook.Memory_Unit" json:"unit,omitempty"` } func (x *Memory) Reset() { *x = Memory{} if protoimpl.UnsafeEnabled { mi := &file_memory_message_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Memory) String() string { return protoimpl.X.MessageStringOf(x) } func (*Memory) ProtoMessage() {} func (x *Memory) ProtoReflect() protoreflect.Message { mi := &file_memory_message_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Memory.ProtoReflect.Descriptor instead. func (*Memory) Descriptor() ([]byte, []int) { return file_memory_message_proto_rawDescGZIP(), []int{0} } func (x *Memory) GetValue() uint64 { if x != nil { return x.Value } return 0 } func (x *Memory) GetUnit() Memory_Unit { if x != nil { return x.Unit } return Memory_UNKNOWN } var File_memory_message_proto protoreflect.FileDescriptor var file_memory_message_proto_rawDesc = []byte{ 0x0a, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x22, 0xb2, 0x01, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x32, 0x0a, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x2e, 0x55, 0x6e, 0x69, 0x74, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x74, 0x22, 0x5e, 0x0a, 0x04, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x42, 0x49, 0x54, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x42, 0x59, 0x54, 0x45, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x4b, 0x49, 0x4c, 0x4f, 0x42, 0x59, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x45, 0x47, 0x41, 0x42, 0x59, 0x54, 0x45, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x47, 0x49, 0x47, 0x41, 0x42, 0x59, 0x54, 0x45, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x45, 0x52, 0x41, 0x42, 0x59, 0x54, 0x45, 0x10, 0x06, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_memory_message_proto_rawDescOnce sync.Once file_memory_message_proto_rawDescData = file_memory_message_proto_rawDesc ) func file_memory_message_proto_rawDescGZIP() []byte { file_memory_message_proto_rawDescOnce.Do(func() { file_memory_message_proto_rawDescData = protoimpl.X.CompressGZIP(file_memory_message_proto_rawDescData) }) return file_memory_message_proto_rawDescData } var file_memory_message_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_memory_message_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_memory_message_proto_goTypes = []interface{}{ (Memory_Unit)(0), // 0: techschool.pcbook.Memory.Unit (*Memory)(nil), // 1: techschool.pcbook.Memory } var file_memory_message_proto_depIdxs = []int32{ 0, // 0: techschool.pcbook.Memory.unit:type_name -> techschool.pcbook.Memory.Unit 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_memory_message_proto_init() } func file_memory_message_proto_init() { if File_memory_message_proto != nil { return } if !protoimpl.UnsafeEnabled { file_memory_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Memory); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_memory_message_proto_rawDesc, NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_memory_message_proto_goTypes, DependencyIndexes: file_memory_message_proto_depIdxs, EnumInfos: file_memory_message_proto_enumTypes, MessageInfos: file_memory_message_proto_msgTypes, }.Build() File_memory_message_proto = out.File file_memory_message_proto_rawDesc = nil file_memory_message_proto_goTypes = nil file_memory_message_proto_depIdxs = nil } ================================================ FILE: pb/processor_message.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: processor_message.proto package pb import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type CPU struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Brand string `protobuf:"bytes,1,opt,name=brand,proto3" json:"brand,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` NumberCores uint32 `protobuf:"varint,3,opt,name=number_cores,json=numberCores,proto3" json:"number_cores,omitempty"` NumberThreads uint32 `protobuf:"varint,4,opt,name=number_threads,json=numberThreads,proto3" json:"number_threads,omitempty"` MinGhz float64 `protobuf:"fixed64,5,opt,name=min_ghz,json=minGhz,proto3" json:"min_ghz,omitempty"` MaxGhz float64 `protobuf:"fixed64,6,opt,name=max_ghz,json=maxGhz,proto3" json:"max_ghz,omitempty"` } func (x *CPU) Reset() { *x = CPU{} if protoimpl.UnsafeEnabled { mi := &file_processor_message_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CPU) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPU) ProtoMessage() {} func (x *CPU) ProtoReflect() protoreflect.Message { mi := &file_processor_message_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPU.ProtoReflect.Descriptor instead. func (*CPU) Descriptor() ([]byte, []int) { return file_processor_message_proto_rawDescGZIP(), []int{0} } func (x *CPU) GetBrand() string { if x != nil { return x.Brand } return "" } func (x *CPU) GetName() string { if x != nil { return x.Name } return "" } func (x *CPU) GetNumberCores() uint32 { if x != nil { return x.NumberCores } return 0 } func (x *CPU) GetNumberThreads() uint32 { if x != nil { return x.NumberThreads } return 0 } func (x *CPU) GetMinGhz() float64 { if x != nil { return x.MinGhz } return 0 } func (x *CPU) GetMaxGhz() float64 { if x != nil { return x.MaxGhz } return 0 } type GPU struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Brand string `protobuf:"bytes,1,opt,name=brand,proto3" json:"brand,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` MinGhz float64 `protobuf:"fixed64,3,opt,name=min_ghz,json=minGhz,proto3" json:"min_ghz,omitempty"` MaxGhz float64 `protobuf:"fixed64,4,opt,name=max_ghz,json=maxGhz,proto3" json:"max_ghz,omitempty"` Memory *Memory `protobuf:"bytes,5,opt,name=memory,proto3" json:"memory,omitempty"` } func (x *GPU) Reset() { *x = GPU{} if protoimpl.UnsafeEnabled { mi := &file_processor_message_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GPU) String() string { return protoimpl.X.MessageStringOf(x) } func (*GPU) ProtoMessage() {} func (x *GPU) ProtoReflect() protoreflect.Message { mi := &file_processor_message_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GPU.ProtoReflect.Descriptor instead. func (*GPU) Descriptor() ([]byte, []int) { return file_processor_message_proto_rawDescGZIP(), []int{1} } func (x *GPU) GetBrand() string { if x != nil { return x.Brand } return "" } func (x *GPU) GetName() string { if x != nil { return x.Name } return "" } func (x *GPU) GetMinGhz() float64 { if x != nil { return x.MinGhz } return 0 } func (x *GPU) GetMaxGhz() float64 { if x != nil { return x.MaxGhz } return 0 } func (x *GPU) GetMemory() *Memory { if x != nil { return x.Memory } return nil } var File_processor_message_proto protoreflect.FileDescriptor var file_processor_message_proto_rawDesc = []byte{ 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x1a, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xab, 0x01, 0x0a, 0x03, 0x43, 0x50, 0x55, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x67, 0x68, 0x7a, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x47, 0x68, 0x7a, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x5f, 0x67, 0x68, 0x7a, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x47, 0x68, 0x7a, 0x22, 0x94, 0x01, 0x0a, 0x03, 0x47, 0x50, 0x55, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x72, 0x61, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x67, 0x68, 0x7a, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x47, 0x68, 0x7a, 0x12, 0x17, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x5f, 0x67, 0x68, 0x7a, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x47, 0x68, 0x7a, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_processor_message_proto_rawDescOnce sync.Once file_processor_message_proto_rawDescData = file_processor_message_proto_rawDesc ) func file_processor_message_proto_rawDescGZIP() []byte { file_processor_message_proto_rawDescOnce.Do(func() { file_processor_message_proto_rawDescData = protoimpl.X.CompressGZIP(file_processor_message_proto_rawDescData) }) return file_processor_message_proto_rawDescData } var file_processor_message_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_processor_message_proto_goTypes = []interface{}{ (*CPU)(nil), // 0: techschool.pcbook.CPU (*GPU)(nil), // 1: techschool.pcbook.GPU (*Memory)(nil), // 2: techschool.pcbook.Memory } var file_processor_message_proto_depIdxs = []int32{ 2, // 0: techschool.pcbook.GPU.memory:type_name -> techschool.pcbook.Memory 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_processor_message_proto_init() } func file_processor_message_proto_init() { if File_processor_message_proto != nil { return } file_memory_message_proto_init() if !protoimpl.UnsafeEnabled { file_processor_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CPU); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_processor_message_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GPU); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_processor_message_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_processor_message_proto_goTypes, DependencyIndexes: file_processor_message_proto_depIdxs, MessageInfos: file_processor_message_proto_msgTypes, }.Build() File_processor_message_proto = out.File file_processor_message_proto_rawDesc = nil file_processor_message_proto_goTypes = nil file_processor_message_proto_depIdxs = nil } ================================================ FILE: pb/screen_message.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: screen_message.proto package pb import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type Screen_Panel int32 const ( Screen_UNKNOWN Screen_Panel = 0 Screen_IPS Screen_Panel = 1 Screen_OLED Screen_Panel = 2 ) // Enum value maps for Screen_Panel. var ( Screen_Panel_name = map[int32]string{ 0: "UNKNOWN", 1: "IPS", 2: "OLED", } Screen_Panel_value = map[string]int32{ "UNKNOWN": 0, "IPS": 1, "OLED": 2, } ) func (x Screen_Panel) Enum() *Screen_Panel { p := new(Screen_Panel) *p = x return p } func (x Screen_Panel) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Screen_Panel) Descriptor() protoreflect.EnumDescriptor { return file_screen_message_proto_enumTypes[0].Descriptor() } func (Screen_Panel) Type() protoreflect.EnumType { return &file_screen_message_proto_enumTypes[0] } func (x Screen_Panel) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Screen_Panel.Descriptor instead. func (Screen_Panel) EnumDescriptor() ([]byte, []int) { return file_screen_message_proto_rawDescGZIP(), []int{0, 0} } type Screen struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields SizeInch float32 `protobuf:"fixed32,1,opt,name=size_inch,json=sizeInch,proto3" json:"size_inch,omitempty"` Resolution *Screen_Resolution `protobuf:"bytes,2,opt,name=resolution,proto3" json:"resolution,omitempty"` Panel Screen_Panel `protobuf:"varint,3,opt,name=panel,proto3,enum=techschool.pcbook.Screen_Panel" json:"panel,omitempty"` Multitouch bool `protobuf:"varint,4,opt,name=multitouch,proto3" json:"multitouch,omitempty"` } func (x *Screen) Reset() { *x = Screen{} if protoimpl.UnsafeEnabled { mi := &file_screen_message_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Screen) String() string { return protoimpl.X.MessageStringOf(x) } func (*Screen) ProtoMessage() {} func (x *Screen) ProtoReflect() protoreflect.Message { mi := &file_screen_message_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Screen.ProtoReflect.Descriptor instead. func (*Screen) Descriptor() ([]byte, []int) { return file_screen_message_proto_rawDescGZIP(), []int{0} } func (x *Screen) GetSizeInch() float32 { if x != nil { return x.SizeInch } return 0 } func (x *Screen) GetResolution() *Screen_Resolution { if x != nil { return x.Resolution } return nil } func (x *Screen) GetPanel() Screen_Panel { if x != nil { return x.Panel } return Screen_UNKNOWN } func (x *Screen) GetMultitouch() bool { if x != nil { return x.Multitouch } return false } type Screen_Resolution struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Width uint32 `protobuf:"varint,1,opt,name=width,proto3" json:"width,omitempty"` Height uint32 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` } func (x *Screen_Resolution) Reset() { *x = Screen_Resolution{} if protoimpl.UnsafeEnabled { mi := &file_screen_message_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Screen_Resolution) String() string { return protoimpl.X.MessageStringOf(x) } func (*Screen_Resolution) ProtoMessage() {} func (x *Screen_Resolution) ProtoReflect() protoreflect.Message { mi := &file_screen_message_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Screen_Resolution.ProtoReflect.Descriptor instead. func (*Screen_Resolution) Descriptor() ([]byte, []int) { return file_screen_message_proto_rawDescGZIP(), []int{0, 0} } func (x *Screen_Resolution) GetWidth() uint32 { if x != nil { return x.Width } return 0 } func (x *Screen_Resolution) GetHeight() uint32 { if x != nil { return x.Height } return 0 } var File_screen_message_proto protoreflect.FileDescriptor var file_screen_message_proto_rawDesc = []byte{ 0x0a, 0x14, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x22, 0xa7, 0x02, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x69, 0x6e, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x08, 0x73, 0x69, 0x7a, 0x65, 0x49, 0x6e, 0x63, 0x68, 0x12, 0x44, 0x0a, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x05, 0x70, 0x61, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x2e, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x52, 0x05, 0x70, 0x61, 0x6e, 0x65, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x1a, 0x3a, 0x0a, 0x0a, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x27, 0x0a, 0x05, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x50, 0x53, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_screen_message_proto_rawDescOnce sync.Once file_screen_message_proto_rawDescData = file_screen_message_proto_rawDesc ) func file_screen_message_proto_rawDescGZIP() []byte { file_screen_message_proto_rawDescOnce.Do(func() { file_screen_message_proto_rawDescData = protoimpl.X.CompressGZIP(file_screen_message_proto_rawDescData) }) return file_screen_message_proto_rawDescData } var file_screen_message_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_screen_message_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_screen_message_proto_goTypes = []interface{}{ (Screen_Panel)(0), // 0: techschool.pcbook.Screen.Panel (*Screen)(nil), // 1: techschool.pcbook.Screen (*Screen_Resolution)(nil), // 2: techschool.pcbook.Screen.Resolution } var file_screen_message_proto_depIdxs = []int32{ 2, // 0: techschool.pcbook.Screen.resolution:type_name -> techschool.pcbook.Screen.Resolution 0, // 1: techschool.pcbook.Screen.panel:type_name -> techschool.pcbook.Screen.Panel 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_screen_message_proto_init() } func file_screen_message_proto_init() { if File_screen_message_proto != nil { return } if !protoimpl.UnsafeEnabled { file_screen_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Screen); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_screen_message_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Screen_Resolution); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_screen_message_proto_rawDesc, NumEnums: 1, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_screen_message_proto_goTypes, DependencyIndexes: file_screen_message_proto_depIdxs, EnumInfos: file_screen_message_proto_enumTypes, MessageInfos: file_screen_message_proto_msgTypes, }.Build() File_screen_message_proto = out.File file_screen_message_proto_rawDesc = nil file_screen_message_proto_goTypes = nil file_screen_message_proto_depIdxs = nil } ================================================ FILE: pb/storage_message.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 // protoc v3.14.0 // source: storage_message.proto package pb import ( proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // This is a compile-time assertion that a sufficiently up-to-date version // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 type Storage_Driver int32 const ( Storage_UNKNOWN Storage_Driver = 0 Storage_HDD Storage_Driver = 1 Storage_SSD Storage_Driver = 2 ) // Enum value maps for Storage_Driver. var ( Storage_Driver_name = map[int32]string{ 0: "UNKNOWN", 1: "HDD", 2: "SSD", } Storage_Driver_value = map[string]int32{ "UNKNOWN": 0, "HDD": 1, "SSD": 2, } ) func (x Storage_Driver) Enum() *Storage_Driver { p := new(Storage_Driver) *p = x return p } func (x Storage_Driver) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Storage_Driver) Descriptor() protoreflect.EnumDescriptor { return file_storage_message_proto_enumTypes[0].Descriptor() } func (Storage_Driver) Type() protoreflect.EnumType { return &file_storage_message_proto_enumTypes[0] } func (x Storage_Driver) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Storage_Driver.Descriptor instead. func (Storage_Driver) EnumDescriptor() ([]byte, []int) { return file_storage_message_proto_rawDescGZIP(), []int{0, 0} } type Storage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Driver Storage_Driver `protobuf:"varint,1,opt,name=driver,proto3,enum=techschool.pcbook.Storage_Driver" json:"driver,omitempty"` Memory *Memory `protobuf:"bytes,2,opt,name=memory,proto3" json:"memory,omitempty"` } func (x *Storage) Reset() { *x = Storage{} if protoimpl.UnsafeEnabled { mi := &file_storage_message_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Storage) String() string { return protoimpl.X.MessageStringOf(x) } func (*Storage) ProtoMessage() {} func (x *Storage) ProtoReflect() protoreflect.Message { mi := &file_storage_message_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Storage.ProtoReflect.Descriptor instead. func (*Storage) Descriptor() ([]byte, []int) { return file_storage_message_proto_rawDescGZIP(), []int{0} } func (x *Storage) GetDriver() Storage_Driver { if x != nil { return x.Driver } return Storage_UNKNOWN } func (x *Storage) GetMemory() *Memory { if x != nil { return x.Memory } return nil } var File_storage_message_proto protoreflect.FileDescriptor var file_storage_message_proto_rawDesc = []byte{ 0x0a, 0x15, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x1a, 0x14, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa0, 0x01, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x22, 0x27, 0x0a, 0x06, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x48, 0x44, 0x44, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x53, 0x44, 0x10, 0x02, 0x42, 0x29, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x74, 0x65, 0x63, 0x68, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2e, 0x70, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x70, 0x62, 0x50, 0x01, 0x5a, 0x04, 0x2e, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_storage_message_proto_rawDescOnce sync.Once file_storage_message_proto_rawDescData = file_storage_message_proto_rawDesc ) func file_storage_message_proto_rawDescGZIP() []byte { file_storage_message_proto_rawDescOnce.Do(func() { file_storage_message_proto_rawDescData = protoimpl.X.CompressGZIP(file_storage_message_proto_rawDescData) }) return file_storage_message_proto_rawDescData } var file_storage_message_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_storage_message_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_storage_message_proto_goTypes = []interface{}{ (Storage_Driver)(0), // 0: techschool.pcbook.Storage.Driver (*Storage)(nil), // 1: techschool.pcbook.Storage (*Memory)(nil), // 2: techschool.pcbook.Memory } var file_storage_message_proto_depIdxs = []int32{ 0, // 0: techschool.pcbook.Storage.driver:type_name -> techschool.pcbook.Storage.Driver 2, // 1: techschool.pcbook.Storage.memory:type_name -> techschool.pcbook.Memory 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_storage_message_proto_init() } func file_storage_message_proto_init() { if File_storage_message_proto != nil { return } file_memory_message_proto_init() if !protoimpl.UnsafeEnabled { file_storage_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Storage); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_storage_message_proto_rawDesc, NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_storage_message_proto_goTypes, DependencyIndexes: file_storage_message_proto_depIdxs, EnumInfos: file_storage_message_proto_enumTypes, MessageInfos: file_storage_message_proto_msgTypes, }.Build() File_storage_message_proto = out.File file_storage_message_proto_rawDesc = nil file_storage_message_proto_goTypes = nil file_storage_message_proto_depIdxs = nil } ================================================ FILE: proto/auth_service.proto ================================================ syntax = "proto3"; package techschool.pcbook; import "google/api/annotations.proto"; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; message LoginRequest { string username = 1; string password = 2; } message LoginResponse { string access_token = 1; } service AuthService { rpc Login(LoginRequest) returns (LoginResponse) { option (google.api.http) = { post : "/v1/auth/login" body : "*" }; }; } ================================================ FILE: proto/filter_message.proto ================================================ syntax = "proto3"; package techschool.pcbook; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; import "memory_message.proto"; message Filter { double max_price_usd = 1; uint32 min_cpu_cores = 2; double min_cpu_ghz = 3; Memory min_ram = 4; } ================================================ FILE: proto/google/api/annotations.proto ================================================ // Copyright (c) 2015, Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package google.api; import "google/api/http.proto"; import "google/protobuf/descriptor.proto"; option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; option java_multiple_files = true; option java_outer_classname = "AnnotationsProto"; option java_package = "com.google.api"; option objc_class_prefix = "GAPI"; extend google.protobuf.MethodOptions { // See `HttpRule`. HttpRule http = 72295728; } ================================================ FILE: proto/google/api/http.proto ================================================ // Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package google.api; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; option java_multiple_files = true; option java_outer_classname = "HttpProto"; option java_package = "com.google.api"; option objc_class_prefix = "GAPI"; // Defines the HTTP configuration for an API service. It contains a list of // [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method // to one or more HTTP REST API methods. message Http { // A list of HTTP configuration rules that apply to individual API methods. // // **NOTE:** All service configuration rules follow "last one wins" order. repeated HttpRule rules = 1; // When set to true, URL path parmeters will be fully URI-decoded except in // cases of single segment matches in reserved expansion, where "%2F" will be // left encoded. // // The default behavior is to not decode RFC 6570 reserved characters in multi // segment matches. bool fully_decode_reserved_expansion = 2; } // `HttpRule` defines the mapping of an RPC method to one or more HTTP // REST API methods. The mapping specifies how different portions of the RPC // request message are mapped to URL path, URL query parameters, and // HTTP request body. The mapping is typically specified as an // `google.api.http` annotation on the RPC method, // see "google/api/annotations.proto" for details. // // The mapping consists of a field specifying the path template and // method kind. The path template can refer to fields in the request // message, as in the example below which describes a REST GET // operation on a resource collection of messages: // // // service Messaging { // rpc GetMessage(GetMessageRequest) returns (Message) { // option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}"; // } // } // message GetMessageRequest { // message SubMessage { // string subfield = 1; // } // string message_id = 1; // mapped to the URL // SubMessage sub = 2; // `sub.subfield` is url-mapped // } // message Message { // string text = 1; // content of the resource // } // // The same http annotation can alternatively be expressed inside the // `GRPC API Configuration` YAML file. // // http: // rules: // - selector: .Messaging.GetMessage // get: /v1/messages/{message_id}/{sub.subfield} // // This definition enables an automatic, bidrectional mapping of HTTP // JSON to RPC. Example: // // HTTP | RPC // -----|----- // `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))` // // In general, not only fields but also field paths can be referenced // from a path pattern. Fields mapped to the path pattern cannot be // repeated and must have a primitive (non-message) type. // // Any fields in the request message which are not bound by the path // pattern automatically become (optional) HTTP query // parameters. Assume the following definition of the request message: // // // service Messaging { // rpc GetMessage(GetMessageRequest) returns (Message) { // option (google.api.http).get = "/v1/messages/{message_id}"; // } // } // message GetMessageRequest { // message SubMessage { // string subfield = 1; // } // string message_id = 1; // mapped to the URL // int64 revision = 2; // becomes a parameter // SubMessage sub = 3; // `sub.subfield` becomes a parameter // } // // // This enables a HTTP JSON to RPC mapping as below: // // HTTP | RPC // -----|----- // `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))` // // Note that fields which are mapped to HTTP parameters must have a // primitive type or a repeated primitive type. Message types are not // allowed. In the case of a repeated type, the parameter can be // repeated in the URL, as in `...?param=A¶m=B`. // // For HTTP method kinds which allow a request body, the `body` field // specifies the mapping. Consider a REST update method on the // message resource collection: // // // service Messaging { // rpc UpdateMessage(UpdateMessageRequest) returns (Message) { // option (google.api.http) = { // put: "/v1/messages/{message_id}" // body: "message" // }; // } // } // message UpdateMessageRequest { // string message_id = 1; // mapped to the URL // Message message = 2; // mapped to the body // } // // // The following HTTP JSON to RPC mapping is enabled, where the // representation of the JSON in the request body is determined by // protos JSON encoding: // // HTTP | RPC // -----|----- // `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })` // // The special name `*` can be used in the body mapping to define that // every field not bound by the path template should be mapped to the // request body. This enables the following alternative definition of // the update method: // // service Messaging { // rpc UpdateMessage(Message) returns (Message) { // option (google.api.http) = { // put: "/v1/messages/{message_id}" // body: "*" // }; // } // } // message Message { // string message_id = 1; // string text = 2; // } // // // The following HTTP JSON to RPC mapping is enabled: // // HTTP | RPC // -----|----- // `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")` // // Note that when using `*` in the body mapping, it is not possible to // have HTTP parameters, as all fields not bound by the path end in // the body. This makes this option more rarely used in practice of // defining REST APIs. The common usage of `*` is in custom methods // which don't use the URL at all for transferring data. // // It is possible to define multiple HTTP methods for one RPC by using // the `additional_bindings` option. Example: // // service Messaging { // rpc GetMessage(GetMessageRequest) returns (Message) { // option (google.api.http) = { // get: "/v1/messages/{message_id}" // additional_bindings { // get: "/v1/users/{user_id}/messages/{message_id}" // } // }; // } // } // message GetMessageRequest { // string message_id = 1; // string user_id = 2; // } // // // This enables the following two alternative HTTP JSON to RPC // mappings: // // HTTP | RPC // -----|----- // `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` // `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")` // // # Rules for HTTP mapping // // The rules for mapping HTTP path, query parameters, and body fields // to the request message are as follows: // // 1. The `body` field specifies either `*` or a field path, or is // omitted. If omitted, it indicates there is no HTTP request body. // 2. Leaf fields (recursive expansion of nested messages in the // request) can be classified into three types: // (a) Matched in the URL template. // (b) Covered by body (if body is `*`, everything except (a) fields; // else everything under the body field) // (c) All other fields. // 3. URL query parameters found in the HTTP request are mapped to (c) fields. // 4. Any body sent with an HTTP request can contain only (b) fields. // // The syntax of the path template is as follows: // // Template = "/" Segments [ Verb ] ; // Segments = Segment { "/" Segment } ; // Segment = "*" | "**" | LITERAL | Variable ; // Variable = "{" FieldPath [ "=" Segments ] "}" ; // FieldPath = IDENT { "." IDENT } ; // Verb = ":" LITERAL ; // // The syntax `*` matches a single path segment. The syntax `**` matches zero // or more path segments, which must be the last part of the path except the // `Verb`. The syntax `LITERAL` matches literal text in the path. // // The syntax `Variable` matches part of the URL path as specified by its // template. A variable template must not contain other variables. If a variable // matches a single path segment, its template may be omitted, e.g. `{var}` // is equivalent to `{var=*}`. // // If a variable contains exactly one path segment, such as `"{var}"` or // `"{var=*}"`, when such a variable is expanded into a URL path, all characters // except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the // Discovery Document as `{var}`. // // If a variable contains one or more path segments, such as `"{var=foo/*}"` // or `"{var=**}"`, when such a variable is expanded into a URL path, all // characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables // show up in the Discovery Document as `{+var}`. // // NOTE: While the single segment variable matches the semantics of // [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 // Simple String Expansion, the multi segment variable **does not** match // RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion // does not expand special characters like `?` and `#`, which would lead // to invalid URLs. // // NOTE: the field paths in variables and in the `body` must not refer to // repeated fields or map fields. message HttpRule { // Selects methods to which this rule applies. // // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. string selector = 1; // Determines the URL pattern is matched by this rules. This pattern can be // used with any of the {get|put|post|delete|patch} methods. A custom method // can be defined using the 'custom' field. oneof pattern { // Used for listing and getting information about resources. string get = 2; // Used for updating a resource. string put = 3; // Used for creating a resource. string post = 4; // Used for deleting a resource. string delete = 5; // Used for updating a resource. string patch = 6; // The custom pattern is used for specifying an HTTP method that is not // included in the `pattern` field, such as HEAD, or "*" to leave the // HTTP method unspecified for this rule. The wild-card rule is useful // for services that provide content to Web (HTML) clients. CustomHttpPattern custom = 8; } // The name of the request field whose value is mapped to the HTTP body, or // `*` for mapping all fields not captured by the path pattern to the HTTP // body. NOTE: the referred field must not be a repeated field and must be // present at the top-level of request message type. string body = 7; // Optional. The name of the response field whose value is mapped to the HTTP // body of response. Other response fields are ignored. When // not set, the response message will be used as HTTP body of response. string response_body = 12; // Additional HTTP bindings for the selector. Nested bindings must // not contain an `additional_bindings` field themselves (that is, // the nesting may only be one level deep). repeated HttpRule additional_bindings = 11; } // A custom pattern is used for defining custom HTTP verb. message CustomHttpPattern { // The name of this custom HTTP verb. string kind = 1; // The path matched by this custom verb. string path = 2; } ================================================ FILE: proto/google/api/httpbody.proto ================================================ // Copyright 2018 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // syntax = "proto3"; package google.api; import "google/protobuf/any.proto"; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; option java_multiple_files = true; option java_outer_classname = "HttpBodyProto"; option java_package = "com.google.api"; option objc_class_prefix = "GAPI"; // Message that represents an arbitrary HTTP body. It should only be used for // payload formats that can't be represented as JSON, such as raw binary or // an HTML page. // // // This message can be used both in streaming and non-streaming API methods in // the request as well as the response. // // It can be used as a top-level request field, which is convenient if one // wants to extract parameters from either the URL or HTTP template into the // request fields and also want access to the raw HTTP body. // // Example: // // message GetResourceRequest { // // A unique request id. // string request_id = 1; // // // The raw HTTP body is bound to this field. // google.api.HttpBody http_body = 2; // } // // service ResourceService { // rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); // rpc UpdateResource(google.api.HttpBody) returns // (google.protobuf.Empty); // } // // Example with streaming methods: // // service CaldavService { // rpc GetCalendar(stream google.api.HttpBody) // returns (stream google.api.HttpBody); // rpc UpdateCalendar(stream google.api.HttpBody) // returns (stream google.api.HttpBody); // } // // Use of this type only changes how the request and response bodies are // handled, all other features will continue to work unchanged. message HttpBody { // The HTTP Content-Type header value specifying the content type of the body. string content_type = 1; // The HTTP request/response body as raw binary. bytes data = 2; // Application specific response metadata. Must be set in the first response // for streaming APIs. repeated google.protobuf.Any extensions = 3; } ================================================ FILE: proto/google/rpc/code.proto ================================================ // Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package google.rpc; option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; option java_multiple_files = true; option java_outer_classname = "CodeProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; // The canonical error codes for Google APIs. // // // Sometimes multiple error codes may apply. Services should return // the most specific error code that applies. For example, prefer // `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. // Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. enum Code { // Not an error; returned on success // // HTTP Mapping: 200 OK OK = 0; // The operation was cancelled, typically by the caller. // // HTTP Mapping: 499 Client Closed Request CANCELLED = 1; // Unknown error. For example, this error may be returned when // a `Status` value received from another address space belongs to // an error space that is not known in this address space. Also // errors raised by APIs that do not return enough error information // may be converted to this error. // // HTTP Mapping: 500 Internal Server Error UNKNOWN = 2; // The client specified an invalid argument. Note that this differs // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments // that are problematic regardless of the state of the system // (e.g., a malformed file name). // // HTTP Mapping: 400 Bad Request INVALID_ARGUMENT = 3; // The deadline expired before the operation could complete. For operations // that change the state of the system, this error may be returned // even if the operation has completed successfully. For example, a // successful response from a server could have been delayed long // enough for the deadline to expire. // // HTTP Mapping: 504 Gateway Timeout DEADLINE_EXCEEDED = 4; // Some requested entity (e.g., file or directory) was not found. // // Note to server developers: if a request is denied for an entire class // of users, such as gradual feature rollout or undocumented whitelist, // `NOT_FOUND` may be used. If a request is denied for some users within // a class of users, such as user-based access control, `PERMISSION_DENIED` // must be used. // // HTTP Mapping: 404 Not Found NOT_FOUND = 5; // The entity that a client attempted to create (e.g., file or directory) // already exists. // // HTTP Mapping: 409 Conflict ALREADY_EXISTS = 6; // The caller does not have permission to execute the specified // operation. `PERMISSION_DENIED` must not be used for rejections // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` // instead for those errors). `PERMISSION_DENIED` must not be // used if the caller can not be identified (use `UNAUTHENTICATED` // instead for those errors). This error code does not imply the // request is valid or the requested entity exists or satisfies // other pre-conditions. // // HTTP Mapping: 403 Forbidden PERMISSION_DENIED = 7; // The request does not have valid authentication credentials for the // operation. // // HTTP Mapping: 401 Unauthorized UNAUTHENTICATED = 16; // Some resource has been exhausted, perhaps a per-user quota, or // perhaps the entire file system is out of space. // // HTTP Mapping: 429 Too Many Requests RESOURCE_EXHAUSTED = 8; // The operation was rejected because the system is not in a state // required for the operation's execution. For example, the directory // to be deleted is non-empty, an rmdir operation is applied to // a non-directory, etc. // // Service implementors can use the following guidelines to decide // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: // (a) Use `UNAVAILABLE` if the client can retry just the failing call. // (b) Use `ABORTED` if the client should retry at a higher level // (e.g., when a client-specified test-and-set fails, indicating the // client should restart a read-modify-write sequence). // (c) Use `FAILED_PRECONDITION` if the client should not retry until // the system state has been explicitly fixed. E.g., if an "rmdir" // fails because the directory is non-empty, `FAILED_PRECONDITION` // should be returned since the client should not retry unless // the files are deleted from the directory. // // HTTP Mapping: 400 Bad Request FAILED_PRECONDITION = 9; // The operation was aborted, typically due to a concurrency issue such as // a sequencer check failure or transaction abort. // // See the guidelines above for deciding between `FAILED_PRECONDITION`, // `ABORTED`, and `UNAVAILABLE`. // // HTTP Mapping: 409 Conflict ABORTED = 10; // The operation was attempted past the valid range. E.g., seeking or // reading past end-of-file. // // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may // be fixed if the system state changes. For example, a 32-bit file // system will generate `INVALID_ARGUMENT` if asked to read at an // offset that is not in the range [0,2^32-1], but it will generate // `OUT_OF_RANGE` if asked to read from an offset past the current // file size. // // There is a fair bit of overlap between `FAILED_PRECONDITION` and // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific // error) when it applies so that callers who are iterating through // a space can easily look for an `OUT_OF_RANGE` error to detect when // they are done. // // HTTP Mapping: 400 Bad Request OUT_OF_RANGE = 11; // The operation is not implemented or is not supported/enabled in this // service. // // HTTP Mapping: 501 Not Implemented UNIMPLEMENTED = 12; // Internal errors. This means that some invariants expected by the // underlying system have been broken. This error code is reserved // for serious errors. // // HTTP Mapping: 500 Internal Server Error INTERNAL = 13; // The service is currently unavailable. This is most likely a // transient condition, which can be corrected by retrying with // a backoff. // // See the guidelines above for deciding between `FAILED_PRECONDITION`, // `ABORTED`, and `UNAVAILABLE`. // // HTTP Mapping: 503 Service Unavailable UNAVAILABLE = 14; // Unrecoverable data loss or corruption. // // HTTP Mapping: 500 Internal Server Error DATA_LOSS = 15; } ================================================ FILE: proto/google/rpc/error_details.proto ================================================ // Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package google.rpc; import "google/protobuf/duration.proto"; option go_package = "google.golang.org/genproto/googleapis/rpc/errdetails;errdetails"; option java_multiple_files = true; option java_outer_classname = "ErrorDetailsProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; // Describes when the clients can retry a failed request. Clients could ignore // the recommendation here or retry when this information is missing from error // responses. // // It's always recommended that clients should use exponential backoff when // retrying. // // Clients should wait until `retry_delay` amount of time has passed since // receiving the error response before retrying. If retrying requests also // fail, clients should use an exponential backoff scheme to gradually increase // the delay between retries based on `retry_delay`, until either a maximum // number of retires have been reached or a maximum retry delay cap has been // reached. message RetryInfo { // Clients should wait at least this long between retrying the same request. google.protobuf.Duration retry_delay = 1; } // Describes additional debugging info. message DebugInfo { // The stack trace entries indicating where the error occurred. repeated string stack_entries = 1; // Additional debugging information provided by the server. string detail = 2; } // Describes how a quota check failed. // // For example if a daily limit was exceeded for the calling project, // a service could respond with a QuotaFailure detail containing the project // id and the description of the quota limit that was exceeded. If the // calling project hasn't enabled the service in the developer console, then // a service could respond with the project id and set `service_disabled` // to true. // // Also see RetryDetail and Help types for other details about handling a // quota failure. message QuotaFailure { // A message type used to describe a single quota violation. For example, a // daily quota or a custom quota that was exceeded. message Violation { // The subject on which the quota check failed. // For example, "clientip:" or "project:". string subject = 1; // A description of how the quota check failed. Clients can use this // description to find more about the quota configuration in the service's // public documentation, or find the relevant quota limit to adjust through // developer console. // // For example: "Service disabled" or "Daily Limit for read operations // exceeded". string description = 2; } // Describes all quota violations. repeated Violation violations = 1; } // Describes what preconditions have failed. // // For example, if an RPC failed because it required the Terms of Service to be // acknowledged, it could list the terms of service violation in the // PreconditionFailure message. message PreconditionFailure { // A message type used to describe a single precondition failure. message Violation { // The type of PreconditionFailure. We recommend using a service-specific // enum type to define the supported precondition violation types. For // example, "TOS" for "Terms of Service violation". string type = 1; // The subject, relative to the type, that failed. // For example, "google.com/cloud" relative to the "TOS" type would // indicate which terms of service is being referenced. string subject = 2; // A description of how the precondition failed. Developers can use this // description to understand how to fix the failure. // // For example: "Terms of service not accepted". string description = 3; } // Describes all precondition violations. repeated Violation violations = 1; } // Describes violations in a client request. This error type focuses on the // syntactic aspects of the request. message BadRequest { // A message type used to describe a single bad request field. message FieldViolation { // A path leading to a field in the request body. The value will be a // sequence of dot-separated identifiers that identify a protocol buffer // field. E.g., "field_violations.field" would identify this field. string field = 1; // A description of why the request element is bad. string description = 2; } // Describes all violations in a client request. repeated FieldViolation field_violations = 1; } // Contains metadata about the request that clients can attach when filing a bug // or providing other forms of feedback. message RequestInfo { // An opaque string that should only be interpreted by the service generating // it. For example, it can be used to identify requests in the service's logs. string request_id = 1; // Any data that was used to serve this request. For example, an encrypted // stack trace that can be sent back to the service provider for debugging. string serving_data = 2; } // Describes the resource that is being accessed. message ResourceInfo { // A name for the type of resource being accessed, e.g. "sql table", // "cloud storage bucket", "file", "Google calendar"; or the type URL // of the resource: e.g. "type.googleapis.com/google.pubsub.v1.Topic". string resource_type = 1; // The name of the resource being accessed. For example, a shared calendar // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current // error is [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. string resource_name = 2; // The owner of the resource (optional). // For example, "user:" or "project:". string owner = 3; // Describes what error is encountered when accessing this resource. // For example, updating a cloud project may require the `writer` permission // on the developer console project. string description = 4; } // Provides links to documentation or for performing an out of band action. // // For example, if a quota check failed with an error indicating the calling // project hasn't enabled the accessed service, this can contain a URL pointing // directly to the right place in the developer console to flip the bit. message Help { // Describes a URL link. message Link { // Describes what the link offers. string description = 1; // The URL of the link. string url = 2; } // URL(s) pointing to additional information on handling the current error. repeated Link links = 1; } // Provides a localized error message that is safe to return to the user // which can be attached to an RPC error. message LocalizedMessage { // The locale used following the specification defined at // http://www.rfc-editor.org/rfc/bcp/bcp47.txt. // Examples are: "en-US", "fr-CH", "es-MX" string locale = 1; // The localized error message in the above locale. string message = 2; } ================================================ FILE: proto/google/rpc/status.proto ================================================ // Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package google.rpc; import "google/protobuf/any.proto"; option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; option java_multiple_files = true; option java_outer_classname = "StatusProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; // The `Status` type defines a logical error model that is suitable for different // programming environments, including REST APIs and RPC APIs. It is used by // [gRPC](https://github.com/grpc). The error model is designed to be: // // - Simple to use and understand for most users // - Flexible enough to meet unexpected needs // // # Overview // // The `Status` message contains three pieces of data: error code, error message, // and error details. The error code should be an enum value of // [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The // error message should be a developer-facing English message that helps // developers *understand* and *resolve* the error. If a localized user-facing // error message is needed, put the localized message in the error details or // localize it in the client. The optional error details may contain arbitrary // information about the error. There is a predefined set of error detail types // in the package `google.rpc` that can be used for common error conditions. // // # Language mapping // // The `Status` message is the logical representation of the error model, but it // is not necessarily the actual wire format. When the `Status` message is // exposed in different client libraries and different wire protocols, it can be // mapped differently. For example, it will likely be mapped to some exceptions // in Java, but more likely mapped to some error codes in C. // // # Other uses // // The error model and the `Status` message can be used in a variety of // environments, either with or without APIs, to provide a // consistent developer experience across different environments. // // Example uses of this error model include: // // - Partial errors. If a service needs to return partial errors to the client, // it may embed the `Status` in the normal response to indicate the partial // errors. // // - Workflow errors. A typical workflow has multiple steps. Each step may // have a `Status` message for error reporting. // // - Batch operations. If a client uses batch request and batch response, the // `Status` message should be used directly inside batch response, one for // each error sub-response. // // - Asynchronous operations. If an API call embeds asynchronous operation // results in its response, the status of those operations should be // represented directly using the `Status` message. // // - Logging. If some API errors are stored in logs, the message `Status` could // be used directly after any stripping needed for security/privacy reasons. message Status { // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. int32 code = 1; // A developer-facing error message, which should be in English. Any // user-facing error message should be localized and sent in the // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. string message = 2; // A list of messages that carry the error details. There is a common set of // message types for APIs to use. repeated google.protobuf.Any details = 3; } ================================================ FILE: proto/keyboard_message.proto ================================================ syntax = "proto3"; package techschool.pcbook; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; message Keyboard { enum Layout { UNKNOWN = 0; QWERTY = 1; QWERTZ = 2; AZERTY = 3; } Layout layout = 1; bool backlit = 2; } ================================================ FILE: proto/laptop_message.proto ================================================ syntax = "proto3"; package techschool.pcbook; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; import "processor_message.proto"; import "memory_message.proto"; import "storage_message.proto"; import "screen_message.proto"; import "keyboard_message.proto"; import "google/protobuf/timestamp.proto"; message Laptop { string id = 1; string brand = 2; string name = 3; CPU cpu = 4; Memory ram = 5; repeated GPU gpus = 6; repeated Storage storages = 7; Screen screen = 8; Keyboard keyboard = 9; oneof weight { double weight_kg = 10; double weight_lb = 11; } double price_usd = 12; uint32 release_year = 13; google.protobuf.Timestamp updated_at = 14; } ================================================ FILE: proto/laptop_service.proto ================================================ syntax = "proto3"; package techschool.pcbook; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; import "laptop_message.proto"; import "filter_message.proto"; import "google/api/annotations.proto"; message CreateLaptopRequest { Laptop laptop = 1; } message CreateLaptopResponse { string id = 1; } message SearchLaptopRequest { Filter filter = 1; } message SearchLaptopResponse { Laptop laptop = 1; } message UploadImageRequest { oneof data { ImageInfo info = 1; bytes chunk_data = 2; }; } message ImageInfo { string laptop_id = 1; string image_type = 2; } message UploadImageResponse { string id = 1; uint32 size = 2; } message RateLaptopRequest { string laptop_id = 1; double score = 2; } message RateLaptopResponse { string laptop_id = 1; uint32 rated_count = 2; double average_score = 3; } service LaptopService { rpc CreateLaptop(CreateLaptopRequest) returns (CreateLaptopResponse) { option (google.api.http) = { post : "/v1/laptop/create" body : "*" }; }; rpc SearchLaptop(SearchLaptopRequest) returns (stream SearchLaptopResponse) { option (google.api.http) = { get : "/v1/laptop/search" }; }; rpc UploadImage(stream UploadImageRequest) returns (UploadImageResponse) { option (google.api.http) = { post : "/v1/laptop/upload_image" body : "*" }; }; rpc RateLaptop(stream RateLaptopRequest) returns (stream RateLaptopResponse) { option (google.api.http) = { post : "/v1/laptop/rate" body : "*" }; }; } ================================================ FILE: proto/memory_message.proto ================================================ syntax = "proto3"; package techschool.pcbook; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; message Memory { enum Unit { UNKNOWN = 0; BIT = 1; BYTE = 2; KILOBYTE = 3; MEGABYTE = 4; GIGABYTE = 5; TERABYTE = 6; } uint64 value = 1; Unit unit = 2; } ================================================ FILE: proto/processor_message.proto ================================================ syntax = "proto3"; package techschool.pcbook; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; import "memory_message.proto"; message CPU { string brand = 1; string name = 2; uint32 number_cores = 3; uint32 number_threads = 4; double min_ghz = 5; double max_ghz = 6; } message GPU { string brand = 1; string name = 2; double min_ghz = 3; double max_ghz = 4; Memory memory = 5; } ================================================ FILE: proto/screen_message.proto ================================================ syntax = "proto3"; package techschool.pcbook; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; message Screen { message Resolution { uint32 width = 1; uint32 height = 2; } enum Panel { UNKNOWN = 0; IPS = 1; OLED = 2; } float size_inch = 1; Resolution resolution = 2; Panel panel = 3; bool multitouch = 4; } ================================================ FILE: proto/storage_message.proto ================================================ syntax = "proto3"; package techschool.pcbook; option go_package = ".;pb"; option java_package = "com.gitlab.techschool.pcbook.pb"; option java_multiple_files = true; import "memory_message.proto"; message Storage { enum Driver { UNKNOWN = 0; HDD = 1; SSD = 2; } Driver driver = 1; Memory memory = 2; } ================================================ FILE: sample/laptop.go ================================================ package sample import ( "github.com/golang/protobuf/ptypes" "gitlab.com/techschool/pcbook/pb" ) // NewKeyboard returns a new sample keyboard func NewKeyboard() *pb.Keyboard { keyboard := &pb.Keyboard{ Layout: randomKeyboardLayout(), Backlit: randomBool(), } return keyboard } // NewCPU returns a new sample CPU func NewCPU() *pb.CPU { brand := randomCPUBrand() name := randomCPUName(brand) numberCores := randomInt(2, 8) numberThreads := randomInt(numberCores, 12) minGhz := randomFloat64(2.0, 3.5) maxGhz := randomFloat64(minGhz, 5.0) cpu := &pb.CPU{ Brand: brand, Name: name, NumberCores: uint32(numberCores), NumberThreads: uint32(numberThreads), MinGhz: minGhz, MaxGhz: maxGhz, } return cpu } // NewGPU returns a new sample GPU func NewGPU() *pb.GPU { brand := randomGPUBrand() name := randomGPUName(brand) minGhz := randomFloat64(1.0, 1.5) maxGhz := randomFloat64(minGhz, 2.0) memGB := randomInt(2, 6) gpu := &pb.GPU{ Brand: brand, Name: name, MinGhz: minGhz, MaxGhz: maxGhz, Memory: &pb.Memory{ Value: uint64(memGB), Unit: pb.Memory_GIGABYTE, }, } return gpu } // NewRAM returns a new sample RAM func NewRAM() *pb.Memory { memGB := randomInt(4, 64) ram := &pb.Memory{ Value: uint64(memGB), Unit: pb.Memory_GIGABYTE, } return ram } // NewSSD returns a new sample SSD func NewSSD() *pb.Storage { memGB := randomInt(128, 1024) ssd := &pb.Storage{ Driver: pb.Storage_SSD, Memory: &pb.Memory{ Value: uint64(memGB), Unit: pb.Memory_GIGABYTE, }, } return ssd } // NewHDD returns a new sample HDD func NewHDD() *pb.Storage { memTB := randomInt(1, 6) hdd := &pb.Storage{ Driver: pb.Storage_HDD, Memory: &pb.Memory{ Value: uint64(memTB), Unit: pb.Memory_TERABYTE, }, } return hdd } // NewScreen returns a new sample Screen func NewScreen() *pb.Screen { screen := &pb.Screen{ SizeInch: randomFloat32(13, 17), Resolution: randomScreenResolution(), Panel: randomScreenPanel(), Multitouch: randomBool(), } return screen } // NewLaptop returns a new sample Laptop func NewLaptop() *pb.Laptop { brand := randomLaptopBrand() name := randomLaptopName(brand) laptop := &pb.Laptop{ Id: randomID(), Brand: brand, Name: name, Cpu: NewCPU(), Ram: NewRAM(), Gpus: []*pb.GPU{NewGPU()}, Storages: []*pb.Storage{NewSSD(), NewHDD()}, Screen: NewScreen(), Keyboard: NewKeyboard(), Weight: &pb.Laptop_WeightKg{ WeightKg: randomFloat64(1.0, 3.0), }, PriceUsd: randomFloat64(1500, 3500), ReleaseYear: uint32(randomInt(2015, 2019)), UpdatedAt: ptypes.TimestampNow(), } return laptop } // RandomLaptopScore returns a random laptop score func RandomLaptopScore() float64 { return float64(randomInt(1, 10)) } ================================================ FILE: sample/random.go ================================================ package sample import ( "math/rand" "time" "github.com/google/uuid" "gitlab.com/techschool/pcbook/pb" ) func init() { rand.Seed(time.Now().UnixNano()) } func randomStringFromSet(a ...string) string { n := len(a) if n == 0 { return "" } return a[rand.Intn(n)] } func randomBool() bool { return rand.Intn(2) == 1 } func randomInt(min, max int) int { return min + rand.Int()%(max-min+1) } func randomFloat64(min, max float64) float64 { return min + rand.Float64()*(max-min) } func randomFloat32(min, max float32) float32 { return min + rand.Float32()*(max-min) } func randomID() string { return uuid.New().String() } func randomKeyboardLayout() pb.Keyboard_Layout { switch rand.Intn(3) { case 1: return pb.Keyboard_QWERTY case 2: return pb.Keyboard_QWERTZ default: return pb.Keyboard_AZERTY } } func randomScreenResolution() *pb.Screen_Resolution { height := randomInt(1080, 4320) width := height * 16 / 9 resolution := &pb.Screen_Resolution{ Width: uint32(width), Height: uint32(height), } return resolution } func randomScreenPanel() pb.Screen_Panel { if rand.Intn(2) == 1 { return pb.Screen_IPS } return pb.Screen_OLED } func randomCPUBrand() string { return randomStringFromSet("Intel", "AMD") } func randomCPUName(brand string) string { if brand == "Intel" { return randomStringFromSet( "Xeon E-2286M", "Core i9-9980HK", "Core i7-9750H", "Core i5-9400F", "Core i3-1005G1", ) } return randomStringFromSet( "Ryzen 7 PRO 2700U", "Ryzen 5 PRO 3500U", "Ryzen 3 PRO 3200GE", ) } func randomGPUBrand() string { return randomStringFromSet("Nvidia", "AMD") } func randomGPUName(brand string) string { if brand == "Nvidia" { return randomStringFromSet( "RTX 2060", "RTX 2070", "GTX 1660-Ti", "GTX 1070", ) } return randomStringFromSet( "RX 590", "RX 580", "RX 5700-XT", "RX Vega-56", ) } func randomLaptopBrand() string { return randomStringFromSet("Apple", "Dell", "Lenovo") } func randomLaptopName(brand string) string { switch brand { case "Apple": return randomStringFromSet("Macbook Air", "Macbook Pro") case "Dell": return randomStringFromSet("Latitude", "Vostro", "XPS", "Alienware") default: return randomStringFromSet("Thinkpad X1", "Thinkpad P1", "Thinkpad P53") } } ================================================ FILE: serializer/file.go ================================================ package serializer import ( "fmt" "io/ioutil" "github.com/golang/protobuf/proto" ) // WriteProtobufToJSONFile writes protocol buffer message to JSON file func WriteProtobufToJSONFile(message proto.Message, filename string) error { data, err := ProtobufToJSON(message) if err != nil { return fmt.Errorf("cannot marshal proto message to JSON: %w", err) } err = ioutil.WriteFile(filename, []byte(data), 0644) if err != nil { return fmt.Errorf("cannot write JSON data to file: %w", err) } return nil } // WriteProtobufToBinaryFile writes protocol buffer message to binary file func WriteProtobufToBinaryFile(message proto.Message, filename string) error { data, err := proto.Marshal(message) if err != nil { return fmt.Errorf("cannot marshal proto message to binary: %w", err) } err = ioutil.WriteFile(filename, data, 0644) if err != nil { return fmt.Errorf("cannot write binary data to file: %w", err) } return nil } // ReadProtobufFromBinaryFile reads protocol buffer message from binary file func ReadProtobufFromBinaryFile(filename string, message proto.Message) error { data, err := ioutil.ReadFile(filename) if err != nil { return fmt.Errorf("cannot read binary data from file: %w", err) } err = proto.Unmarshal(data, message) if err != nil { return fmt.Errorf("cannot unmarshal binary to proto message: %w", err) } return nil } ================================================ FILE: serializer/file_test.go ================================================ package serializer_test import ( "testing" "github.com/golang/protobuf/proto" "github.com/stretchr/testify/require" "gitlab.com/techschool/pcbook/pb" "gitlab.com/techschool/pcbook/sample" "gitlab.com/techschool/pcbook/serializer" ) func TestFileSerializer(t *testing.T) { t.Parallel() binaryFile := "../tmp/laptop.bin" jsonFile := "../tmp/laptop.json" laptop1 := sample.NewLaptop() err := serializer.WriteProtobufToBinaryFile(laptop1, binaryFile) require.NoError(t, err) err = serializer.WriteProtobufToJSONFile(laptop1, jsonFile) require.NoError(t, err) laptop2 := &pb.Laptop{} err = serializer.ReadProtobufFromBinaryFile(binaryFile, laptop2) require.NoError(t, err) require.True(t, proto.Equal(laptop1, laptop2)) } ================================================ FILE: serializer/json.go ================================================ package serializer import ( "github.com/golang/protobuf/jsonpb" "github.com/golang/protobuf/proto" ) // ProtobufToJSON converts protocol buffer message to JSON string func ProtobufToJSON(message proto.Message) (string, error) { marshaler := jsonpb.Marshaler{ EnumsAsInts: false, EmitDefaults: true, Indent: " ", OrigName: true, } return marshaler.MarshalToString(message) } // JSONToProtobufMessage converts JSON string to protocol buffer message func JSONToProtobufMessage(data string, message proto.Message) error { return jsonpb.UnmarshalString(data, message) } ================================================ FILE: service/auth_interceptor.go ================================================ package service import ( "context" "log" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) // AuthInterceptor is a server interceptor for authentication and authorization type AuthInterceptor struct { jwtManager *JWTManager accessibleRoles map[string][]string } // NewAuthInterceptor returns a new auth interceptor func NewAuthInterceptor(jwtManager *JWTManager, accessibleRoles map[string][]string) *AuthInterceptor { return &AuthInterceptor{jwtManager, accessibleRoles} } // Unary returns a server interceptor function to authenticate and authorize unary RPC func (interceptor *AuthInterceptor) Unary() grpc.UnaryServerInterceptor { return func( ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, ) (interface{}, error) { log.Println("--> unary interceptor: ", info.FullMethod) err := interceptor.authorize(ctx, info.FullMethod) if err != nil { return nil, err } return handler(ctx, req) } } // Stream returns a server interceptor function to authenticate and authorize stream RPC func (interceptor *AuthInterceptor) Stream() grpc.StreamServerInterceptor { return func( srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler, ) error { log.Println("--> stream interceptor: ", info.FullMethod) err := interceptor.authorize(stream.Context(), info.FullMethod) if err != nil { return err } return handler(srv, stream) } } func (interceptor *AuthInterceptor) authorize(ctx context.Context, method string) error { accessibleRoles, ok := interceptor.accessibleRoles[method] if !ok { // everyone can access return nil } md, ok := metadata.FromIncomingContext(ctx) if !ok { return status.Errorf(codes.Unauthenticated, "metadata is not provided") } values := md["authorization"] if len(values) == 0 { return status.Errorf(codes.Unauthenticated, "authorization token is not provided") } accessToken := values[0] claims, err := interceptor.jwtManager.Verify(accessToken) if err != nil { return status.Errorf(codes.Unauthenticated, "access token is invalid: %v", err) } for _, role := range accessibleRoles { if role == claims.Role { return nil } } return status.Error(codes.PermissionDenied, "no permission to access this RPC") } ================================================ FILE: service/auth_server.go ================================================ package service import ( "context" "gitlab.com/techschool/pcbook/pb" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // AuthServer is the server for authentication type AuthServer struct { pb.UnimplementedAuthServiceServer userStore UserStore jwtManager *JWTManager } // NewAuthServer returns a new auth server func NewAuthServer(userStore UserStore, jwtManager *JWTManager) pb.AuthServiceServer { return &AuthServer{userStore: userStore, jwtManager: jwtManager} } // Login is a unary RPC to login user func (server *AuthServer) Login(ctx context.Context, req *pb.LoginRequest) (*pb.LoginResponse, error) { user, err := server.userStore.Find(req.GetUsername()) if err != nil { return nil, status.Errorf(codes.Internal, "cannot find user: %v", err) } if user == nil || !user.IsCorrectPassword(req.GetPassword()) { return nil, status.Errorf(codes.NotFound, "incorrect username/password") } token, err := server.jwtManager.Generate(user) if err != nil { return nil, status.Errorf(codes.Internal, "cannot generate access token") } res := &pb.LoginResponse{AccessToken: token} return res, nil } ================================================ FILE: service/image_store.go ================================================ package service import ( "bytes" "fmt" "os" "sync" "github.com/google/uuid" ) // ImageStore is an interface to store laptop images type ImageStore interface { // Save saves a new laptop image to the store Save(laptopID string, imageType string, imageData bytes.Buffer) (string, error) } // DiskImageStore stores image on disk, and its info on memory type DiskImageStore struct { mutex sync.RWMutex imageFolder string images map[string]*ImageInfo } // ImageInfo contains information of the laptop image type ImageInfo struct { LaptopID string Type string Path string } // NewDiskImageStore returns a new DiskImageStore func NewDiskImageStore(imageFolder string) *DiskImageStore { return &DiskImageStore{ imageFolder: imageFolder, images: make(map[string]*ImageInfo), } } // Save adds a new image to a laptop func (store *DiskImageStore) Save( laptopID string, imageType string, imageData bytes.Buffer, ) (string, error) { imageID, err := uuid.NewRandom() if err != nil { return "", fmt.Errorf("cannot generate image id: %w", err) } imagePath := fmt.Sprintf("%s/%s%s", store.imageFolder, imageID, imageType) file, err := os.Create(imagePath) if err != nil { return "", fmt.Errorf("cannot create image file: %w", err) } _, err = imageData.WriteTo(file) if err != nil { return "", fmt.Errorf("cannot write image to file: %w", err) } store.mutex.Lock() defer store.mutex.Unlock() store.images[imageID.String()] = &ImageInfo{ LaptopID: laptopID, Type: imageType, Path: imagePath, } return imageID.String(), nil } ================================================ FILE: service/jwt_manager.go ================================================ package service import ( "fmt" "time" "github.com/dgrijalva/jwt-go" ) // JWTManager is a JSON web token manager type JWTManager struct { secretKey string tokenDuration time.Duration } // UserClaims is a custom JWT claims that contains some user's information type UserClaims struct { jwt.StandardClaims Username string `json:"username"` Role string `json:"role"` } // NewJWTManager returns a new JWT manager func NewJWTManager(secretKey string, tokenDuration time.Duration) *JWTManager { return &JWTManager{secretKey, tokenDuration} } // Generate generates and signs a new token for a user func (manager *JWTManager) Generate(user *User) (string, error) { claims := UserClaims{ StandardClaims: jwt.StandardClaims{ ExpiresAt: time.Now().Add(manager.tokenDuration).Unix(), }, Username: user.Username, Role: user.Role, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(manager.secretKey)) } // Verify verifies the access token string and return a user claim if the token is valid func (manager *JWTManager) Verify(accessToken string) (*UserClaims, error) { token, err := jwt.ParseWithClaims( accessToken, &UserClaims{}, func(token *jwt.Token) (interface{}, error) { _, ok := token.Method.(*jwt.SigningMethodHMAC) if !ok { return nil, fmt.Errorf("unexpected token signing method") } return []byte(manager.secretKey), nil }, ) if err != nil { return nil, fmt.Errorf("invalid token: %w", err) } claims, ok := token.Claims.(*UserClaims) if !ok { return nil, fmt.Errorf("invalid token claims") } return claims, nil } ================================================ FILE: service/laptop_client_test.go ================================================ package service_test import ( "bufio" "context" "fmt" "io" "net" "os" "path/filepath" "testing" "github.com/stretchr/testify/require" "gitlab.com/techschool/pcbook/pb" "gitlab.com/techschool/pcbook/sample" "gitlab.com/techschool/pcbook/serializer" "gitlab.com/techschool/pcbook/service" "google.golang.org/grpc" ) func TestClientCreateLaptop(t *testing.T) { t.Parallel() laptopStore := service.NewInMemoryLaptopStore() serverAddress := startTestLaptopServer(t, laptopStore, nil, nil) laptopClient := newTestLaptopClient(t, serverAddress) laptop := sample.NewLaptop() expectedID := laptop.Id req := &pb.CreateLaptopRequest{ Laptop: laptop, } res, err := laptopClient.CreateLaptop(context.Background(), req) require.NoError(t, err) require.NotNil(t, res) require.Equal(t, expectedID, res.Id) // check that the laptop is saved to the store other, err := laptopStore.Find(res.Id) require.NoError(t, err) require.NotNil(t, other) // check that the saved laptop is the same as the one we send requireSameLaptop(t, laptop, other) } func TestClientSearchLaptop(t *testing.T) { t.Parallel() filter := &pb.Filter{ MaxPriceUsd: 2000, MinCpuCores: 4, MinCpuGhz: 2.2, MinRam: &pb.Memory{Value: 8, Unit: pb.Memory_GIGABYTE}, } laptopStore := service.NewInMemoryLaptopStore() expectedIDs := make(map[string]bool) for i := 0; i < 6; i++ { laptop := sample.NewLaptop() switch i { case 0: laptop.PriceUsd = 2500 case 1: laptop.Cpu.NumberCores = 2 case 2: laptop.Cpu.MinGhz = 2.0 case 3: laptop.Ram = &pb.Memory{Value: 4096, Unit: pb.Memory_MEGABYTE} case 4: laptop.PriceUsd = 1999 laptop.Cpu.NumberCores = 4 laptop.Cpu.MinGhz = 2.5 laptop.Cpu.MaxGhz = laptop.Cpu.MinGhz + 2.0 laptop.Ram = &pb.Memory{Value: 16, Unit: pb.Memory_GIGABYTE} expectedIDs[laptop.Id] = true case 5: laptop.PriceUsd = 2000 laptop.Cpu.NumberCores = 6 laptop.Cpu.MinGhz = 2.8 laptop.Cpu.MaxGhz = laptop.Cpu.MinGhz + 2.0 laptop.Ram = &pb.Memory{Value: 64, Unit: pb.Memory_GIGABYTE} expectedIDs[laptop.Id] = true } err := laptopStore.Save(laptop) require.NoError(t, err) } serverAddress := startTestLaptopServer(t, laptopStore, nil, nil) laptopClient := newTestLaptopClient(t, serverAddress) req := &pb.SearchLaptopRequest{Filter: filter} stream, err := laptopClient.SearchLaptop(context.Background(), req) require.NoError(t, err) found := 0 for { res, err := stream.Recv() if err == io.EOF { break } require.NoError(t, err) require.Contains(t, expectedIDs, res.GetLaptop().GetId()) found += 1 } require.Equal(t, len(expectedIDs), found) } func TestClientUploadImage(t *testing.T) { t.Parallel() testImageFolder := "../tmp" laptopStore := service.NewInMemoryLaptopStore() imageStore := service.NewDiskImageStore(testImageFolder) laptop := sample.NewLaptop() err := laptopStore.Save(laptop) require.NoError(t, err) serverAddress := startTestLaptopServer(t, laptopStore, imageStore, nil) laptopClient := newTestLaptopClient(t, serverAddress) imagePath := fmt.Sprintf("%s/laptop.jpg", testImageFolder) file, err := os.Open(imagePath) require.NoError(t, err) defer file.Close() stream, err := laptopClient.UploadImage(context.Background()) require.NoError(t, err) imageType := filepath.Ext(imagePath) req := &pb.UploadImageRequest{ Data: &pb.UploadImageRequest_Info{ Info: &pb.ImageInfo{ LaptopId: laptop.GetId(), ImageType: imageType, }, }, } err = stream.Send(req) require.NoError(t, err) reader := bufio.NewReader(file) buffer := make([]byte, 1024) size := 0 for { n, err := reader.Read(buffer) if err == io.EOF { break } require.NoError(t, err) size += n req := &pb.UploadImageRequest{ Data: &pb.UploadImageRequest_ChunkData{ ChunkData: buffer[:n], }, } err = stream.Send(req) require.NoError(t, err) } res, err := stream.CloseAndRecv() require.NoError(t, err) require.NotZero(t, res.GetId()) require.EqualValues(t, size, res.GetSize()) savedImagePath := fmt.Sprintf("%s/%s%s", testImageFolder, res.GetId(), imageType) require.FileExists(t, savedImagePath) require.NoError(t, os.Remove(savedImagePath)) } func TestClientRateLaptop(t *testing.T) { t.Parallel() laptopStore := service.NewInMemoryLaptopStore() ratingStore := service.NewInMemoryRatingStore() laptop := sample.NewLaptop() err := laptopStore.Save(laptop) require.NoError(t, err) serverAddress := startTestLaptopServer(t, laptopStore, nil, ratingStore) laptopClient := newTestLaptopClient(t, serverAddress) stream, err := laptopClient.RateLaptop(context.Background()) require.NoError(t, err) scores := []float64{8, 7.5, 10} averages := []float64{8, 7.75, 8.5} n := len(scores) for i := 0; i < n; i++ { req := &pb.RateLaptopRequest{ LaptopId: laptop.GetId(), Score: scores[i], } err := stream.Send(req) require.NoError(t, err) } err = stream.CloseSend() require.NoError(t, err) for idx := 0; ; idx++ { res, err := stream.Recv() if err == io.EOF { require.Equal(t, n, idx) return } require.NoError(t, err) require.Equal(t, laptop.GetId(), res.GetLaptopId()) require.Equal(t, uint32(idx+1), res.GetRatedCount()) require.Equal(t, averages[idx], res.GetAverageScore()) } } func startTestLaptopServer(t *testing.T, laptopStore service.LaptopStore, imageStore service.ImageStore, ratingStore service.RatingStore) string { laptopServer := service.NewLaptopServer(laptopStore, imageStore, ratingStore) grpcServer := grpc.NewServer() pb.RegisterLaptopServiceServer(grpcServer, laptopServer) listener, err := net.Listen("tcp", ":0") // random available port require.NoError(t, err) go grpcServer.Serve(listener) return listener.Addr().String() } func newTestLaptopClient(t *testing.T, serverAddress string) pb.LaptopServiceClient { conn, err := grpc.Dial(serverAddress, grpc.WithInsecure()) require.NoError(t, err) return pb.NewLaptopServiceClient(conn) } func requireSameLaptop(t *testing.T, laptop1 *pb.Laptop, laptop2 *pb.Laptop) { json1, err := serializer.ProtobufToJSON(laptop1) require.NoError(t, err) json2, err := serializer.ProtobufToJSON(laptop2) require.NoError(t, err) require.Equal(t, json1, json2) } ================================================ FILE: service/laptop_server.go ================================================ package service import ( "bytes" "context" "errors" "io" "log" "github.com/google/uuid" "gitlab.com/techschool/pcbook/pb" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) const maxImageSize = 1 << 20 // LaptopServer is the server that provides laptop services type LaptopServer struct { pb.UnimplementedLaptopServiceServer laptopStore LaptopStore imageStore ImageStore ratingStore RatingStore } // NewLaptopServer returns a new LaptopServer func NewLaptopServer(laptopStore LaptopStore, imageStore ImageStore, ratingStore RatingStore) *LaptopServer { return &LaptopServer{ laptopStore: laptopStore, imageStore: imageStore, ratingStore: ratingStore, } } // CreateLaptop is a unary RPC to create a new laptop func (server *LaptopServer) CreateLaptop( ctx context.Context, req *pb.CreateLaptopRequest, ) (*pb.CreateLaptopResponse, error) { laptop := req.GetLaptop() log.Printf("receive a create-laptop request with id: %s", laptop.Id) if len(laptop.Id) > 0 { // check if it's a valid UUID _, err := uuid.Parse(laptop.Id) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "laptop ID is not a valid UUID: %v", err) } } else { id, err := uuid.NewRandom() if err != nil { return nil, status.Errorf(codes.Internal, "cannot generate a new laptop ID: %v", err) } laptop.Id = id.String() } // some heavy processing // time.Sleep(6 * time.Second) if err := contextError(ctx); err != nil { return nil, err } // save the laptop to store err := server.laptopStore.Save(laptop) if err != nil { code := codes.Internal if errors.Is(err, ErrAlreadyExists) { code = codes.AlreadyExists } return nil, status.Errorf(code, "cannot save laptop to the store: %v", err) } log.Printf("saved laptop with id: %s", laptop.Id) res := &pb.CreateLaptopResponse{ Id: laptop.Id, } return res, nil } // SearchLaptop is a server-streaming RPC to search for laptops func (server *LaptopServer) SearchLaptop( req *pb.SearchLaptopRequest, stream pb.LaptopService_SearchLaptopServer, ) error { filter := req.GetFilter() log.Printf("receive a search-laptop request with filter: %v", filter) err := server.laptopStore.Search( stream.Context(), filter, func(laptop *pb.Laptop) error { res := &pb.SearchLaptopResponse{Laptop: laptop} err := stream.Send(res) if err != nil { return err } log.Printf("sent laptop with id: %s", laptop.GetId()) return nil }, ) if err != nil { return status.Errorf(codes.Internal, "unexpected error: %v", err) } return nil } // UploadImage is a client-streaming RPC to upload a laptop image func (server *LaptopServer) UploadImage(stream pb.LaptopService_UploadImageServer) error { req, err := stream.Recv() if err != nil { return logError(status.Errorf(codes.Unknown, "cannot receive image info")) } laptopID := req.GetInfo().GetLaptopId() imageType := req.GetInfo().GetImageType() log.Printf("receive an upload-image request for laptop %s with image type %s", laptopID, imageType) laptop, err := server.laptopStore.Find(laptopID) if err != nil { return logError(status.Errorf(codes.Internal, "cannot find laptop: %v", err)) } if laptop == nil { return logError(status.Errorf(codes.InvalidArgument, "laptop id %s doesn't exist", laptopID)) } imageData := bytes.Buffer{} imageSize := 0 for { err := contextError(stream.Context()) if err != nil { return err } log.Print("waiting to receive more data") req, err := stream.Recv() if err == io.EOF { log.Print("no more data") break } if err != nil { return logError(status.Errorf(codes.Unknown, "cannot receive chunk data: %v", err)) } chunk := req.GetChunkData() size := len(chunk) log.Printf("received a chunk with size: %d", size) imageSize += size if imageSize > maxImageSize { return logError(status.Errorf(codes.InvalidArgument, "image is too large: %d > %d", imageSize, maxImageSize)) } // write slowly // time.Sleep(time.Second) _, err = imageData.Write(chunk) if err != nil { return logError(status.Errorf(codes.Internal, "cannot write chunk data: %v", err)) } } imageID, err := server.imageStore.Save(laptopID, imageType, imageData) if err != nil { return logError(status.Errorf(codes.Internal, "cannot save image to the store: %v", err)) } res := &pb.UploadImageResponse{ Id: imageID, Size: uint32(imageSize), } err = stream.SendAndClose(res) if err != nil { return logError(status.Errorf(codes.Unknown, "cannot send response: %v", err)) } log.Printf("saved image with id: %s, size: %d", imageID, imageSize) return nil } // RateLaptop is a bidirectional-streaming RPC that allows client to rate a stream of laptops // with a score, and returns a stream of average score for each of them func (server *LaptopServer) RateLaptop(stream pb.LaptopService_RateLaptopServer) error { for { err := contextError(stream.Context()) if err != nil { return err } req, err := stream.Recv() if err == io.EOF { log.Print("no more data") break } if err != nil { return logError(status.Errorf(codes.Unknown, "cannot receive stream request: %v", err)) } laptopID := req.GetLaptopId() score := req.GetScore() log.Printf("received a rate-laptop request: id = %s, score = %.2f", laptopID, score) found, err := server.laptopStore.Find(laptopID) if err != nil { return logError(status.Errorf(codes.Internal, "cannot find laptop: %v", err)) } if found == nil { return logError(status.Errorf(codes.NotFound, "laptopID %s is not found", laptopID)) } rating, err := server.ratingStore.Add(laptopID, score) if err != nil { return logError(status.Errorf(codes.Internal, "cannot add rating to the store: %v", err)) } res := &pb.RateLaptopResponse{ LaptopId: laptopID, RatedCount: rating.Count, AverageScore: rating.Sum / float64(rating.Count), } err = stream.Send(res) if err != nil { return logError(status.Errorf(codes.Unknown, "cannot send stream response: %v", err)) } } return nil } func contextError(ctx context.Context) error { switch ctx.Err() { case context.Canceled: return logError(status.Error(codes.Canceled, "request is canceled")) case context.DeadlineExceeded: return logError(status.Error(codes.DeadlineExceeded, "deadline is exceeded")) default: return nil } } func logError(err error) error { if err != nil { log.Print(err) } return err } ================================================ FILE: service/laptop_server_test.go ================================================ package service_test import ( "context" "testing" "github.com/stretchr/testify/require" "gitlab.com/techschool/pcbook/pb" "gitlab.com/techschool/pcbook/sample" "gitlab.com/techschool/pcbook/service" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func TestServerCreateLaptop(t *testing.T) { t.Parallel() laptopNoID := sample.NewLaptop() laptopNoID.Id = "" laptopInvalidID := sample.NewLaptop() laptopInvalidID.Id = "invalid-uuid" laptopDuplicateID := sample.NewLaptop() storeDuplicateID := service.NewInMemoryLaptopStore() err := storeDuplicateID.Save(laptopDuplicateID) require.Nil(t, err) testCases := []struct { name string laptop *pb.Laptop store service.LaptopStore code codes.Code }{ { name: "success_with_id", laptop: sample.NewLaptop(), store: service.NewInMemoryLaptopStore(), code: codes.OK, }, { name: "success_no_id", laptop: laptopNoID, store: service.NewInMemoryLaptopStore(), code: codes.OK, }, { name: "failure_invalid_id", laptop: laptopInvalidID, store: service.NewInMemoryLaptopStore(), code: codes.InvalidArgument, }, { name: "failure_duplicate_id", laptop: laptopDuplicateID, store: storeDuplicateID, code: codes.AlreadyExists, }, } for i := range testCases { tc := testCases[i] t.Run(tc.name, func(t *testing.T) { t.Parallel() req := &pb.CreateLaptopRequest{ Laptop: tc.laptop, } server := service.NewLaptopServer(tc.store, nil, nil) res, err := server.CreateLaptop(context.Background(), req) if tc.code == codes.OK { require.NoError(t, err) require.NotNil(t, res) require.NotEmpty(t, res.Id) if len(tc.laptop.Id) > 0 { require.Equal(t, tc.laptop.Id, res.Id) } } else { require.Error(t, err) require.Nil(t, res) st, ok := status.FromError(err) require.True(t, ok) require.Equal(t, tc.code, st.Code()) } }) } } ================================================ FILE: service/laptop_store.go ================================================ package service import ( "context" "errors" "fmt" "log" "sync" "github.com/jinzhu/copier" "gitlab.com/techschool/pcbook/pb" ) // ErrAlreadyExists is returned when a record with the same ID already exists in the store var ErrAlreadyExists = errors.New("record already exists") // LaptopStore is an interface to store laptop type LaptopStore interface { // Save saves the laptop to the store Save(laptop *pb.Laptop) error // Find finds a laptop by ID Find(id string) (*pb.Laptop, error) // Search searches for laptops with filter, returns one by one via the found function Search(ctx context.Context, filter *pb.Filter, found func(laptop *pb.Laptop) error) error } // InMemoryLaptopStore stores laptop in memory type InMemoryLaptopStore struct { mutex sync.RWMutex data map[string]*pb.Laptop } // NewInMemoryLaptopStore returns a new InMemoryLaptopStore func NewInMemoryLaptopStore() *InMemoryLaptopStore { return &InMemoryLaptopStore{ data: make(map[string]*pb.Laptop), } } // Save saves the laptop to the store func (store *InMemoryLaptopStore) Save(laptop *pb.Laptop) error { store.mutex.Lock() defer store.mutex.Unlock() if store.data[laptop.Id] != nil { return ErrAlreadyExists } other, err := deepCopy(laptop) if err != nil { return err } store.data[other.Id] = other return nil } // Find finds a laptop by ID func (store *InMemoryLaptopStore) Find(id string) (*pb.Laptop, error) { store.mutex.RLock() defer store.mutex.RUnlock() laptop := store.data[id] if laptop == nil { return nil, nil } return deepCopy(laptop) } // Search searches for laptops with filter, returns one by one via the found function func (store *InMemoryLaptopStore) Search( ctx context.Context, filter *pb.Filter, found func(laptop *pb.Laptop) error, ) error { store.mutex.RLock() defer store.mutex.RUnlock() for _, laptop := range store.data { if ctx.Err() == context.Canceled || ctx.Err() == context.DeadlineExceeded { log.Print("context is cancelled") return nil } // time.Sleep(time.Second) // log.Print("checking laptop id: ", laptop.GetId()) if isQualified(filter, laptop) { other, err := deepCopy(laptop) if err != nil { return err } err = found(other) if err != nil { return err } } } return nil } func isQualified(filter *pb.Filter, laptop *pb.Laptop) bool { if laptop.GetPriceUsd() > filter.GetMaxPriceUsd() { return false } if laptop.GetCpu().GetNumberCores() < filter.GetMinCpuCores() { return false } if laptop.GetCpu().GetMinGhz() < filter.GetMinCpuGhz() { return false } if toBit(laptop.GetRam()) < toBit(filter.GetMinRam()) { return false } return true } func toBit(memory *pb.Memory) uint64 { value := memory.GetValue() switch memory.GetUnit() { case pb.Memory_BIT: return value case pb.Memory_BYTE: return value << 3 // 8 = 2^3 case pb.Memory_KILOBYTE: return value << 13 // 1024 * 8 = 2^10 * 2^3 = 2^13 case pb.Memory_MEGABYTE: return value << 23 case pb.Memory_GIGABYTE: return value << 33 case pb.Memory_TERABYTE: return value << 43 default: return 0 } } func deepCopy(laptop *pb.Laptop) (*pb.Laptop, error) { other := &pb.Laptop{} err := copier.Copy(other, laptop) if err != nil { return nil, fmt.Errorf("cannot copy laptop data: %w", err) } return other, nil } ================================================ FILE: service/rating_store.go ================================================ package service import "sync" // RatingStore is an interface to store laptop ratings type RatingStore interface { // Add adds a new laptop score to the store and returns its rating Add(laptopID string, score float64) (*Rating, error) } // Rating contains the rating information of a laptop type Rating struct { Count uint32 Sum float64 } // InMemoryRatingStore stores laptop ratings in memory type InMemoryRatingStore struct { mutex sync.RWMutex rating map[string]*Rating } // NewInMemoryRatingStore returns a new InMemoryRatingStore func NewInMemoryRatingStore() *InMemoryRatingStore { return &InMemoryRatingStore{ rating: make(map[string]*Rating), } } // Add adds a new laptop score to the store and returns its rating func (store *InMemoryRatingStore) Add(laptopID string, score float64) (*Rating, error) { store.mutex.Lock() defer store.mutex.Unlock() rating := store.rating[laptopID] if rating == nil { rating = &Rating{ Count: 1, Sum: score, } } else { rating.Count++ rating.Sum += score } store.rating[laptopID] = rating return rating, nil } ================================================ FILE: service/user.go ================================================ package service import ( "fmt" "golang.org/x/crypto/bcrypt" ) // User contains user's information type User struct { Username string HashedPassword string Role string } // NewUser returns a new user func NewUser(username string, password string, role string) (*User, error) { hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("cannot hash password: %w", err) } user := &User{ Username: username, HashedPassword: string(hashedPassword), Role: role, } return user, nil } // IsCorrectPassword checks if the provided password is correct or not func (user *User) IsCorrectPassword(password string) bool { err := bcrypt.CompareHashAndPassword([]byte(user.HashedPassword), []byte(password)) return err == nil } // Clone returns a clone of this user func (user *User) Clone() *User { return &User{ Username: user.Username, HashedPassword: user.HashedPassword, Role: user.Role, } } ================================================ FILE: service/user_store.go ================================================ package service import "sync" // UserStore is an interface to store users type UserStore interface { // Save saves a user to the store Save(user *User) error // Find finds a user by username Find(username string) (*User, error) } // InMemoryUserStore stores users in memory type InMemoryUserStore struct { mutex sync.RWMutex users map[string]*User } // NewInMemoryUserStore returns a new in-memory user store func NewInMemoryUserStore() *InMemoryUserStore { return &InMemoryUserStore{ users: make(map[string]*User), } } // Save saves a user to the store func (store *InMemoryUserStore) Save(user *User) error { store.mutex.Lock() defer store.mutex.Unlock() if store.users[user.Username] != nil { return ErrAlreadyExists } store.users[user.Username] = user.Clone() return nil } // Find finds a user by username func (store *InMemoryUserStore) Find(username string) (*User, error) { store.mutex.RLock() defer store.mutex.RUnlock() user := store.users[username] if user == nil { return nil, nil } return user.Clone(), nil } ================================================ FILE: swagger/auth_service.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "auth_service.proto", "version": "version not set" }, "tags": [ { "name": "AuthService" } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": { "/v1/auth/login": { "post": { "operationId": "AuthService_Login", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/pcbookLoginResponse" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/pcbookLoginRequest" } } ], "tags": [ "AuthService" ] } } }, "definitions": { "pcbookLoginRequest": { "type": "object", "properties": { "username": { "type": "string" }, "password": { "type": "string" } } }, "pcbookLoginResponse": { "type": "object", "properties": { "accessToken": { "type": "string" } } }, "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: swagger/filter_message.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "filter_message.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: swagger/keyboard_message.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "keyboard_message.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: swagger/laptop_message.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "laptop_message.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: swagger/laptop_service.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "laptop_service.proto", "version": "version not set" }, "tags": [ { "name": "LaptopService" } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": { "/v1/laptop/create": { "post": { "operationId": "LaptopService_CreateLaptop", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/pcbookCreateLaptopResponse" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/pcbookCreateLaptopRequest" } } ], "tags": [ "LaptopService" ] } }, "/v1/laptop/rate": { "post": { "operationId": "LaptopService_RateLaptop", "responses": { "200": { "description": "A successful response.(streaming responses)", "schema": { "type": "object", "properties": { "result": { "$ref": "#/definitions/pcbookRateLaptopResponse" }, "error": { "$ref": "#/definitions/rpcStatus" } }, "title": "Stream result of pcbookRateLaptopResponse" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "body", "description": " (streaming inputs)", "in": "body", "required": true, "schema": { "$ref": "#/definitions/pcbookRateLaptopRequest" } } ], "tags": [ "LaptopService" ] } }, "/v1/laptop/search": { "get": { "operationId": "LaptopService_SearchLaptop", "responses": { "200": { "description": "A successful response.(streaming responses)", "schema": { "type": "object", "properties": { "result": { "$ref": "#/definitions/pcbookSearchLaptopResponse" }, "error": { "$ref": "#/definitions/rpcStatus" } }, "title": "Stream result of pcbookSearchLaptopResponse" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "filter.maxPriceUsd", "in": "query", "required": false, "type": "number", "format": "double" }, { "name": "filter.minCpuCores", "in": "query", "required": false, "type": "integer", "format": "int64" }, { "name": "filter.minCpuGhz", "in": "query", "required": false, "type": "number", "format": "double" }, { "name": "filter.minRam.value", "in": "query", "required": false, "type": "string", "format": "uint64" }, { "name": "filter.minRam.unit", "in": "query", "required": false, "type": "string", "enum": [ "UNKNOWN", "BIT", "BYTE", "KILOBYTE", "MEGABYTE", "GIGABYTE", "TERABYTE" ], "default": "UNKNOWN" } ], "tags": [ "LaptopService" ] } }, "/v1/laptop/upload_image": { "post": { "operationId": "LaptopService_UploadImage", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/pcbookUploadImageResponse" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "body", "description": " (streaming inputs)", "in": "body", "required": true, "schema": { "$ref": "#/definitions/pcbookUploadImageRequest" } } ], "tags": [ "LaptopService" ] } } }, "definitions": { "KeyboardLayout": { "type": "string", "enum": [ "UNKNOWN", "QWERTY", "QWERTZ", "AZERTY" ], "default": "UNKNOWN" }, "MemoryUnit": { "type": "string", "enum": [ "UNKNOWN", "BIT", "BYTE", "KILOBYTE", "MEGABYTE", "GIGABYTE", "TERABYTE" ], "default": "UNKNOWN" }, "ScreenPanel": { "type": "string", "enum": [ "UNKNOWN", "IPS", "OLED" ], "default": "UNKNOWN" }, "ScreenResolution": { "type": "object", "properties": { "width": { "type": "integer", "format": "int64" }, "height": { "type": "integer", "format": "int64" } } }, "StorageDriver": { "type": "string", "enum": [ "UNKNOWN", "HDD", "SSD" ], "default": "UNKNOWN" }, "pcbookCPU": { "type": "object", "properties": { "brand": { "type": "string" }, "name": { "type": "string" }, "numberCores": { "type": "integer", "format": "int64" }, "numberThreads": { "type": "integer", "format": "int64" }, "minGhz": { "type": "number", "format": "double" }, "maxGhz": { "type": "number", "format": "double" } } }, "pcbookCreateLaptopRequest": { "type": "object", "properties": { "laptop": { "$ref": "#/definitions/pcbookLaptop" } } }, "pcbookCreateLaptopResponse": { "type": "object", "properties": { "id": { "type": "string" } } }, "pcbookFilter": { "type": "object", "properties": { "maxPriceUsd": { "type": "number", "format": "double" }, "minCpuCores": { "type": "integer", "format": "int64" }, "minCpuGhz": { "type": "number", "format": "double" }, "minRam": { "$ref": "#/definitions/pcbookMemory" } } }, "pcbookGPU": { "type": "object", "properties": { "brand": { "type": "string" }, "name": { "type": "string" }, "minGhz": { "type": "number", "format": "double" }, "maxGhz": { "type": "number", "format": "double" }, "memory": { "$ref": "#/definitions/pcbookMemory" } } }, "pcbookImageInfo": { "type": "object", "properties": { "laptopId": { "type": "string" }, "imageType": { "type": "string" } } }, "pcbookKeyboard": { "type": "object", "properties": { "layout": { "$ref": "#/definitions/KeyboardLayout" }, "backlit": { "type": "boolean" } } }, "pcbookLaptop": { "type": "object", "properties": { "id": { "type": "string" }, "brand": { "type": "string" }, "name": { "type": "string" }, "cpu": { "$ref": "#/definitions/pcbookCPU" }, "ram": { "$ref": "#/definitions/pcbookMemory" }, "gpus": { "type": "array", "items": { "$ref": "#/definitions/pcbookGPU" } }, "storages": { "type": "array", "items": { "$ref": "#/definitions/pcbookStorage" } }, "screen": { "$ref": "#/definitions/pcbookScreen" }, "keyboard": { "$ref": "#/definitions/pcbookKeyboard" }, "weightKg": { "type": "number", "format": "double" }, "weightLb": { "type": "number", "format": "double" }, "priceUsd": { "type": "number", "format": "double" }, "releaseYear": { "type": "integer", "format": "int64" }, "updatedAt": { "type": "string", "format": "date-time" } } }, "pcbookMemory": { "type": "object", "properties": { "value": { "type": "string", "format": "uint64" }, "unit": { "$ref": "#/definitions/MemoryUnit" } } }, "pcbookRateLaptopRequest": { "type": "object", "properties": { "laptopId": { "type": "string" }, "score": { "type": "number", "format": "double" } } }, "pcbookRateLaptopResponse": { "type": "object", "properties": { "laptopId": { "type": "string" }, "ratedCount": { "type": "integer", "format": "int64" }, "averageScore": { "type": "number", "format": "double" } } }, "pcbookScreen": { "type": "object", "properties": { "sizeInch": { "type": "number", "format": "float" }, "resolution": { "$ref": "#/definitions/ScreenResolution" }, "panel": { "$ref": "#/definitions/ScreenPanel" }, "multitouch": { "type": "boolean" } } }, "pcbookSearchLaptopResponse": { "type": "object", "properties": { "laptop": { "$ref": "#/definitions/pcbookLaptop" } } }, "pcbookStorage": { "type": "object", "properties": { "driver": { "$ref": "#/definitions/StorageDriver" }, "memory": { "$ref": "#/definitions/pcbookMemory" } } }, "pcbookUploadImageRequest": { "type": "object", "properties": { "info": { "$ref": "#/definitions/pcbookImageInfo" }, "chunkData": { "type": "string", "format": "byte" } } }, "pcbookUploadImageResponse": { "type": "object", "properties": { "id": { "type": "string" }, "size": { "type": "integer", "format": "int64" } } }, "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: swagger/memory_message.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "memory_message.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: swagger/processor_message.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "processor_message.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: swagger/screen_message.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "screen_message.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: swagger/storage_message.swagger.json ================================================ { "swagger": "2.0", "info": { "title": "storage_message.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "typeUrl": { "type": "string" }, "value": { "type": "string", "format": "byte" } } }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } ================================================ FILE: tmp/laptop.json ================================================ { "id": "22901c4c-2f34-43b6-8190-e3a57daa1a41", "brand": "Apple", "name": "Macbook Air", "cpu": { "brand": "AMD", "name": "Ryzen 7 PRO 2700U", "number_cores": 2, "number_threads": 6, "min_ghz": 2.678712980507639, "max_ghz": 4.812780681764419 }, "ram": { "value": "55", "unit": "GIGABYTE" }, "gpus": [ { "brand": "AMD", "name": "RX Vega-56", "min_ghz": 1.3477295700901486, "max_ghz": 1.707348476579961, "memory": { "value": "6", "unit": "GIGABYTE" } } ], "storages": [ { "driver": "SSD", "memory": { "value": "221", "unit": "GIGABYTE" } }, { "driver": "HDD", "memory": { "value": "6", "unit": "TERABYTE" } } ], "screen": { "size_inch": 16.203865, "resolution": { "width": 2158, "height": 1214 }, "panel": "IPS", "multitouch": true }, "keyboard": { "layout": "QWERTY", "backlit": true }, "weight_kg": 1.8918947371347534, "price_usd": 2316.488298992809, "release_year": 2017, "updated_at": "2021-02-02T20:54:24.526251Z" }