Skip to content

Instantly share code, notes, and snippets.

@bitroniq
Last active July 13, 2022 07:51
Show Gist options
  • Save bitroniq/ff6d6e48a2f8e646603ab5f6d705e9c8 to your computer and use it in GitHub Desktop.
Save bitroniq/ff6d6e48a2f8e646603ab5f6d705e9c8 to your computer and use it in GitHub Desktop.
log-shipping-api
MYSQL_HOST = localhost:3306
MYSQL_DATABASE = kafka_test
MYSQL_USERNAME = root
MYSQL_PASSWORD = root
API_HOST = localhost
API_PORT = 8080

log-shipping-api

Snippets for interview

package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
"strings"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
var (
host = kingpin.Flag("host", "Host for this service").String()
port = kingpin.Flag("port", "Port for this service").String()
config = kingpin.Flag("config", "Path of config file").String()
)
const DEFAULT_HOST = "localhost"
const DEFAULT_PORT = "8080"
func main() {
kingpin.Parse()
r := mux.NewRouter()
// connect to db
var err error
if *config != "" {
err = godotenv.Load(*config)
} else {
err = godotenv.Load()
}
if err != nil {
log.Panic("Loading error while loading environment: ", err)
}
log.Println("Environment set up successfully!")
// set up host and port
hostP := *host
portP := *port
// Set host
if hostP == "" {
osEnvHost := os.Getenv("API_HOST")
if osEnvHost == "" {
hostP = DEFAULT_HOST
} else {
hostP = osEnvHost
}
}
// Set port
if portP == "" {
osEnvPort := os.Getenv("API_PORT")
if osEnvPort == "" {
portP = DEFAULT_PORT
} else {
portP = osEnvPort
}
}
var b strings.Builder
fmt.Fprintf(&b, "%s:%s@tcp(%s)/%s?charset=utf8", os.Getenv("MYSQL_USERNAME"), os.Getenv("MYSQL_PASSWORD"), os.Getenv("MYSQL_HOST"), os.Getenv("MYSQL_DATABASE"))
// try to connect db.
var db *sql.DB
db, err = sql.Open("mysql", b.String())
if err != nil {
log.Panic("Failed to connected to DB: ", err)
}
err = db.Ping()
if err != nil {
db.Close()
log.Panic("Failed to connected to DB: ", err)
}
log.Println("Sucessfully connected to db.")
serverMapping := ServerMapping{DB: db}
// A route with a route variable:
r.HandleFunc("/server-mapping", serverMapping.CreateServerMapping).Methods("POST")
r.HandleFunc("/server-mapping", serverMapping.GetServerMapping).Methods("GET")
r.HandleFunc("/server-mapping/{id}", serverMapping.GetServerMapping).Methods("GET")
r.HandleFunc("/server-mapping/{id}", serverMapping.UpdateServerMapping).Methods("PUT")
r.HandleFunc("/server-mapping/{id}", serverMapping.DeleteServerMapping).Methods("DELETE")
server := fmt.Sprintf("%s:%s", hostP, portP)
log.Printf("Server is running on %s", server)
http.ListenAndServe(server, r)
}
package main
import (
"database/sql"
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
)
type ServerMapping struct {
DB *sql.DB
}
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
type GetServerMappingResponse struct {
ID int64 `json:"id"`
Host string `json:"host"`
Server string `json:"server"`
}
type CreateServerMappingRequest struct {
Host string `json:"host"`
Server string `json:"server"`
}
// CreateServerMapping create a new server mapping
func (serverMapping *ServerMapping) CreateServerMapping(w http.ResponseWriter, r *http.Request) {
log.Println("Create request is accepted.")
w.Header().Set("Content-Type", "application/json")
if r.Body == nil {
log.Printf("Status: %d Message: Must supply body.", http.StatusBadRequest)
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusBadRequest, Message: "Must supply body."})
return
}
defer r.Body.Close()
params := CreateServerMappingRequest{}
if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
log.Printf("Status: %d Failed to decode params: %s", http.StatusBadRequest, err)
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusBadRequest, Message: "Unable to decode body."})
return
}
if params.Host == "" || params.Server == "" {
log.Printf("Status: %d Must supply host and server in the body", http.StatusBadRequest)
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusBadRequest, Message: "Must supply host and server."})
return
}
const insertSQL = `
INSERT INTO sys_servers (host, server) VALUES (?, ?)
`
stmtIns, err := serverMapping.DB.Prepare(insertSQL)
if err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
defer stmtIns.Close() // Close
_, err = stmtIns.Exec(params.Host, params.Server)
if err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
const getLastInsertSQL = `
SELECT * FROM sys_servers WHERE id= LAST_INSERT_ID()
`
response := GetServerMappingResponse{}
if err := serverMapping.DB.QueryRow(getLastInsertSQL).Scan(&response.ID, &response.Host, &response.Server); err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
log.Println("Successfully created mapping")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(response)
}
// GetServerMapping get server mapping based on id and host
func (serverMapping *ServerMapping) GetServerMapping(w http.ResponseWriter, r *http.Request) {
log.Println("Get request is accepted.")
mappingID := mux.Vars(r)["id"]
searchHost := r.FormValue("host")
defer r.Body.Close()
w.Header().Set("Content-Type", "application/json")
if mappingID != "" {
// Query with id
log.Println("Searching mappings using id:", mappingID)
selectQuery := `SELECT id, host, server FROM sys_servers WHERE id=?;`
var mapping GetServerMappingResponse
if err := serverMapping.DB.QueryRow(selectQuery, mappingID).Scan(&mapping.ID, &mapping.Host, &mapping.Server); err != nil {
if err == sql.ErrNoRows {
log.Printf("Status: %d %s is invalid id.", http.StatusBadRequest, mappingID)
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusBadRequest, Message: "Invalid id is provided."})
return
}
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
json.NewEncoder(w).Encode(mapping)
} else if searchHost != "" {
// Query with host
log.Println("Searching mapping using host:", searchHost)
selectQuery := `SELECT id, host, server FROM sys_servers WHERE host=?;`
rows, err := serverMapping.DB.Query(selectQuery, searchHost)
if err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
defer rows.Close()
mappings := []GetServerMappingResponse{}
for rows.Next() {
mapping := GetServerMappingResponse{}
if err := rows.Scan(&mapping.ID, &mapping.Host, &mapping.Server); err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
mappings = append(mappings, mapping)
}
json.NewEncoder(w).Encode(mappings)
} else {
log.Println("Search all mappings")
selectQuery := `SELECT id, host, server FROM sys_servers;`
rows, err := serverMapping.DB.Query(selectQuery)
if err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
defer rows.Close()
mappings := []GetServerMappingResponse{}
for rows.Next() {
mapping := GetServerMappingResponse{}
if err := rows.Scan(&mapping.ID, &mapping.Host, &mapping.Server); err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
mappings = append(mappings, mapping)
}
json.NewEncoder(w).Encode(mappings)
}
}
// UpdateServerMapping updates server mapping
func (serverMapping *ServerMapping) UpdateServerMapping(w http.ResponseWriter, r *http.Request) {
log.Println("Get request is accepted.")
w.Header().Set("Content-Type", "application/json")
mappingID := mux.Vars(r)["id"]
defer r.Body.Close()
params := CreateServerMappingRequest{}
if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
log.Printf("Status: %d Failed to decode params: %s", http.StatusBadRequest, err)
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusBadRequest, Message: "Unable to decode body."})
return
}
if params.Host == "" || params.Server == "" {
log.Printf("Status: %d Must supply host and server in the body", http.StatusBadRequest)
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusBadRequest, Message: "Must supply host and server."})
return
}
log.Println("Searching mappings using id:", mappingID)
selectQuery := `SELECT id, host, server FROM sys_servers WHERE id=?;`
var mapping GetServerMappingResponse
if err := serverMapping.DB.QueryRow(selectQuery, mappingID).Scan(&mapping.ID, &mapping.Host, &mapping.Server); err != nil {
if err == sql.ErrNoRows {
log.Printf("Status: %d %s is invalid id.", http.StatusBadRequest, mappingID)
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusBadRequest, Message: "Invalid id is provided."})
return
}
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
log.Println("Updating mappings using id:", mappingID)
updateQuery := `UPDATE sys_servers SET host = ?, server = ? WHERE id=?;`
stmtIns, err := serverMapping.DB.Prepare(updateQuery)
if err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
defer stmtIns.Close() // Close
_, err = stmtIns.Exec(params.Host, params.Server, mappingID)
if err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
log.Println("Successfully updated mapping")
return
}
// DeleteServerMapping deletes server mapping
func (serverMapping *ServerMapping) DeleteServerMapping(w http.ResponseWriter, r *http.Request) {
log.Println("Get request is accepted.")
mappingID := mux.Vars(r)["id"]
defer r.Body.Close()
w.Header().Set("Content-Type", "application/json")
log.Println("Searching mappings using id:", mappingID)
selectQuery := `SELECT id, host, server FROM sys_servers WHERE id=?;`
var mapping GetServerMappingResponse
if err := serverMapping.DB.QueryRow(selectQuery, mappingID).Scan(&mapping.ID, &mapping.Host, &mapping.Server); err != nil {
if err == sql.ErrNoRows {
log.Printf("Status: %d %s is invalid id.", http.StatusBadRequest, mappingID)
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusBadRequest, Message: "Invalid id is provided."})
return
}
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
log.Println("Deleting mappings using id:", mappingID)
deleteQuery := `DELETE from sys_servers WHERE id=?;`
stmtIns, err := serverMapping.DB.Prepare(deleteQuery)
if err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
defer stmtIns.Close() // Close
_, err = stmtIns.Exec(mappingID)
if err != nil {
log.Printf("Status: %d DB Error: %s", http.StatusForbidden, err)
w.WriteHeader(http.StatusForbidden)
json.NewEncoder(w).Encode(ErrorResponse{Code: http.StatusForbidden, Message: "DB Error."})
return
}
log.Println("Successfully deleted mapping")
return
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment