@ -1,53 +1,62 @@
package main
package main
import (
import (
"encoding/json "
"flag "
"fmt"
"fmt"
"io"
"log"
"log"
"net/http"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"time"
)
type RandomData struct {
// unexported for json
ID int64
DateCreated time . Time
LastUpdated time . Time
// exported for json
Title string ` json:"title" `
Text string ` json:"text" `
}
type randomDataHandler struct {
dbhandle "github.com/Unbewohnte/crud-api/dbHandle"
dbFilepath string
"github.com/Unbewohnte/crud-api/logs"
}
)
func InitLogs ( ) {
var (
var logsDir string = filepath . Join ( "." , "logs" )
port * uint = flag . Uint ( "port" , 8080 , "Specifies a port on which the helping page will be served" )
dbname string = "database.db"
)
err := os . MkdirAll ( logsDir , os . ModePerm )
func init ( ) {
// set up logs, parse flags
err := logs . SetUp ( )
if err != nil {
if err != nil {
panic ( err )
panic ( err )
}
}
logfile , err := os . Create ( filepath . Join ( logsDir , "logs.log" ) )
flag . Parse ( )
}
func main ( ) {
// create a local db file
db , err := dbhandle . CreateLocalDB ( dbname )
if err != nil {
if err != nil {
panic ( err )
log . Fatalf ( "error setting up a database: %s" , err )
}
mux := http . NewServeMux ( )
mux . HandleFunc ( "/" , helpPage )
mux . HandleFunc ( "/randomdata" , db . HandleGlobalWeb )
mux . HandleFunc ( "/randomdata/" , db . HandleSpecificWeb )
server := & http . Server {
Addr : fmt . Sprintf ( ":%d" , * port ) ,
Handler : mux ,
ReadTimeout : 5 * time . Second ,
WriteTimeout : 5 * time . Second ,
}
}
log . SetOutput ( logfile )
log . Printf ( "Starting on port %d\n" , * port )
fmt . Printf ( "Starting on port %d\n" , * port )
log . Fatal ( server . ListenAndServe ( ) )
}
}
func homepage ( w http . ResponseWriter , r * http . Request ) {
func hel pP age ( w http . ResponseWriter , r * http . Request ) {
helpMessage := `
helpMessage := `
< h1 > CRUD api in Go ' s standart library < / h1 >
< h1 > CRUD api < / h1 >
< ul >
< ul >
< li > ( GET ) < a href = "/randomdata" > / randomdata < / a > - to get all database < / li >
< li > ( GET ) < a href = "/randomdata" > / randomdata < / a > - to get all info from database ( obviously a bad idea in serious projects ᗜˬᗜ ) < / li >
< li > ( GET ) / randomdata / { id } - to get specific random data under corresponding id < / li >
< li > ( GET ) / randomdata / { id } - to get specific random data under corresponding id < / li >
< li > ( POST ) / randomdata - to create random data < / li >
< li > ( POST ) / randomdata - to create random data < / li >
< li > ( DELETE ) / randomdata / { id } - to delete specified random data < / li >
< li > ( DELETE ) / randomdata / { id } - to delete specified random data < / li >
< li > ( PUT ) / randomdata / { id } - to update random data with given id < / li >
< li > ( PUT ) / randomdata / { id } - to update random data with given id < / li >
< / ul >
< / ul >
@ -55,316 +64,105 @@ func homepage(w http.ResponseWriter, r *http.Request) {
fmt . Fprint ( w , helpMessage )
fmt . Fprint ( w , helpMessage )
}
}
func newDatabaseHandler ( ) * randomDataHandler {
// func (dbHandler *randomDataHandler) updateSpecificRandomData(w http.ResponseWriter, r *http.Request) {
dbDirpath := filepath . Join ( "." , "database" )
// if r.Header.Get("content-type") != "application/json" {
err := os . MkdirAll ( dbDirpath , os . ModePerm )
// w.WriteHeader(http.StatusUnsupportedMediaType)
if err != nil {
// w.Write([]byte(fmt.Sprintf("Got `%s` instead of `application/json`", r.Header.Get("content-type"))))
panic ( err )
// return
}
// }
dbFilepath := filepath . Join ( dbDirpath , "database.json" )
// requestBody, err := io.ReadAll(r.Body)
dbFile , err := os . OpenFile ( dbFilepath , os . O_CREATE , os . ModePerm )
// if err != nil {
if err != nil {
// log.Println("Error reading http request (create) : ", err)
panic ( err )
// w.WriteHeader(http.StatusBadRequest)
}
// r.Body.Close()
defer dbFile . Close ( )
// return
// }
log . Println ( "Successfully created new database handler" )
// defer r.Body.Close()
return & randomDataHandler {
// var givenUpdatedRandomData RandomData
dbFilepath : dbFilepath ,
// err = json.Unmarshal(requestBody, &givenUpdatedRandomData)
}
// if err != nil {
}
// log.Println("Error unmarshalling request body (updateSpecificRandomData) : ", err)
// return
func ( dbHandler * randomDataHandler ) writeRandomData ( newData RandomData ) error {
// }
dbBytes , err := dbHandler . readDatabase ( )
if err != nil {
// givenID := strings.Split(r.URL.String(), "/")[2]
log . Println ( "Error reading db (writeRandomData) : " , err )
}
// dbBytes, err := dbHandler.readDatabase()
// if err != nil {
var db [ ] RandomData
// log.Println("Error reading db (updateSpecificRandomData) : ", err)
// w.WriteHeader(http.StatusInternalServerError)
err = json . Unmarshal ( dbBytes , & db )
// return
if err != nil {
// }
log . Println ( "Error unmarshalling db (writeRandomData) : " , err )
// var db []RandomData
}
// err = json.Unmarshal(dbBytes, &db)
db = append ( db , newData )
// if err != nil {
// log.Println("Error unmarshalling database (update) : ", err)
dbFile , err := os . OpenFile ( dbHandler . dbFilepath , os . O_WRONLY , 0644 )
// return
if err != nil {
// }
log . Println ( "Error opening db file (writeRandomData) : " , err )
}
// int64GivenID, _ := strconv.ParseInt(givenID, 10, 64)
defer dbFile . Close ( )
// var counter int64
// for _, randomData := range db {
jsonBytes , err := json . MarshalIndent ( db , "" , " " )
// if int64GivenID == randomData.ID {
if err != nil {
// var updatedRandomData RandomData
log . Println ( "Error marshalling db (writeRandomData) : " , err )
}
// updatedRandomData = givenUpdatedRandomData
// updatedRandomData.ID = randomData.ID
dbFile . Write ( jsonBytes )
// updatedRandomData.DateCreated = randomData.DateCreated
// updatedRandomData.LastUpdated = time.Now().UTC()
return nil
}
// dbHandler.removeRandomData(int64GivenID)
// dbHandler.writeRandomData(updatedRandomData)
func ( dbHandler * randomDataHandler ) readDatabase ( ) ( [ ] byte , error ) {
dbBytes , err := os . ReadFile ( dbHandler . dbFilepath )
// log.Printf("Successfully updated RandomData with id %v \n", updatedRandomData.ID)
if err != nil {
// return
log . Println ( "Error reading db (readDatabase) : " , err )
// }
return nil , err
// counter++
}
// }
return dbBytes , nil
}
// w.WriteHeader(http.StatusNotFound)
// }
func ( dbHandler * randomDataHandler ) removeRandomData ( id int64 ) error {
dbBytes , err := dbHandler . readDatabase ( )
// func (dbHandler *randomDataHandler) deleteSpecificRandomData(w http.ResponseWriter, r *http.Request) {
if err != nil {
// givenID := strings.Split(r.URL.String(), "/")[2]
return err
}
// int64GivenID, _ := strconv.ParseInt(givenID, 10, 64)
var db [ ] RandomData
// err := dbHandler.removeRandomData(int64GivenID)
err = json . Unmarshal ( dbBytes , & db )
// if err != nil {
if err != nil {
// log.Println("Error removing RandomData (deleteSpecificRandomData) : ", err)
return err
// w.WriteHeader(http.StatusInternalServerError)
}
// return
// }
var counter int64 = 0
for _ , randomData := range db {
// w.WriteHeader(http.StatusOK)
if id == randomData . ID {
db = append ( db [ : counter ] , db [ counter + 1 : ] ... )
// log.Printf("Successfully deleted RandomData with id %v \n", int64GivenID)
err = dbHandler . writeDB ( db )
// }
if err != nil {
return err
// func (dbHandler *randomDataHandler) handle(w http.ResponseWriter, r *http.Request) {
}
// switch r.Method {
}
// case "GET":
counter ++
// dbHandler.get(w, r)
}
// case "POST":
return nil
// dbHandler.create(w, r)
}
// default:
// w.WriteHeader(http.StatusMethodNotAllowed)
func ( dbHandler * randomDataHandler ) writeDB ( db [ ] RandomData ) error {
// }
jsonEncodedDB , err := json . MarshalIndent ( db , "" , " " )
// }
if err != nil {
return err
// func (dbHandler *randomDataHandler) handleSpecific(w http.ResponseWriter, r *http.Request) {
}
// switch r.Method {
// case "GET":
dbFile , err := os . OpenFile ( dbHandler . dbFilepath , os . O_WRONLY | os . O_TRUNC , 0644 )
// dbHandler.getSpecificRandomData(w, r)
if err != nil {
// case "PUT":
return err
// dbHandler.updateSpecificRandomData(w, r)
}
// case "DELETE":
defer dbFile . Close ( )
// dbHandler.deleteSpecificRandomData(w, r)
// default:
dbFile . Write ( jsonEncodedDB )
// w.WriteHeader(http.StatusMethodNotAllowed)
// }
return nil
// }
}
func ( dbHandler * randomDataHandler ) get ( w http . ResponseWriter , r * http . Request ) {
dbBytes , err := dbHandler . readDatabase ( )
if err != nil {
log . Println ( "Error reading db (get) : " , err )
w . WriteHeader ( http . StatusInternalServerError )
return
}
w . Write ( dbBytes )
}
func ( dbHandler * randomDataHandler ) create ( w http . ResponseWriter , r * http . Request ) {
if r . Header . Get ( "content-type" ) != "application/json" {
w . WriteHeader ( http . StatusUnsupportedMediaType )
w . Write ( [ ] byte ( fmt . Sprintf ( "Got `%s` instead of `application/json`" , r . Header . Get ( "content-type" ) ) ) )
return
}
requestBody , err := io . ReadAll ( r . Body )
if err != nil {
log . Println ( "Error reading http request (create) : " , err )
w . WriteHeader ( http . StatusBadRequest )
return
}
defer r . Body . Close ( )
var newRandomData RandomData
err = json . Unmarshal ( requestBody , & newRandomData )
if err != nil {
log . Printf ( "Error unmarshalling http request (create) : %q \n" , err )
w . WriteHeader ( http . StatusBadRequest )
return
}
newRandomData . DateCreated = time . Now ( ) . UTC ( )
newRandomData . LastUpdated = newRandomData . DateCreated
newRandomData . ID = time . Now ( ) . UTC ( ) . UnixNano ( )
err = dbHandler . writeRandomData ( newRandomData )
if err != nil {
log . Println ( "Error writing RandomData (create): " , err )
w . WriteHeader ( http . StatusInternalServerError )
return
}
w . WriteHeader ( http . StatusOK )
log . Println ( "Successfuly added to db : " , newRandomData )
}
func ( dbHandler * randomDataHandler ) getSpecificRandomData ( w http . ResponseWriter , r * http . Request ) {
givenID := strings . Split ( r . URL . String ( ) , "/" ) [ 2 ]
dbBytes , err := dbHandler . readDatabase ( )
if err != nil {
log . Println ( "Error reading db (getSpecificRandomData) : " , err )
w . WriteHeader ( http . StatusInternalServerError )
return
}
var db [ ] RandomData
err = json . Unmarshal ( dbBytes , & db )
if err != nil {
log . Println ( "Error unmarshalling database (getSpecificRandomData) : " , err )
w . WriteHeader ( http . StatusInternalServerError )
return
}
int64GivenID , _ := strconv . ParseInt ( givenID , 10 , 64 )
for _ , randomData := range db {
if int64GivenID == randomData . ID {
response , err := json . MarshalIndent ( randomData , "" , " " )
if err != nil {
log . Println ( "Error marshaling response(getSpecificRandomData) : " , err )
w . WriteHeader ( http . StatusInternalServerError )
return
}
w . Write ( response )
return
}
}
w . WriteHeader ( http . StatusNotFound )
}
func ( dbHandler * randomDataHandler ) updateSpecificRandomData ( w http . ResponseWriter , r * http . Request ) {
if r . Header . Get ( "content-type" ) != "application/json" {
w . WriteHeader ( http . StatusUnsupportedMediaType )
w . Write ( [ ] byte ( fmt . Sprintf ( "Got `%s` instead of `application/json`" , r . Header . Get ( "content-type" ) ) ) )
return
}
requestBody , err := io . ReadAll ( r . Body )
if err != nil {
log . Println ( "Error reading http request (create) : " , err )
w . WriteHeader ( http . StatusBadRequest )
r . Body . Close ( )
return
}
defer r . Body . Close ( )
var givenUpdatedRandomData RandomData
err = json . Unmarshal ( requestBody , & givenUpdatedRandomData )
if err != nil {
log . Println ( "Error unmarshalling request body (updateSpecificRandomData) : " , err )
return
}
givenID := strings . Split ( r . URL . String ( ) , "/" ) [ 2 ]
dbBytes , err := dbHandler . readDatabase ( )
if err != nil {
log . Println ( "Error reading db (updateSpecificRandomData) : " , err )
w . WriteHeader ( http . StatusInternalServerError )
return
}
var db [ ] RandomData
err = json . Unmarshal ( dbBytes , & db )
if err != nil {
log . Println ( "Error unmarshalling database (update) : " , err )
return
}
int64GivenID , _ := strconv . ParseInt ( givenID , 10 , 64 )
var counter int64
for _ , randomData := range db {
if int64GivenID == randomData . ID {
var updatedRandomData RandomData
updatedRandomData = givenUpdatedRandomData
updatedRandomData . ID = randomData . ID
updatedRandomData . DateCreated = randomData . DateCreated
updatedRandomData . LastUpdated = time . Now ( ) . UTC ( )
dbHandler . removeRandomData ( int64GivenID )
dbHandler . writeRandomData ( updatedRandomData )
log . Printf ( "Successfully updated RandomData with id %v \n" , updatedRandomData . ID )
return
}
counter ++
}
w . WriteHeader ( http . StatusNotFound )
}
func ( dbHandler * randomDataHandler ) deleteSpecificRandomData ( w http . ResponseWriter , r * http . Request ) {
givenID := strings . Split ( r . URL . String ( ) , "/" ) [ 2 ]
int64GivenID , _ := strconv . ParseInt ( givenID , 10 , 64 )
err := dbHandler . removeRandomData ( int64GivenID )
if err != nil {
log . Println ( "Error removing RandomData (deleteSpecificRandomData) : " , err )
w . WriteHeader ( http . StatusInternalServerError )
return
}
w . WriteHeader ( http . StatusOK )
log . Printf ( "Successfully deleted RandomData with id %v \n" , int64GivenID )
}
func ( dbHandler * randomDataHandler ) handle ( w http . ResponseWriter , r * http . Request ) {
switch r . Method {
case "GET" :
dbHandler . get ( w , r )
case "POST" :
dbHandler . create ( w , r )
default :
w . WriteHeader ( http . StatusMethodNotAllowed )
}
}
func ( dbHandler * randomDataHandler ) handleSpecific ( w http . ResponseWriter , r * http . Request ) {
switch r . Method {
case "GET" :
dbHandler . getSpecificRandomData ( w , r )
case "PUT" :
dbHandler . updateSpecificRandomData ( w , r )
case "DELETE" :
dbHandler . deleteSpecificRandomData ( w , r )
default :
w . WriteHeader ( http . StatusMethodNotAllowed )
}
}
func init ( ) {
InitLogs ( )
}
func main ( ) {
databaseHandler := newDatabaseHandler ( )
servemux := http . NewServeMux ( )
servemux . HandleFunc ( "/" , homepage )
servemux . HandleFunc ( "/randomdata" , databaseHandler . handle )
servemux . HandleFunc ( "/randomdata/" , databaseHandler . handleSpecific )
server := & http . Server {
Addr : ":8000" ,
Handler : servemux ,
ReadTimeout : 5 * time . Second ,
WriteTimeout : 5 * time . Second ,
}
log . Fatal ( server . ListenAndServe ( ) )
}