From 99b6c74ea165a5ce996b9d243779cf8f6caf608d Mon Sep 17 00:00:00 2001 From: Unbewohnte <65883674+Unbewohnte@users.noreply.github.com> Date: Sat, 10 Apr 2021 17:44:42 +0300 Subject: [PATCH] Add files via upload --- curl_examples | 6 + database/database.json | 23 +++ main.go | 367 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 396 insertions(+) create mode 100644 curl_examples create mode 100644 database/database.json create mode 100644 main.go diff --git a/curl_examples b/curl_examples new file mode 100644 index 0000000..ecb5135 --- /dev/null +++ b/curl_examples @@ -0,0 +1,6 @@ +curl localhost:8000/randomdata +curl localhost:8000/randomdata -H "content-type:application/json" -d '{"title":"This is a title","text":"This is a text"}' -X POST +curl localhost:8000/randomdata/1618064651615612586 -H "content-type:application/json" -d {"title":"This is an updated title","text":"This is an updated text"}' -X PUT +curl localhost:8000/randomdata/1618065099475731301 +curl localhost:8000/randomdata/1618065099475731301 -X DELETE + diff --git a/database/database.json b/database/database.json new file mode 100644 index 0000000..05590d1 --- /dev/null +++ b/database/database.json @@ -0,0 +1,23 @@ +[ + { + "ID": 1618064636424100339, + "DateCreated": "2021-04-10T14:23:56.424100065Z", + "LastUpdated": "2021-04-10T14:23:56.424100065Z", + "title": "FUMO?", + "text": "FUMO" + }, + { + "ID": 1618064651615612586, + "DateCreated": "2021-04-10T14:24:11.615612068Z", + "LastUpdated": "2021-04-10T14:24:11.615612068Z", + "title": "Title", + "text": "text" + }, + { + "ID": 1618065099475731301, + "DateCreated": "2021-04-10T14:31:39.47573104Z", + "LastUpdated": "2021-04-10T14:31:39.47573104Z", + "title": "link", + "text": "https://youtu.be/dQw4w9WgXcQ" + } +] \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..c3102eb --- /dev/null +++ b/main.go @@ -0,0 +1,367 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "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 { + dbFilepath string +} + +func InitLogs() { + var logsDir string = filepath.Join(".", "logs") + + err := os.MkdirAll(logsDir, os.ModePerm) + if err != nil { + panic(err) + } + logfile, err := os.Create(filepath.Join(logsDir, "logs.log")) + if err != nil { + panic(err) + } + log.SetOutput(logfile) +} + +func homepage(w http.ResponseWriter, r *http.Request) { + helpMessage := ` +

REST api in Go's standart library

+ + ` + fmt.Fprint(w, helpMessage) +} + +func newDatabaseHandler() *randomDataHandler { + dbDirpath := filepath.Join(".", "database") + err := os.MkdirAll(dbDirpath, os.ModePerm) + if err != nil { + panic(err) + } + dbFilepath := filepath.Join(dbDirpath, "database.json") + + dbFile, err := os.OpenFile(dbFilepath, os.O_CREATE, os.ModePerm) + if err != nil { + panic(err) + } + defer dbFile.Close() + + log.Println("Successfully created new database handler") + + return &randomDataHandler{ + dbFilepath: dbFilepath, + } +} + +func (dbHandler *randomDataHandler) writeRandomData(newData RandomData) error { + dbBytes, err := dbHandler.readDatabase() + if err != nil { + log.Println("Error reading db (writeRandomData) : ", err) + } + + var db []RandomData + + err = json.Unmarshal(dbBytes, &db) + if err != nil { + log.Println("Error unmarshalling db (writeRandomData) : ", err) + } + + db = append(db, newData) + + dbFile, err := os.OpenFile(dbHandler.dbFilepath, os.O_WRONLY, 0644) + if err != nil { + log.Println("Error opening db file (writeRandomData) : ", err) + } + defer dbFile.Close() + + jsonBytes, err := json.MarshalIndent(db, "", " ") + if err != nil { + log.Println("Error marshalling db (writeRandomData) : ", err) + } + + dbFile.Write(jsonBytes) + + return nil +} + +func (dbHandler *randomDataHandler) readDatabase() ([]byte, error) { + dbBytes, err := os.ReadFile(dbHandler.dbFilepath) + if err != nil { + log.Println("Error reading db (readDatabase) : ", err) + return nil, err + } + return dbBytes, nil +} + +func (dbHandler *randomDataHandler) removeRandomData(id int64) error { + dbBytes, err := dbHandler.readDatabase() + if err != nil { + return err + } + + var db []RandomData + err = json.Unmarshal(dbBytes, &db) + if err != nil { + return err + } + + var counter int64 = 0 + for _, randomData := range db { + if id == randomData.ID { + db = append(db[:counter], db[counter+1:]...) + err = dbHandler.writeDB(db) + if err != nil { + return err + } + } + counter++ + } + return nil +} + +func (dbHandler *randomDataHandler) writeDB(db []RandomData) error { + jsonEncodedDB, err := json.MarshalIndent(db, "", " ") + if err != nil { + return err + } + + dbFile, err := os.OpenFile(dbHandler.dbFilepath, os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer dbFile.Close() + + dbFile.Write(jsonEncodedDB) + + 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 main() { + InitLogs() + + 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()) +}