Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
273 lines (227 sloc) 9.46 KB
package main
import (
"fmt"
"log"
"net/http"
"strconv"
"github.com/gorilla/mux" //third party routing package
)
type keyValue struct { //struct which is used to store different data types to the map
String string
Integer int
IsInteger bool
Float float64
IsFloat bool
Boolean bool
IsBoolean bool
}
var data map[string][]keyValue //map with type []keyValue (struct above)
func main() {
data = map[string][]keyValue{} //creates an instance of that map
r := mux.NewRouter() //creates an instance of the mux router
r.HandleFunc("/keys", getKeys).Methods("GET") //URL with GET method which calls func getKeys
r.HandleFunc("/keys/{key}", getKeys).Methods("GET") //all others are basically the same as above, with additional params
r.HandleFunc("/keys/{key}/{value}", createKey).Methods("POST")
r.HandleFunc("/keys/{key}/{newkey}", updateKeyValue).Methods("PUT")
r.HandleFunc("/keys/{key}/{value}/{newvalue}", updateKeyValue).Methods("PUT")
r.HandleFunc("/keys/{key}", deleteKeyValue).Methods("DELETE")
r.HandleFunc("/keys/{key}/{value}", deleteKeyValue).Methods("DELETE")
log.Fatal(http.ListenAndServe(":8080", r)) //runs the web server and listen on port 8080 wrapped around a log.fatal
}
//function used to displays keys and their values
func getKeys(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r) //gets the variables passed in to the URL request (r) and stores them in to the params variable
k := params["key"] //stores the key param in URL to variable 'k'
//if k isn't empty, show the key: value of the key entered
if k != "" {
if _, ok := data[k]; ok { //checks if the key exists... if resolves to true, continue
for _, item := range data[k] {
switch item {
case item.IsInteger:
fmt.Fprintf(w, "KEY: %v, VALUE: %v, DATA TYPE: Integer\n", k, item.Integer)
break
case item.IsFloat:
fmt.Fprintf(w, "KEY: %v, VALUE: %v, DATA TYPE: Float\n", k, item.Float)
break
case item.IsBoolean:
fmt.Fprintf(w, "KEY: %v, VALUE: %v, DATA TYPE: Boolean\n", k, item.Boolean)
break
default:
fmt.Fprintf(w, "KEY: %v, VALUE: %v, DATA TYPE: String\n", k, item.String)
}
}
} else {
//if key doesn't exist, do the below
fmt.Fprintf(w, "That key doesn't exist in the Key-Value store.")
}
} else {
//loops through all keys in data (doesn't specify a key, as we want to list ALL keys in the key-value map)
for item := range data {
//inner loop which gets all the values to those keys, allowing us to list them on different lines.
for _, value := range data[item] {
it := item
switch {
case item.IsInteger:
fmt.Fprintf(w, "KEY: %v, VALUE: %v, DATA TYPE: Integer\n", k, item.Integer)
case item.IsFloat:
fmt.Fprintf(w, "KEY: %v, VALUE: %v, DATA TYPE: Float\n", k, item.Float)
case item.IsBoolean:
fmt.Fprintf(w, "KEY: %v, VALUE: %v, DATA TYPE: Boolean\n", k, item.Boolean)
default:
fmt.Fprintf(w, "KEY: %v, VALUE: %v, DATA TYPE: String\n", k, item.String)
}
}
}
}
}
func createKey(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
k := params["key"]
v := params["value"]
va := parseKeyValue(v) //parses the 'string' value of variable 'v' into the relevant data type which we store in the struct below.
data[k] = append(data[k], va) //we append to the map, the value of va which is parsed v
switch {
case va.IsInteger:
fmt.Fprintf(w, "KEY: %v VALUE: %v DATA TYPE: Integer was created successfully.", k, v)
case va.IsFloat:
fmt.Fprintf(w, "KEY: %v VALUE: %v DATA TYPE: Float was created successfully.", k, v)
case va.IsBoolean:
fmt.Fprintf(w, "KEY: %v VALUE: %v DATA TYPE: Boolean was created successfully.", k, v)
default:
fmt.Fprintf(w, "KEY: %v VALUE: %v DATA TYPE: String was created successfully.", k, v)
}
}
//this updates the key-value by deleting the old value and creating a new value (I need to figure a better way of doing this)
func updateKeyValue(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
//get the URL params and store them in usable variables
k := params["key"]
v := params["value"]
nk := params["newkey"]
nv := params["newvalue"]
//if v and nv variables are nil, then continue with the below block. This means the KEY only is being updated
if v == "" && nv == "" {
if val, ok := data[k]; ok { //checks if the key exists...
data[nk] = val //if the key exists... take all values of data[k] and create a new key, and append to that.
delete(data, k) //delete the old key
fmt.Fprintf(w, "KEY: %v has been updated with new KEY: %v", k, nk)
} else {
fmt.Fprintf(w, "That key doesn't exist in the Key-Value store")
}
} else { //if we're update the value, do the below
if _, ok := data[k]; ok { //checks if the key exists first
shouldDelete := false //create a variable to determine which should be deleted from the struct.
for i, keyValue := range data[k] { //loop through the keys
switch{
case keyValue.IsInteger:
in, _ := strconv.Atoi(v) //converts the STRING to an integer
if keyValue.Integer == in { //compares the integer variable in keyValue to the in (number given in the URL param) and if it returns true (they're the same) continue with the block of code
shouldDelete = true //sets the shouldDelete variable to true
break
}
case keyValue.IsFloat:
fl, _ := strconv.ParseFloat(v, 64)
if keyValue.Float == fl {
shouldDelete = true
break
}
case keyValue.IsBoolean:
bl, _ := strconv.ParseBool(v)
if keyValue.Boolean == bl {
shouldDelete = true
break
}
case keyValue.String == v:
shouldDelete = true
break
}
if shouldDelete {
//if the shouldDelete variable has been set to true, run this block of code.
if len(data[k])-1 == i {
data[k] = data[k][:i]
} else {
data[k] = append(data[k][:i], data[k][i+1:]...)
}
break
}
}
if !shouldDelete {
//No element matching the V, and to we can't delete.
fmt.Fprintf(w, "KEY: %v, VALUE: %v wasn't found in the Key-Value store", k, v)
}
//parse the nv and get the datatype back and store it in variable va
va := parseKeyValue(nv)
data[k] = append(data[k], va) //append va to the data["KEY"]
fmt.Fprintf(w, "KEY: %v: VALUE: %v has been updated with Value: %v", k, v, nv)
} else {
fmt.Fprintf(w, "That key-value pairing doesn't exist in the Key-Value store")
}
}
}
//function to delete keys
func deleteKeyValue(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
k := params["key"]
v := params["value"]
//delete the value
if v != "" { //checks if the value param isn't empty... if it isn't, we want to delete the VALUE from the KEY in the MAP
if _, ok := data[k]; ok { //validates that the KEY exists.
shouldDelete := false //creates variable and sets to false
for i, keyValue := range data[k] { //iterates over the KEY values in the MAP
if keyValue.IsInteger { //checks if this is true - if it is, continue
in, _ := strconv.Atoi(v) //converts V (String) to int and stores in variable in
if keyValue.Integer == in { //checks the integer variable in the keyValue list and compares it to the in variable we just created.
shouldDelete = true //sets shouldDelete to true
}
} else if keyValue.IsFloat {
fl, _ := strconv.ParseFloat(v, 64) //converts string to float64
if keyValue.Float == fl { //checks if the keyValue.Float variable is the same as f1 variable and continues if it is
shouldDelete = true
}
} else if keyValue.IsBoolean {
bl, _ := strconv.ParseBool(v)
if keyValue.Boolean == bl {
shouldDelete = true
}
} else if keyValue.String == v {
shouldDelete = true
}
if shouldDelete { //checks the should delete variable, if it is true... then one of the conditions above was met
if len(data[k])-1 == i { //checks the length of the slice, -1 to that value and compares it with the value of i... deleting the last slice.
data[k] = data[k][:i]
} else {
data[k] = append(data[k][:i], data[k][i+1:]...) //will run if the element entered, is not the last one in the slice.
}
fmt.Fprintf(w, "KEY: %v, VALUE: %v has been deleted from the Key-Value store", k, v)
break
}
}
if !shouldDelete {
//There was no element matching variable v and so we print the below
fmt.Fprintf(w, "KEY: %v, VALUE: %v wasn't found in the Key-Value store", k, v)
}
} else {
fmt.Fprintf(w, "That Key-Value pairing doesn't exist in the Key-Value store")
}
} else { //if no value was entered, then just delete the whole key and all values inside.
if _, ok := data[k]; ok {
delete(data, k)
fmt.Fprintf(w, "KEY: %v has been deleted from the Key-Value store", k)
} else {
fmt.Fprintf(w, "That key doesn't exist in the Key-Value store")
}
}
}
//helper function to parse the url params into valid data types
func parseKeyValue(urlParams string) keyValue { //return keyValue
var paramValue keyValue //sets paramValue to type keyValue
var err error
paramValue.String = urlParams //set the urlparam to a string (everything entered is a string.)
paramValue.Integer, err = strconv.Atoi(urlParams) //check if it can be converted to an integer
paramValue.IsInteger = err == nil //set the isInteger to true if err == nil. The below is exactly the same thing
paramValue.Float, err = strconv.ParseFloat(urlParams, 64)
paramValue.IsFloat = err == nil
paramValue.Boolean, err = strconv.ParseBool(urlParams)
paramValue.IsBoolean = err == nil
return paramValue //return the paramValue to keyValue
}