Server-Side Scripting/Document Databases/Go

From Wikiversity
Jump to navigation Jump to search

routes/lesson10.go[edit | edit source]

// This program creates and displays a temperature database
// with options to insert, update, and delete records.
//
// References:
//  https://golang.org/doc/
//  https://www.mongodb.com/languages/golang
//
// Requires a Mongo installation to manage the database.
// Use of a Docker container is recommended.
// See https://en.wikiversity.org/wiki/Docker/MongoDB .
// If both the Node website and Mongo are running in containers, 
// use 172.17.0.2 for the mongodb host address.
// If the Node website and/or Mongo are running locally, use 127.0.0.1
// for the mongodb host address.

package routes

import (
    "context"
    "html/template"
    "log"
    "net/http"
    "path/filepath"
    "strconv"
    "time"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

// const HOST = "mongodb://172.17.0.2";
const HOST = "mongodb://127.0.0.1";
// const HOST = "mongodb://mongo-server"
const DATABASE = "temperature"
const COLLECTION = "countries"

func Lesson10(response http.ResponseWriter, request *http.Request) {
	response.Header().Set("Content-Type", "text/html; charset=utf-8")

	type Data struct {
		Table  template.HTML
	}
	
	result := ""

    switch request.Method {
        case "GET":
            result = processGet10()
        case "POST":
            result = processPost10(request)
        default:
            result = "Unexpected request method: " + request.Method
    }

	data := Data{template.HTML(result)}
	path := filepath.Join("templates", "lesson10.html")
	parsed, _ := template.ParseFiles(path)
	parsed.Execute(response, data)
}

func processGet10() string {
    result := getData10()
    return result
}

func processPost10(request *http.Request) string {
    country := request.FormValue("country")
    temperature := request.FormValue("temperature")

    if !countryExists10(country) {
        insertCountry10(country, temperature)
    } else if (temperature != "") {
        updateCountry10(country, temperature)
    } else {
        deleteCountry10(country)
    }

    result := getData10()
    return result
}

func getData10() string {
    client, err := mongo.NewClient(options.Client().ApplyURI(HOST))
    if err != nil {
        log.Fatal(err)
    }

    ctxt, _ := context.WithTimeout(context.Background(), 10 * time.Second)
    err = client.Connect(ctxt)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctxt)

    collection := client.Database(DATABASE).Collection(COLLECTION)
    cursor, err := collection.Find(ctxt, bson.M{})
    if err != nil {
        log.Fatal(err)
    }
    defer cursor.Close(ctxt)

    var documents []bson.M
    err = cursor.All(ctxt, &documents)
    if err != nil {
        log.Fatal(err)
    }

    result := formatDocuments10(documents)
    return result;
}

func formatDocuments10(documents []bson.M) string {
    result := "<table><tr><th>ID</th>"
    result += "<th>Country</th>"
    result += "<th>Temperature</th></tr>"

    for _, document := range documents {
        id := document["_id"].(primitive.ObjectID)
        country := document["country"].(string)
        temperature := document["temperature"].(float64)

        result += "<tr><td>" + id.Hex() + "</td>"
        result += "<td>" + country + "</td>"
        result += "<td>" + strconv.FormatFloat(temperature, 'f', 1, 64) + "</td></tr>"
    }

    result += "</table>"
    return result
}

func countryExists10(country string) bool {
    client, err := mongo.NewClient(options.Client().ApplyURI(HOST))
    if err != nil {
        log.Fatal(err)
    }

    ctxt, _ := context.WithTimeout(context.Background(), 10 * time.Second)
    err = client.Connect(ctxt)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctxt)

    collection := client.Database(DATABASE).Collection(COLLECTION)
    filter := bson.D {
        {"country", country},
    }
    count, err := collection.CountDocuments(ctxt, filter)
	if err != nil {
		log.Fatal(err)
	}    

    return count == 1
}

func insertCountry10(country string, temperature string) {
    value, err := strconv.ParseFloat(temperature, 32)
    if err != nil {
        log.Fatal(err)
    }

    client, err := mongo.NewClient(options.Client().ApplyURI(HOST))
    if err != nil {
        log.Fatal(err)
    }

    ctxt, _ := context.WithTimeout(context.Background(), 10 * time.Second)
    err = client.Connect(ctxt)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctxt)

    collection := client.Database(DATABASE).Collection(COLLECTION)
    document := bson.D {
            {"country", country},
            {"temperature", value},
    }
    _, err = collection.InsertOne(ctxt, document)
    if err != nil {
        log.Fatal(err)
    }
}

func updateCountry10(country string, temperature string) {
    value, err := strconv.ParseFloat(temperature, 32)
    if err != nil {
        log.Fatal(err)
    }

    client, err := mongo.NewClient(options.Client().ApplyURI(HOST))
    if err != nil {
        log.Fatal(err)
    }

    ctxt, _ := context.WithTimeout(context.Background(), 10 * time.Second)
    err = client.Connect(ctxt)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctxt)

    collection := client.Database(DATABASE).Collection(COLLECTION)
    filter := bson.M {
        "country": country,
    }
    update := bson.D{
        {"$set", bson.D{{"temperature", value}}},
    }
    _, err = collection.UpdateOne(ctxt, filter, update)
    if err != nil {
        log.Fatal(err)
    }
}

func deleteCountry10(country string) {
    client, err := mongo.NewClient(options.Client().ApplyURI(HOST))
    if err != nil {
        log.Fatal(err)
    }

    ctxt, _ := context.WithTimeout(context.Background(), 10 * time.Second)
    err = client.Connect(ctxt)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(ctxt)

    collection := client.Database(DATABASE).Collection(COLLECTION)
    filter := bson.M {
        "country": country,
    }
    _, err = collection.DeleteOne(ctxt, filter)
    if err != nil {
        log.Fatal(err)
    }
}

templates/lesson10.html[edit | edit source]

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Lesson 10</title>
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <h1>Temperature Data Entry</h1>
    <p>Enter country and temperature. Enter again to update.</p>
    <p>Enter country without temperature to delete.</p>
    <form method="POST">
        <p><label for="country">Country:</label>
            <input type="text" id="country" name="country" required>
        </p>
        <p><label for="stop">Temperature:</label>
            <input type="text" id="temperature" name="temperature">
        </p>
        <p><input type="submit" name="submit" value="Submit"></p>
    </form>
    <hr>
    {{.Table}}
</body>
</html>

Try It[edit | edit source]

See Server-Side Scripting/Routes and Templates/Go to create a test environment.