├── pkg
└── entities
│ ├── topic.go
│ └── research.go
├── api
├── routes
│ ├── topic.go
│ └── research.go
├── presenter
│ ├── topic.go
│ └── research.go
├── app.go
└── handlers
│ ├── topic_handler.go
│ └── research_handler.go
├── .gitignore
├── go.mod
├── README.md
└── go.sum
/pkg/entities/topic.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | import "time"
4 |
5 | type Topic struct {
6 | ID string `json:"id"`
7 | Title string `json:"title"`
8 | CreatedAt time.Time `json:"createdAt" `
9 | UpdatedAt time.Time `json:"updatedAt" `
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/entities/research.go:
--------------------------------------------------------------------------------
1 | package entities
2 |
3 | import "time"
4 |
5 | // Research Constructs your research model under entities.
6 | type Research struct {
7 | ID string `json:"id"`
8 | Title string `json:"title"`
9 | Content string `json:"content"`
10 | TopicID string `json:"topic_id"`
11 | CreatedAt time.Time `json:"createdAt"`
12 | UpdatedAt time.Time `json:"updatedAt"`
13 | }
14 |
--------------------------------------------------------------------------------
/api/routes/topic.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import (
4 | "minerva_api/api/handlers"
5 |
6 | firebase "firebase.google.com/go"
7 | "github.com/gofiber/fiber/v2"
8 | )
9 |
10 | // TopicRouter is the Router for GoFiber App
11 | func TopicRouter(app *fiber.App, appFire *firebase.App) {
12 | app.Get("/topics", handlers.GetTopics(appFire))
13 | app.Post("/topics", handlers.AddTopic(appFire))
14 | app.Put("/topics", handlers.UpdateTopic(appFire))
15 | app.Delete("/topics", handlers.RemoveTopic(appFire))
16 | }
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | notes.txt
2 | key.json
3 | apikey.txt
4 |
5 | #My project's gitignore wasn't working before, here is why and the solution :
6 |
7 | # The files/folder in your version control will not just delete themselves just because you added them to the .gitignore.
8 | # They are already in the repository and you have to remove them. You can just do that with this:
9 |
10 | # Remember to commit everything you've changed before you do this!
11 |
12 | # git rm -rf --cached .
13 | # git add .
14 | # This removes all files from the repository and adds them back (this time respecting the rules in your .gitignore).
15 |
--------------------------------------------------------------------------------
/api/routes/research.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import (
4 | "minerva_api/api/handlers"
5 |
6 | firebase "firebase.google.com/go"
7 | "github.com/gofiber/fiber/v2"
8 | )
9 |
10 | // ResearchRouter is the Router for GoFiber App
11 | func ResearchRouter(app *fiber.App, appFire *firebase.App) {
12 | app.Get("/topic/researches", handlers.GetResearches(appFire))
13 | app.Get("/topic/research/:id", handlers.GetResearchByID(appFire))
14 | app.Post("/topic/research", handlers.AddResearch(appFire))
15 | app.Put("/topic/research", handlers.UpdateResearch(appFire))
16 | app.Delete("/topic/research", handlers.RemoveResearch(appFire))
17 | app.Post("/topic/research/upload/pdf", handlers.PostPDF(appFire))
18 | app.Post("/topic/research/upload/image", handlers.PostImage(appFire))
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/api/presenter/topic.go:
--------------------------------------------------------------------------------
1 | package presenter
2 |
3 | import (
4 | "minerva_api/pkg/entities"
5 |
6 | "github.com/gofiber/fiber/v2"
7 | )
8 |
9 | // Topic is the presenter object which will be taken in the request by Handler
10 | type Topic struct {
11 | ID string `json:"id"`
12 | Title string `json:"title"`
13 | }
14 |
15 | // TopicSuccessResponse is the singular SuccessResponse that will be passed in the response by
16 | // Handler
17 | func TopicSuccessResponse(data *entities.Topic) *fiber.Map {
18 |
19 | newTopic := Topic{
20 | ID: data.ID,
21 | Title: data.Title,
22 | }
23 | return &fiber.Map{
24 | "status": true,
25 | "data": newTopic,
26 | "error": nil,
27 | }
28 | }
29 |
30 | // TopicsSuccessResponse is the list SuccessResponse that will be passed in the response by Handler
31 | func TopicssSuccessResponse(data *[]Topic) *fiber.Map {
32 | return &fiber.Map{
33 | "status": true,
34 | "data": data,
35 | "error": nil,
36 | }
37 | }
38 |
39 | // TopicErrorResponse is the ErrorResponse that will be passed in the response by Handler
40 | func TopicErrorResponse(err error) *fiber.Map {
41 | return &fiber.Map{
42 | "status": false,
43 | "data": "",
44 | "error": err.Error(),
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/api/presenter/research.go:
--------------------------------------------------------------------------------
1 | package presenter
2 |
3 | import (
4 | "minerva_api/pkg/entities"
5 |
6 | "github.com/gofiber/fiber/v2"
7 | )
8 |
9 | // Research is the presenter object which will be taken in the request by Handler
10 | type Research struct {
11 | ID string `json:"id"`
12 | Title string `json:"title"`
13 | Content string `json:"content"`
14 | TopicId string `json:"topic_id"`
15 | }
16 |
17 | // ResearchSuccessResponse is the singular SuccessResponse that will be passed in the response by
18 | // Handler
19 | func ResearchSuccessResponse(data *entities.Research) *fiber.Map {
20 |
21 | newResearch := Research{
22 | ID: data.ID,
23 | Title: data.Title,
24 | Content: data.Content,
25 | TopicId: data.TopicID,
26 | }
27 | return &fiber.Map{
28 | "status": true,
29 | "data": newResearch,
30 | "error": nil,
31 | }
32 | }
33 |
34 | // ResearchesSuccessResponse is the list SuccessResponse that will be passed in the response by Handler
35 | func ResearchesSuccessResponse(data *[]Research) *fiber.Map {
36 | return &fiber.Map{
37 | "status": true,
38 | "data": data,
39 | "error": nil,
40 | }
41 | }
42 |
43 | // ResearchErrorResponse is the ErrorResponse that will be passed in the response by Handler
44 | func ResearchErrorResponse(err error) *fiber.Map {
45 | return &fiber.Map{
46 | "status": false,
47 | "data": "",
48 | "error": err.Error(),
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/api/app.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "minerva_api/api/routes"
8 | "time"
9 |
10 | firebase "firebase.google.com/go"
11 | "github.com/gofiber/fiber/v2"
12 | "github.com/gofiber/fiber/v2/middleware/cors"
13 | "github.com/gofiber/fiber/v2/middleware/logger"
14 | recoverMw "github.com/gofiber/fiber/v2/middleware/recover"
15 | "google.golang.org/api/option"
16 | )
17 |
18 | func main() {
19 |
20 | appFire, cancel, err := databaseConnection()
21 | if err != nil {
22 | log.Fatal("Database Connection Error $s", err)
23 | }
24 | fmt.Println("Database connection success!")
25 |
26 | //Creates new fiber app
27 | app := fiber.New()
28 |
29 | //Middlewares
30 | app.Use(logger.New()) //records the details of incoming requests when any HTTP request is made. This can be used for purposes such as debugging and performance optimization.
31 | app.Use(recoverMw.New()) //catches any errors that may cause the program to crash or interrupt and keep the server running.
32 | app.Use(cors.New()) //It helps applications bypass CORS restrictions by providing appropriate responses that allow or deny HTTP requests access to their resources.
33 |
34 | routes.ResearchRouter(app, appFire)
35 | routes.TopicRouter(app, appFire)
36 |
37 | defer cancel()
38 | defer app.Shutdown()
39 | //Starts the HTTP server
40 | log.Fatal(app.Listen(":8080"))
41 | }
42 |
43 | func databaseConnection() (*firebase.App, context.CancelFunc, error) {
44 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
45 | //Take file and return as opt for API to use
46 | opt := option.WithCredentialsFile("C:/Users/sumey/Desktop/software/Back-End/Minerva/api/key.json")
47 | //Creates a new App from the provided config and client options.
48 | appFire, err := firebase.NewApp(ctx, nil, opt)
49 | if err != nil {
50 | cancel()
51 | return nil, nil, err
52 | }
53 |
54 | return appFire, cancel, nil
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module minerva_api
2 |
3 | go 1.19
4 |
5 | require (
6 | firebase.google.com/go v3.13.0+incompatible
7 | github.com/gofiber/fiber/v2 v2.42.0
8 | )
9 |
10 | require (
11 | cloud.google.com/go/iam v0.13.0 // indirect
12 | cloud.google.com/go/storage v1.30.1 // indirect
13 | )
14 |
15 | require (
16 | cloud.google.com/go v0.110.0 // indirect
17 | cloud.google.com/go/compute v1.19.0 // indirect
18 | cloud.google.com/go/compute/metadata v0.2.3 // indirect
19 | cloud.google.com/go/firestore v1.9.0
20 | cloud.google.com/go/longrunning v0.4.1 // indirect
21 | github.com/andybalholm/brotli v1.0.4 // indirect
22 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
23 | github.com/golang/protobuf v1.5.3 // indirect
24 | github.com/google/go-cmp v0.5.9 // indirect
25 | github.com/google/uuid v1.3.0
26 | github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
27 | github.com/googleapis/gax-go/v2 v2.8.0 // indirect
28 | github.com/klauspost/compress v1.15.9 // indirect
29 | github.com/mattn/go-colorable v0.1.13 // indirect
30 | github.com/mattn/go-isatty v0.0.17 // indirect
31 | github.com/mattn/go-runewidth v0.0.14 // indirect
32 | github.com/philhofer/fwd v1.1.1 // indirect
33 | github.com/rivo/uniseg v0.2.0 // indirect
34 | github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
35 | github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
36 | github.com/tinylib/msgp v1.1.6 // indirect
37 | github.com/valyala/bytebufferpool v1.0.0 // indirect
38 | github.com/valyala/fasthttp v1.44.0 // indirect
39 | github.com/valyala/tcplisten v1.0.0 // indirect
40 | go.opencensus.io v0.24.0 // indirect
41 | golang.org/x/net v0.8.0 // indirect
42 | golang.org/x/oauth2 v0.6.0 // indirect
43 | golang.org/x/sync v0.1.0 // indirect
44 | golang.org/x/sys v0.6.0 // indirect
45 | golang.org/x/text v0.8.0 // indirect
46 | golang.org/x/time v0.3.0 // indirect
47 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
48 | google.golang.org/api v0.114.0
49 | google.golang.org/appengine v1.6.7 // indirect
50 | google.golang.org/genproto v0.0.0-20230323212658-478b75c54725 // indirect
51 | google.golang.org/grpc v1.54.0 // indirect
52 | google.golang.org/protobuf v1.30.0 // indirect
53 | )
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MinervaApi
2 | **This is a simple Go and Firebase-based RESTful API designed to manage data stored in Firebase. The API allows users to create new topics and associated research. The API is designed to be easy to use and easy to set up.**
3 |
4 | ## Installation
5 | To use this API, you will need to install **Go** and the **Firebase SDK**.
6 | Create a project in the Firebase Console and add a service account. This will be used to communicate with Firebase.
7 | Add the configuration file for the Firebase SDK to the project folder.
8 | Create a Go module using the go mod init command.
9 |
10 | ## Usage
11 | To use the API, clone this repo and run the **main.go** file.(After insert your key to the project) The API listens on port 8080 by default. To create a new topic, send a POST request to /topic with a JSON payload containing the topic title and author JWT. To create a new research, send a POST request to /topic/research with a JSON payload containing the research title, content, author jwt, contributor, and topic ID.
12 | ### Necessary Variables
13 |
14 | To run this project, you will need to add your firebase key json file to project's api folder.
15 |
16 | `key.json`
17 |
18 | You can find your private key on Your firebase account -> Your firebase project -> Project Settings ->Service accounts -> Firebase Admin SDK -> (I choose Node.js) Generate new private key =>
19 | your private key will be downloaded to your device.
20 |
21 |
22 | #### Example Requests
23 |
24 | ```http
25 | POST /topics HTTP/1.1
26 | Host: localhost:8080
27 | Content-Type: application/json
28 | ```
29 |
30 | | Parameter | Type | Value |
31 | | :-------- | :------- | :------------------------- |
32 | | `title` | `string` | "Example Topic" |
33 |
34 |
35 | ```http
36 | POST /topic/research HTTP/1.1
37 | Host: localhost:8080
38 | Content-Type: application/json
39 | ```
40 | | Parameter | Type | Value |
41 | | :-------- | :------- | :------------------------- |
42 | | `"title"` | `string` | "Example Research" |
43 | | `"content"` | `string` | "paragraph1" |
44 | | `"topic_id"` | `string` | "67890" |
45 |
46 | #### Example Responses
47 |
48 | ```http
49 | HTTP/1.1 200 OK
50 | Content-Type: application/json
51 | ```
52 | | Parameter | Type | Value |
53 | | :-------- | :------- | :------------------------- |
54 | | `data` | `map[string]string` |` "id" `: "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
`"title"` : "Research's title"
`"topic_id"` : "xxxxxxxxxxxxxxxxxxxx" |
55 | | `error` | `string` |"null" |
56 | | `status` | `string` |"true" |
57 |
58 | ```http
59 | HTTP/1.1 200 OK
60 | Content-Type: application/json
61 | ```
62 | | Parameter | Type |Key |
63 | | :-------- | :------------------ |:-----------|
64 | | `data` | `map[string]string` |` "id" `: "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
`"title"` : "Research's title"
`"topic_id"` : "xxxxxxxxxxxxxxxxxxxx"
`"content"` : "paragraph1"
|
65 | | `error` | `string` |"null" ||
66 | | `status` | `string` |"true" ||
67 |
68 |
69 | ## Contributing
70 | Contributions are welcome! Please feel free to fork this repository and submit pull requests.
71 |
72 | #### License
73 | -> This project is licensed by me :).
74 |
--------------------------------------------------------------------------------
/api/handlers/topic_handler.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "minerva_api/api/presenter"
8 | "minerva_api/pkg/entities"
9 | "net/http"
10 | "time"
11 |
12 | "cloud.google.com/go/firestore"
13 | firebase "firebase.google.com/go"
14 | "github.com/gofiber/fiber/v2"
15 | "google.golang.org/api/iterator"
16 | )
17 |
18 | func GetTopics(appFire *firebase.App) fiber.Handler {
19 | Client, _ := appFire.Firestore(context.Background())
20 | return func(c *fiber.Ctx) error {
21 |
22 | var requestBody *entities.Topic
23 | //Body Parser,Error Handler
24 | err := c.BodyParser(&requestBody)
25 | if err != nil {
26 | c.Status(http.StatusBadRequest)
27 | return c.JSON(presenter.TopicErrorResponse(err))
28 | }
29 |
30 | var topics []presenter.Topic
31 | // Creates a reference to a collection to Topic path.
32 | userCol := Client.Collection("Topic")
33 |
34 | //Documents returns an iterator over the query's resulting documents.
35 | query := userCol.Documents(context.Background())
36 |
37 | for {
38 | //Next returns the next result. Its second return value is iterator.Done if there are no more results
39 | doc, err := query.Next()
40 | if err == iterator.Done {
41 | break
42 | }
43 | var topic presenter.Topic
44 | doc.DataTo(&topic)
45 | topics = append(topics, topic)
46 | }
47 |
48 | return c.JSON(&fiber.Map{
49 | "status": true,
50 | "data": &topics,
51 | "err": nil,
52 | })
53 | }
54 | }
55 |
56 | func AddTopic(appFire *firebase.App) fiber.Handler {
57 | Client, _ := appFire.Firestore(context.Background())
58 | return func(c *fiber.Ctx) error {
59 |
60 | var requestBody *entities.Topic
61 |
62 | //Body Parser,Error Handler
63 | if err := c.BodyParser(&requestBody); err != nil {
64 | c.Status(http.StatusBadRequest)
65 | return c.JSON(presenter.TopicErrorResponse(err))
66 | }
67 |
68 | requestBody.CreatedAt = time.Now()
69 | requestBody.UpdatedAt = time.Now()
70 |
71 | if requestBody.ID == "" || requestBody.Title == "" {
72 | c.Status(http.StatusInternalServerError)
73 | return c.JSON(presenter.TopicErrorResponse(errors.New(
74 | "please specify title and topic id")))
75 | }
76 |
77 | // Creates a reference to a collection to Topic path.
78 | userCol := Client.Collection("Topic")
79 | //Creates unıq id for document
80 | docRefUID := userCol.NewDoc()
81 |
82 | requestBody.ID = docRefUID.ID
83 | // Add Topic to Firestore
84 | _, err := docRefUID.Set(context.Background(), &requestBody)
85 | if err != nil {
86 | c.Status(http.StatusInternalServerError)
87 | return c.JSON(presenter.TopicErrorResponse(err))
88 | }
89 |
90 | return c.JSON(presenter.TopicSuccessResponse(requestBody))
91 | }
92 | }
93 | func UpdateTopic(appFire *firebase.App) fiber.Handler {
94 | Client, _ := appFire.Firestore(context.Background())
95 | return func(c *fiber.Ctx) error {
96 |
97 | var requestBody *entities.Topic
98 |
99 | //Body Parser,Error Handler
100 | if err := c.BodyParser(&requestBody); err != nil {
101 | c.Status(http.StatusBadRequest)
102 | return c.JSON(presenter.TopicErrorResponse(err))
103 | }
104 |
105 | if requestBody.ID == "" {
106 | c.Status(http.StatusInternalServerError)
107 | return c.JSON(presenter.TopicErrorResponse(errors.New(
108 | "please specify the Topic ID")))
109 | }
110 |
111 | requestBody.UpdatedAt = time.Now()
112 |
113 | //Indicates the document's path
114 | docRefPath := fmt.Sprintf("Topic/%s", requestBody.ID)
115 | //Indicates to document
116 | userDoc := Client.Doc(docRefPath)
117 |
118 | //Updates the given parameters at the Document
119 | _, err := userDoc.Update(context.Background(), []firestore.Update{
120 | {Path: "Title", Value: requestBody.Title},
121 | {Path: "UpdatedAt", Value: requestBody.UpdatedAt},
122 | })
123 |
124 | if err != nil {
125 | c.Status(http.StatusInternalServerError)
126 | return c.JSON(presenter.TopicErrorResponse(err))
127 | }
128 |
129 | return c.JSON(presenter.TopicSuccessResponse(requestBody))
130 | }
131 | }
132 | func RemoveTopic(appFire *firebase.App) fiber.Handler {
133 | Client, _ := appFire.Firestore(context.Background())
134 | return func(c *fiber.Ctx) error {
135 |
136 | var requestBody *entities.Topic
137 |
138 | //Body Parser,Error Handler
139 | if err := c.BodyParser(&requestBody); err != nil {
140 | c.Status(http.StatusBadRequest)
141 | return c.JSON(presenter.TopicErrorResponse(err))
142 | }
143 |
144 | if requestBody.ID == "" {
145 | c.Status(http.StatusInternalServerError)
146 | return c.JSON(presenter.TopicErrorResponse(errors.New(
147 | "please specify the Topic ID ")))
148 | }
149 |
150 | //Indicates the document's path
151 | docRefPath := fmt.Sprintf("Topic/%s", requestBody.ID)
152 | //Indicates to document
153 | userDoc := Client.Doc(docRefPath)
154 | _, err := userDoc.Delete(context.Background())
155 | if err != nil {
156 | c.Status(http.StatusInternalServerError)
157 | return c.JSON(presenter.TopicErrorResponse(err))
158 | }
159 |
160 | return c.JSON(presenter.TopicSuccessResponse(requestBody))
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/api/handlers/research_handler.go:
--------------------------------------------------------------------------------
1 | package handlers
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "io"
8 | "log"
9 | "minerva_api/api/presenter"
10 | "minerva_api/pkg/entities"
11 | "net/http"
12 | "strings"
13 | "time"
14 |
15 | "cloud.google.com/go/firestore"
16 | "cloud.google.com/go/storage"
17 |
18 | firebase "firebase.google.com/go"
19 | "github.com/gofiber/fiber/v2"
20 | "github.com/google/uuid"
21 | "google.golang.org/api/iterator"
22 | "google.golang.org/api/option"
23 | )
24 |
25 | // AddResearch is handler/controller which creates Researches in the Database
26 | func AddResearch(appFire *firebase.App) fiber.Handler {
27 | Client, _ := appFire.Firestore(context.Background())
28 | return func(c *fiber.Ctx) error {
29 |
30 | var requestBody *entities.Research
31 |
32 | //Body Parser,Error Handler
33 | if err := c.BodyParser(&requestBody); err != nil {
34 | c.Status(http.StatusBadRequest)
35 | return c.JSON(presenter.ResearchErrorResponse(err))
36 | }
37 |
38 | if requestBody.TopicID == "" || requestBody.Title == "" {
39 | c.Status(http.StatusInternalServerError)
40 | return c.JSON(presenter.ResearchErrorResponse(errors.New(
41 | "please specify title and the topic id")))
42 | }
43 |
44 | collectionName := UUID()
45 |
46 | // Creates a reference to a collection group to Research path.
47 | colPath := fmt.Sprintf("Topic/%s/%s", requestBody.TopicID, collectionName)
48 |
49 | collection := Client.Collection(colPath)
50 |
51 | // Set the ID field of the research to a new ID.
52 | requestBody.ID = collectionName
53 |
54 | // Set the created and updated timestamps for the research.
55 | requestBody.CreatedAt = time.Now()
56 | requestBody.UpdatedAt = time.Now()
57 |
58 | _, err := collection.Doc(collectionName).Set(context.Background(), &requestBody)
59 | if err != nil {
60 | c.Status(http.StatusInternalServerError)
61 | return c.JSON(presenter.ResearchErrorResponse(err))
62 | }
63 |
64 | return c.JSON(presenter.ResearchSuccessResponse(requestBody))
65 | }
66 | }
67 |
68 | // UpdateResearch is handler/controller which updates data of Researches in the database
69 | func UpdateResearch(appFire *firebase.App) fiber.Handler {
70 | Client, _ := appFire.Firestore(context.Background())
71 | return func(c *fiber.Ctx) error {
72 |
73 | var requestBody *entities.Research
74 | err := c.BodyParser(&requestBody)
75 | if err != nil {
76 | c.Status(http.StatusBadRequest)
77 | return c.JSON(presenter.ResearchErrorResponse(err))
78 | }
79 | requestBody.UpdatedAt = time.Now()
80 | //Indicates the document's path
81 | docRefPath := fmt.Sprintf("Topic/%s/%s/%s", requestBody.TopicID, requestBody.ID, requestBody.ID)
82 | //Indicates to document
83 | userDoc := Client.Doc(docRefPath)
84 |
85 | //Updates the given parameters at the Document
86 | _, err = userDoc.Update(context.Background(), []firestore.Update{
87 | {Path: "Title", Value: requestBody.Title},
88 | {Path: "Content", Value: requestBody.Content},
89 | {Path: "UpdatedAt", Value: requestBody.UpdatedAt},
90 | })
91 | if err != nil {
92 | c.Status(http.StatusInternalServerError)
93 | return c.JSON(presenter.ResearchErrorResponse(err))
94 | }
95 | return c.JSON(presenter.ResearchSuccessResponse(requestBody))
96 | }
97 | }
98 |
99 | // RemoveResearch is handler/controller which removes Researches from the Database
100 | func RemoveResearch(appFire *firebase.App) fiber.Handler {
101 | Client, _ := appFire.Firestore(context.Background())
102 | return func(c *fiber.Ctx) error {
103 | var requestBody *entities.Research
104 | err := c.BodyParser(&requestBody)
105 | if err != nil {
106 | c.Status(http.StatusBadRequest)
107 | return c.JSON(presenter.ResearchErrorResponse(err))
108 | }
109 |
110 | //Indicates the document's path
111 | docRefPath := fmt.Sprintf("Topic/%s/%s/%s", requestBody.TopicID, requestBody.ID, requestBody.ID)
112 |
113 | //Indicates to document
114 | userDoc := Client.Doc(docRefPath)
115 |
116 | //Deletes the document
117 | _, err = userDoc.Delete(context.Background())
118 |
119 | if err != nil {
120 | c.Status(http.StatusInternalServerError)
121 | return c.JSON(presenter.ResearchErrorResponse(err))
122 | }
123 | return c.JSON(&fiber.Map{
124 | "status": true,
125 | "data": "removed successfully",
126 | "err": nil,
127 | })
128 | }
129 | }
130 |
131 | // GetResearch is handler/controller which lists single Research from the database
132 | func GetResearchByID(appFire *firebase.App) fiber.Handler {
133 | Client, _ := appFire.Firestore(context.Background())
134 | return func(c *fiber.Ctx) error {
135 |
136 | // Extract the research ID from the URL path
137 | id := c.Params("id")
138 |
139 | var requestBody entities.Research
140 |
141 | err := c.BodyParser(&requestBody)
142 | if err != nil {
143 | c.Status(http.StatusBadRequest)
144 | return c.JSON(presenter.ResearchErrorResponse(err))
145 | }
146 |
147 | //var researches []presenter.Research
148 |
149 | //Indicates the document's path
150 | docPath := fmt.Sprintf("Topic/%s/%s/%s", requestBody.TopicID, id, id)
151 | //Indicates to document
152 | userDoc := Client.Doc(docPath)
153 |
154 | docSnapShot, err := userDoc.Get(context.Background())
155 | if err != nil {
156 | c.Status(http.StatusBadRequest)
157 | return c.JSON(presenter.ResearchErrorResponse(err))
158 | }
159 |
160 | data := docSnapShot.Data()
161 |
162 | return c.JSON(&fiber.Map{
163 | "status": true,
164 | "data": &data,
165 | "err": nil,
166 | })
167 | }
168 | }
169 |
170 | // GetResearches is handler/controller which lists all Researches from the database
171 | func GetResearches(appFire *firebase.App) fiber.Handler {
172 | Client, _ := appFire.Firestore(context.Background())
173 | return func(c *fiber.Ctx) error {
174 |
175 | var requestBody entities.Research
176 | err := c.BodyParser(&requestBody)
177 | if err != nil {
178 | c.Status(http.StatusBadRequest)
179 | return c.JSON(presenter.ResearchErrorResponse(err))
180 | }
181 |
182 | var researches []presenter.Research
183 | //Indicates the document's path
184 | docPath := fmt.Sprintf("Topic/%s", requestBody.TopicID)
185 | //Indicates to document
186 | userDoc := Client.Doc(docPath)
187 |
188 | //Documents returns an iterator over the query's resulting documents.
189 | query := userDoc.Collections(context.Background())
190 |
191 | for {
192 | //Next returns the next result. Its second return value is iterator.Done if there are no more results
193 | col, err := query.Next()
194 | if err == iterator.Done {
195 | break
196 | }
197 | if err != nil {
198 | return err
199 | }
200 | //Next returns the next result. Its second return value is iterator.Done if there are no more results
201 | doc, err := (col.Documents(context.Background())).Next()
202 | if err == iterator.Done {
203 | break
204 | }
205 | if err != nil {
206 | return err
207 | }
208 | var research presenter.Research
209 | doc.DataTo(&research)
210 | researches = append(researches, research)
211 | }
212 |
213 | return c.JSON(&fiber.Map{
214 | "status": true,
215 | "data": &researches,
216 | "err": nil,
217 | })
218 |
219 | }
220 | }
221 |
222 | func PostPDF(appFire *firebase.App) fiber.Handler {
223 | client, err := storage.NewClient(context.Background(), option.WithCredentialsFile("C:\\Users\\sumey\\Desktop\\software\\Back-End\\Minerva\\api\\key.json"))
224 | if err != nil {
225 | log.Printf("Failed to create client: %v", err)
226 | }
227 |
228 | firestoreClient, err := appFire.Firestore(context.Background())
229 | if err != nil {
230 | log.Printf("Failed to create firestore client: %v", err)
231 | }
232 | return func(c *fiber.Ctx) error {
233 | // Parse the multipart form:
234 | if form, err := c.MultipartForm(); err == nil {
235 | // Get all files from "documents" key:
236 | files := form.File["pdf"]
237 |
238 | // Initialize urls slice
239 | var urls []string
240 |
241 | // Loop through files:
242 | for _, file := range files {
243 | // Save the files to Firebase Storage:
244 | wc := client.Bucket("minerva-95196.appspot.com").Object(file.Filename).NewWriter(context.Background())
245 | wc.ContentType = file.Header["Content-Type"][0]
246 | // open the uploaded file
247 | f, err := file.Open()
248 | if err != nil {
249 | return err
250 | }
251 | // write the file to the bucket
252 | if _, err := io.Copy(wc, f); err != nil {
253 | return err
254 | }
255 | if err := wc.Close(); err != nil {
256 | return err
257 | }
258 | // print the file url
259 | url := wc.Attrs().MediaLink
260 |
261 | //Get topicID and researchID from request
262 | topicID := form.Value["topic_id"]
263 | researchID := form.Value["research_id"]
264 |
265 | //Convert []string to string
266 | topicIDstring := strings.Join(topicID, "")
267 | researchIDstring := strings.Join(researchID, "")
268 |
269 | //Indicates te firestore document path
270 | docRefPath := fmt.Sprintf("Topic/%v/%v/%v", topicIDstring, researchIDstring, researchIDstring)
271 | //Indicates to document
272 | userDoc := firestoreClient.Doc(docRefPath)
273 | //Retrieve the document
274 | docSnap, err := userDoc.Get(context.Background())
275 | if err != nil {
276 | c.Status(http.StatusInternalServerError)
277 | return c.JSON(presenter.ResearchErrorResponse(err))
278 | }
279 |
280 | //Get the existing URLs
281 | data := docSnap.Data()
282 |
283 | // Check if "PdfUrl" field exists in the document
284 | if pdfUrls, ok := data["PdfUrl"].([]interface{}); ok {
285 |
286 | for _, url := range pdfUrls {
287 | urls = append(urls, url.(string))
288 | }
289 | }
290 |
291 | //Append the new URL to the existing URLs
292 | urls = append(urls, url)
293 |
294 | //Saves the pdf's url at the Document
295 | _, err = userDoc.Set(context.Background(), map[string][]string{
296 | "PdfUrl": urls,
297 | }, firestore.MergeAll)
298 | if err != nil {
299 | c.Status(http.StatusInternalServerError)
300 | return c.JSON(presenter.ResearchErrorResponse(err))
301 | }
302 | //Updates the given parameters at the Document
303 | _, err = userDoc.Update(context.Background(), []firestore.Update{
304 | {Path: "UpdatedAt", Value: time.Now()},
305 | })
306 | if err != nil {
307 | c.Status(http.StatusInternalServerError)
308 | return c.JSON(presenter.ResearchErrorResponse(err))
309 | }
310 | }
311 | }
312 | return c.JSON("Pdf url saved to database succesfully")
313 | }
314 | }
315 |
316 | func PostImage(appFire *firebase.App) fiber.Handler {
317 | client, err := storage.NewClient(context.Background(), option.WithCredentialsFile("C:\\Users\\sumey\\Desktop\\software\\Back-End\\Minerva\\api\\key.json"))
318 | if err != nil {
319 | log.Printf("Failed to create client: %v", err)
320 | }
321 |
322 | firestoreClient, err := appFire.Firestore(context.Background())
323 | if err != nil {
324 | log.Printf("Failed to create firestore client: %v", err)
325 | }
326 | return func(c *fiber.Ctx) error {
327 | // Parse the multipart form:
328 | if form, err := c.MultipartForm(); err == nil {
329 | // Get all files from "documents" key:
330 | files := form.File["image"]
331 |
332 | // Initialize urls slice
333 | var urls []string
334 |
335 | // Loop through files:
336 | for _, file := range files {
337 | // Save the files to Firebase Storage:
338 | wc := client.Bucket("minerva-95196.appspot.com").Object(file.Filename).NewWriter(context.Background())
339 | wc.ContentType = file.Header["Content-Type"][0]
340 | // open the uploaded file
341 | f, err := file.Open()
342 | if err != nil {
343 | return err
344 | }
345 | // write the file to the bucket
346 | if _, err := io.Copy(wc, f); err != nil {
347 | return err
348 | }
349 | if err := wc.Close(); err != nil {
350 | return err
351 | }
352 | // print the file url
353 | url := wc.Attrs().MediaLink
354 |
355 | //Get topicID and researchID from request
356 | topicID := form.Value["topic_id"]
357 | researchID := form.Value["research_id"]
358 |
359 | //Convert []string to string
360 | topicIDstring := strings.Join(topicID, "")
361 | researchIDstring := strings.Join(researchID, "")
362 |
363 | //Indicates te firestore document path
364 | docRefPath := fmt.Sprintf("Topic/%v/%v/%v", topicIDstring, researchIDstring, researchIDstring)
365 | //Indicates to document
366 | userDoc := firestoreClient.Doc(docRefPath)
367 | //Retrieve the document
368 | docSnap, err := userDoc.Get(context.Background())
369 | if err != nil {
370 | c.Status(http.StatusInternalServerError)
371 | return c.JSON(presenter.ResearchErrorResponse(err))
372 | }
373 |
374 | //Get the existing URLs
375 | data := docSnap.Data()
376 |
377 | // Check if "PdfUrl" field exists in the document
378 | if pdfUrls, ok := data["ImageUrl"].([]interface{}); ok {
379 |
380 | for _, url := range pdfUrls {
381 | urls = append(urls, url.(string))
382 | }
383 | }
384 |
385 | //Append the new URL to the existing URLs
386 | urls = append(urls, url)
387 |
388 | //Saves the pdf's url at the Document
389 | _, err = userDoc.Set(context.Background(), map[string][]string{
390 | "ImageUrl": urls,
391 | }, firestore.MergeAll)
392 | if err != nil {
393 | c.Status(http.StatusInternalServerError)
394 | return c.JSON(presenter.ResearchErrorResponse(err))
395 | }
396 | //Updates the given parameters at the Document
397 | _, err = userDoc.Update(context.Background(), []firestore.Update{
398 | {Path: "UpdatedAt", Value: time.Now()},
399 | })
400 | if err != nil {
401 | c.Status(http.StatusInternalServerError)
402 | return c.JSON(presenter.ResearchErrorResponse(err))
403 | }
404 | }
405 | }
406 | return c.JSON("Image url saved to database succesfully")
407 | }
408 | }
409 |
410 | // UUID generates a uniq id
411 | func UUID() string {
412 | newUUID := uuid.New().String()
413 | return newUUID
414 | }
415 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
3 | cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
4 | cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY=
5 | cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
6 | cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
7 | cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
8 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
9 | cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
10 | cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA=
11 | cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE=
12 | cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE=
13 | cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY=
14 | cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
15 | cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
16 | cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
17 | cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
18 | cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI=
19 | cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
20 | cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
21 | cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
22 | firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
23 | firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
24 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
25 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
26 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
27 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
28 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
29 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
30 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
31 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
32 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
33 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
34 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
35 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
36 | github.com/gofiber/fiber/v2 v2.42.0 h1:Fnp7ybWvS+sjNQsFvkhf4G8OhXswvB6Vee8hM/LyS+8=
37 | github.com/gofiber/fiber/v2 v2.42.0/go.mod h1:3+SGNjqMh5VQH5Vz2Wdi43zTIV16ktlFd3x3R6O1Zlc=
38 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
39 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
40 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
41 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
42 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
43 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
44 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
45 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
46 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
47 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
48 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
49 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
50 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
51 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
52 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
53 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
54 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
55 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
56 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
57 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
58 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
59 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
60 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
61 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
62 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
63 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
64 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
65 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
66 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
67 | github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
68 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
69 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
70 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
71 | github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
72 | github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
73 | github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
74 | github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
75 | github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc=
76 | github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
77 | github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
78 | github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
79 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
80 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
81 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
82 | github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
83 | github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
84 | github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
85 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
86 | github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
87 | github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
88 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
89 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
90 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
91 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
92 | github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
93 | github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8=
94 | github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo=
95 | github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
96 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
97 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
98 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
99 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
100 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
101 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
102 | github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
103 | github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
104 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
105 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
106 | github.com/valyala/fasthttp v1.44.0 h1:R+gLUhldIsfg1HokMuQjdQ5bh9nuXHPIfvkYUu9eR5Q=
107 | github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
108 | github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
109 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
110 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
111 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
112 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
113 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
114 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
115 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
116 | golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
117 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
118 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
119 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
120 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
121 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
122 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
123 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
124 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
125 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
126 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
127 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
128 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
129 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
130 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
131 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
132 | golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
133 | golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
134 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
135 | golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
136 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
137 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
138 | golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
139 | golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
140 | golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
141 | golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
142 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
143 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
144 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
145 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
146 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
147 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
148 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
149 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
150 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
151 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
152 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
153 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
154 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
155 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
156 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
157 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
158 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
159 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
160 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
161 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
162 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
163 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
164 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
165 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
166 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
167 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
168 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
169 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
170 | golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
171 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
172 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
173 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
174 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
175 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
176 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
177 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
178 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
179 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
180 | golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
181 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
182 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
183 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
184 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
185 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
186 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
187 | google.golang.org/api v0.111.0 h1:bwKi+z2BsdwYFRKrqwutM+axAlYLz83gt5pDSXCJT+0=
188 | google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
189 | google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
190 | google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
191 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
192 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
193 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
194 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
195 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
196 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
197 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
198 | google.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 h1:/cadn7taPtPlCgiWNetEPsle7jgnlad2R7gR5MXB6dM=
199 | google.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=
200 | google.golang.org/genproto v0.0.0-20230323212658-478b75c54725 h1:VmCWItVXcKboEMCwZaWge+1JLiTCQSngZeINF+wzO+g=
201 | google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
202 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
203 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
204 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
205 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
206 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
207 | google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
208 | google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
209 | google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
210 | google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
211 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
212 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
213 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
214 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
215 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
216 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
217 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
218 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
219 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
220 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
221 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
222 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
223 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
224 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
225 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
226 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
227 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
228 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
229 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
230 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
231 |
--------------------------------------------------------------------------------