Skip to content

Commit

Permalink
Added mux-elasticsearch sample
Browse files Browse the repository at this point in the history
Signed-off-by: ayush3160 <[email protected]>
  • Loading branch information
ayush3160 committed Aug 6, 2024
1 parent 2afb959 commit fae6389
Show file tree
Hide file tree
Showing 14 changed files with 1,100 additions and 0 deletions.
1 change: 1 addition & 0 deletions mux-elasticsearch/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mux-elasticsearch
23 changes: 23 additions & 0 deletions mux-elasticsearch/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Use the Go 1.19 base image with the bullseye tag
FROM golang:1.22-bookworm


RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
rm -rf /var/lib/apt/lists/*

# Set the working directory inside the container
WORKDIR /app

COPY go.mod /app/
COPY go.sum /app/

RUN go mod download

# Copy the contents from the local directory to the working directory in the container
COPY . /app

RUN go build -o app .

# Run the application server using "go run handler.go main.go"
CMD ["./app"]
86 changes: 86 additions & 0 deletions mux-elasticsearch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Introduction
This is a sample go project to show the crud operations of golang with elasticsearch and mux.

## Installation Setup

```bash
git clone https://github.com/keploy/samples-go.git && cd samples-go/mux-elasticsearch
go mod download
```

## Installation Keploy
Install keploy via one-click:-

```sh
curl --silent -O -L https://keploy.io/install.sh && source install.sh
```

### Install And Run Elastic

Using the docker we will start our elastic instance:-

```bash
docker run --name=es01 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "xpack.security.enabled=false" docker.elastic.co/elasticsearch/elasticsearch:8.14.3
```

In the above command we are passing two environment variables to run the elastic in dev mode and disabling the need to pass the password to connect to elastic database. If you want to enable it you can follow steps mentioned on this [page](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html) . Then you have to pass the password and http cert file to connect.


### Capture the Testcases

Now, we will create the binary of our application:-

```zsh
go build -cover
```

Once we have our binary file ready,this command will start the recording of API calls using ebpf:-

```shell
sudo -E keploy record -c "./mux-elasticsearch"
```

Make API Calls using Hoppscotch, Postman or cURL command. Keploy with capture those calls to generate the test-suites containing testcases and data mocks.

#### Generate testcases

To genereate testcases we just need to make some API calls. You can use [Postman](https://www.postman.com/), [Hoppscotch](https://hoppscotch.io/), or simply `curl`

1. Post Command To Insert Document

```bash
curl --request POST \
--url http://localhost:8000/documents \
--header 'Content-Type: application/json' \
--data '{
"title" : "somethingTitle",
"content" : "something22"
}'
```
this will return the response which includes the id of the inserted document.
```json
{"id":"1b5wJ5EBIPW7ZBPsse8e"}
```

2. Fetch the Products
```bash
curl --request GET \
--url http://localhost:8000/documents/1b5wJ5EBIPW7ZBPsse8e
```

we will get output:

```json
{"content":"something22","title":"somethingTitle"}
```


Now let's run the test mode (in the mux-elasticsearch directory, not the Keploy directory).

### Run captured testcases

```shell
sudo -E keploy test -c "./mux-elasticsearch" --delay 10 --goCoverage
```


243 changes: 243 additions & 0 deletions mux-elasticsearch/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"strings"
"time"

"github.com/elastic/go-elasticsearch/v8"
"github.com/elastic/go-elasticsearch/v8/esapi"
"github.com/gorilla/mux"
)

type App struct {
Router *mux.Router
DB *elasticsearch.Client
Server *http.Server
}

const SearchIndex = "documents"

func (a *App) Initialize() error {
var err error
a.DB, err = elasticsearch.NewDefaultClient()

if err != nil {
return fmt.Errorf("error : %s", err)
}

_, err = esapi.IndicesExistsRequest{
Index: []string{SearchIndex},
}.Do(context.Background(), a.DB)

if err != nil {
fmt.Println("Indices is not present")
a.CreateIndices()
}

a.Router = mux.NewRouter()
a.Server = &http.Server{
Addr: ":8000",
Handler: a.Router,
}

a.initializeRoutes()

return nil
}

type Document struct {
ID string `json:"id,omitempty"`
Title string `json:"title"`
Content string `json:"content"`
}

type CreateDocumentResponse struct {
ID string `json:"_id"`
}

func (a *App) CreateIndices() {
var err error

_, err = a.DB.Indices.Create(SearchIndex)

if err != nil {
fmt.Println(err)
}
}

func (a *App) createDocument(w http.ResponseWriter, r *http.Request) {
var doc Document
err := json.NewDecoder(r.Body).Decode(&doc)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

data, err := json.Marshal(doc)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

req := esapi.IndexRequest{
Index: "documents",
Body: strings.NewReader(string(data)),
}
res, err := req.Do(context.Background(), a.DB)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer res.Body.Close()

if res.IsError() {
http.Error(w, res.String(), http.StatusInternalServerError)
return
}

var createResponse CreateDocumentResponse
if err := json.NewDecoder(res.Body).Decode(&createResponse); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{"id": createResponse.ID})
}

func (a *App) getDocument(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["id"]

req := esapi.GetRequest{
Index: "documents",
DocumentID: id,
}
res, err := req.Do(context.Background(), a.DB)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer res.Body.Close()

if res.IsError() {
http.Error(w, res.String(), http.StatusNotFound)
return
}

var doc map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&doc); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

source := doc["_source"].(map[string]interface{})

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(source)
}

func (a *App) updateDocument(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["id"]

var doc Document
err := json.NewDecoder(r.Body).Decode(&doc)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

data, err := json.Marshal(doc)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

req := esapi.UpdateRequest{
Index: "documents",
DocumentID: id,
Body: strings.NewReader(fmt.Sprintf(`{"doc": %s}`, string(data))),
}
res, err := req.Do(context.Background(), a.DB)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer res.Body.Close()

if res.IsError() {
http.Error(w, res.String(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
}

func (a *App) deleteDocument(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["id"]

req := esapi.DeleteRequest{
Index: "documents",
DocumentID: id,
}
res, err := req.Do(context.Background(), a.DB)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer res.Body.Close()

if res.IsError() {
http.Error(w, res.String(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
}

func (a *App) Run(port string) {
go func() {
if err := a.Server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Could not listen on %s: %v\n", port, err)
}
}()

quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit

log.Println("Server is shutting down...")

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := a.Server.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}

log.Println("Server exiting")
}

func (a *App) Hello(res http.ResponseWriter, req *http.Request) {
var result = "Hello"
res.Write([]byte(result))
}

func (a *App) initializeRoutes() {
a.Router.HandleFunc("/", a.Hello).Methods("GET")
a.Router.HandleFunc("/documents", a.createDocument).Methods("POST")
a.Router.HandleFunc("/documents/{id}", a.getDocument).Methods("GET")
a.Router.HandleFunc("/documents/{id}", a.updateDocument).Methods("PUT")
a.Router.HandleFunc("/documents/{id}", a.deleteDocument).Methods("DELETE")

}
14 changes: 14 additions & 0 deletions mux-elasticsearch/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module mux-elasticsearch

go 1.22

require (
github.com/elastic/elastic-transport-go/v8 v8.6.0 // indirect
github.com/elastic/go-elasticsearch/v8 v8.14.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
)
17 changes: 17 additions & 0 deletions mux-elasticsearch/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
github.com/elastic/elastic-transport-go/v8 v8.6.0 h1:Y2S/FBjx1LlCv5m6pWAF2kDJAHoSjSRSJCApolgfthA=
github.com/elastic/elastic-transport-go/v8 v8.6.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
github.com/elastic/go-elasticsearch/v8 v8.14.0 h1:1ywU8WFReLLcxE1WJqii3hTtbPUE2hc38ZK/j4mMFow=
github.com/elastic/go-elasticsearch/v8 v8.14.0/go.mod h1:WRvnlGkSuZyp83M2U8El/LGXpCjYLrvlkSgkAH4O5I4=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
Loading

0 comments on commit fae6389

Please sign in to comment.