Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] 7 - Implementar HTTP DELETE/plants/{id} #21

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions elastic/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package elastic

import (
es "github.com/elastic/go-elasticsearch/v8"
log "github.com/sirupsen/logrus"
)

var esInstance *es.Client

// GetClient returns a client connected to the running elasticseach cluster
func GetClient() (*es.Client, error) {
if esInstance != nil {
return esInstance, nil
}

cfg := es.Config{
Addresses: []string{
"http://elasticsearch:9200",
"http://elasticsearch2:9200",
"http://elasticsearch3:9200",
},
}
esClient, err := es.NewClient(cfg)
if err != nil {
log.WithFields(log.Fields{
"config": cfg,
"error": err,
}).Error("Could not obtain an Elasticsearch client")

return nil, err
}

esInstance = esClient

return esInstance, nil
}
41 changes: 41 additions & 0 deletions elastic/dao/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package elastic

import (
"bytes"
"encoding/json"

"github.com/gdgtoledo/linneo/elastic"
"github.com/gdgtoledo/linneo/plants"
log "github.com/sirupsen/logrus"
)

func (id string) getQuery() map[string]interface{} {
query := map[string]interface{}{"query": {
match: {
id: id,
},
}}

return query
}

// Delete a plant by id
func Delete(id string) (plants.DeleteByIDResult, error) {
result := plants.DeleteByIDResult{}

esClient, err := elastic.GetClient()

if err != nil {
return result, err
}

var buf bytes.Buffer

if err := json.NewEncoder(&buf).Encode(query); err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Error encoding Elasticsearch query")

return result, err
}
}
47 changes: 8 additions & 39 deletions dao/elasticsearch.go → elastic/dao/search.go
Original file line number Diff line number Diff line change
@@ -1,52 +1,21 @@
package dao
package elastic

import (
"bytes"
"encoding/json"
"fmt"

es "github.com/elastic/go-elasticsearch/v8"
"github.com/gdgtoledo/linneo/elastic"
"github.com/gdgtoledo/linneo/plants"
log "github.com/sirupsen/logrus"
)

var esInstance *es.Client

// SearchResult wraps a search result
type SearchResult map[string]interface{}

// getElasticsearchClient returns a client connected to the running elasticseach cluster
func getElasticsearchClient() (*es.Client, error) {
if esInstance != nil {
return esInstance, nil
}

cfg := es.Config{
Addresses: []string{
"http://elasticsearch:9200",
"http://elasticsearch2:9200",
"http://elasticsearch3:9200",
},
}
esClient, err := es.NewClient(cfg)
if err != nil {
log.WithFields(log.Fields{
"config": cfg,
"error": err,
}).Error("Could not obtain an Elasticsearch client")

return nil, err
}

esInstance = esClient

return esInstance, nil
}

// Search executes a query in the proper index
func Search(indexName string, query map[string]interface{}) (SearchResult, error) {
result := SearchResult{}
func Search(query plants.SearchQueryByIndexName) (plants.SearchQueryByIndexNameResult, error) {
result := plants.SearchQueryByIndexNameResult{}

esClient, err := elastic.GetClient()

esClient, err := getElasticsearchClient()
if err != nil {
return result, err
}
Expand All @@ -65,7 +34,7 @@ func Search(indexName string, query map[string]interface{}) (SearchResult, error
}).Debug("Elasticsearch query")

res, err := esClient.Search(
esClient.Search.WithIndex(indexName),
esClient.Search.WithIndex(query.IndexName),
esClient.Search.WithBody(&buf),
esClient.Search.WithTrackTotalHits(true),
esClient.Search.WithPretty(),
Expand Down
34 changes: 30 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,54 @@ package main
import (
"net/http"

"github.com/gdgtoledo/linneo/dao"
"github.com/gdgtoledo/linneo/plants"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)

func searchPlants(c *gin.Context) {
res, err := dao.Search("plants", map[string]interface{}{})
var routes = map[string]string{"plants": "/plants", "plant": "/plants/:id"}

func handleSearchItems(c *gin.Context) {
searchQueryByIndexName := plants.SearchQueryByIndexName{
IndexName: "plants",
Query: map[string]interface{}{},
}

res, err := plants.Search(searchQueryByIndexName)

log.WithFields(log.Fields{
"result": res,
}).Info("Query Result")

if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Error querying database")
}

c.String(http.StatusNoContent, "There are no plants in the primary storage")
}

func handleDeleteItem(c *gin.Context) {
var plant plants.Model
result, err := plants.Delete(plant.ID)

log.WithFields(log.Fields{
"result": result,
}).Info("Delete Query Result")

if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Error deleting a plant")
}
}

func setupRouter() *gin.Engine {
r := gin.Default()

r.GET("/plants", searchPlants)
r.GET(routes["plants"], handleSearchItems)
r.DELETE(routes["plant"], handleDeleteItem)

return r
}
Expand Down
9 changes: 9 additions & 0 deletions plants/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package plants
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yo dejaría ésto como un método en el main.go, para no tener un paquete con sólo un método público, o al menos en un fichero dentro del paquete raíz

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bueno, yo también he tenido esa duda, al final, desde mi opinión inexperta, lo hice así para tener un dominio que no dependa de ninguna implementación, en este caso, nuestro dominio son las planticas y su gestión, en el main tenemos el problema que tenemos un buen salpicado de marisco de implementación del servidor, con gin, elastic logs tracking and monitoring

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

la idea era tener un paquete plants de nuestra entidad de dominio y aquí empiezan mis dudas

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

según veo y a efectos practicos, un cliente lo unico que debería pedir es un search('plants'),
ese plants se lo daría el contexto de Gin

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crearía un api.go en el raíz, y allí todos estos metodicos


import dao "github.com/gdgtoledo/linneo/elastic/dao"

// Delete by id
func Delete(id string) (DeleteQueryByIDResult, error) {
result, err := dao.Delete(id)
return result, err
}
27 changes: 27 additions & 0 deletions plants/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package plants

// Model for an item
type Model struct {
ID string
Name string
Species string
Genere string
Geo string
}

// SearchQueryByIndexName struct
type SearchQueryByIndexName struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El index es algo que se pone en el cliente, yo lo llamaría SearchQuery

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eliminarias ese IndexName string del struct entonces?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sí, así es reutilizable

IndexName string
Query map[string]interface{}
}

// SearchQueryByIndexNameResult wraps a search result
type SearchQueryByIndexNameResult map[string]interface{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El index es algo que se pone en el cliente, yo lo llamaría SearchQueryResult


// DeleteByIDResult wraps a delete item result
type DeleteQueryByID interface {
getQuery() map[string]interface{}
}

// DeleteByIDResult wraps a delete item result
type DeleteQueryByIDResult map[string]interface{}
11 changes: 11 additions & 0 deletions plants/search.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package plants
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lo mismo que para el Delete, lo dejaría en el main.go, o al menos en un fichero dentro del paquete raíz


import (
dao "github.com/gdgtoledo/linneo/elastic/dao"
)

// Search by query with an indexName
func Search(searchQueryByIndexName SearchQueryByIndexName) (SearchQueryByIndexNameResult, error) {
response, err := dao.Search(searchQueryByIndexName)
return response, err
}