From 15c4c98cd3143cebbbcc0971ea251fd1c17666dd Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Thu, 15 Oct 2020 14:56:15 -0700 Subject: [PATCH] Refactor database to store Block fields --- server/main/main.go | 66 ++++++++++++++++----------- server/main/octoDatabase.go | 91 ++++++++++++++++++++++++++++--------- 2 files changed, 108 insertions(+), 49 deletions(-) diff --git a/server/main/main.go b/server/main/main.go index 21e082894..045ecfa22 100644 --- a/server/main/main.go +++ b/server/main/main.go @@ -55,7 +55,7 @@ func handleGetBlocks(w http.ResponseWriter, r *http.Request) { parentID := query.Get("parent_id") blockType := query.Get("type") - var blocks []string + var blocks []Block if len(blockType) > 0 && len(parentID) > 0 { blocks = store.getBlocksWithParentAndType(parentID, blockType) } else if len(blockType) > 0 { @@ -65,8 +65,14 @@ func handleGetBlocks(w http.ResponseWriter, r *http.Request) { } log.Printf("GetBlocks parentID: %s, type: %s, %d result(s)", parentID, blockType, len(blocks)) - response := `[` + strings.Join(blocks[:], ",") + `]` - jsonResponse(w, http.StatusOK, response) + json, err := json.Marshal(blocks) + if err != nil { + log.Printf(`ERROR json.Marshal: %v`, r) + errorResponse(w, http.StatusInternalServerError, `{}`) + return + } + + jsonBytesResponse(w, http.StatusOK, json) } func handlePostBlocks(w http.ResponseWriter, r *http.Request) { @@ -117,19 +123,13 @@ func handlePostBlocks(w http.ResponseWriter, r *http.Request) { blockIDsToNotify = append(blockIDsToNotify, block.ParentID) } - jsonBytes, err := json.Marshal(block) - if err != nil { - errorResponse(w, http.StatusInternalServerError, `{}`) - return - } - - store.insertBlock(block, string(jsonBytes)) + store.insertBlock(block) } wsServer.broadcastBlockChangeToWebsocketClients(blockIDsToNotify) log.Printf("POST Blocks %d block(s)", len(blocks)) - jsonResponse(w, http.StatusOK, "{}") + jsonStringResponse(w, http.StatusOK, "{}") } func handleDeleteBlock(w http.ResponseWriter, r *http.Request) { @@ -149,7 +149,7 @@ func handleDeleteBlock(w http.ResponseWriter, r *http.Request) { wsServer.broadcastBlockChangeToWebsocketClients(blockIDsToNotify) log.Printf("DELETE Block %s", blockID) - jsonResponse(w, http.StatusOK, "{}") + jsonStringResponse(w, http.StatusOK, "{}") } func handleGetSubTree(w http.ResponseWriter, r *http.Request) { @@ -159,16 +159,28 @@ func handleGetSubTree(w http.ResponseWriter, r *http.Request) { blocks := store.getSubTree(blockID) log.Printf("GetSubTree blockID: %s, %d result(s)", blockID, len(blocks)) - response := `[` + strings.Join(blocks[:], ",") + `]` - jsonResponse(w, http.StatusOK, response) + json, err := json.Marshal(blocks) + if err != nil { + log.Printf(`ERROR json.Marshal: %v`, r) + errorResponse(w, http.StatusInternalServerError, `{}`) + return + } + + jsonBytesResponse(w, http.StatusOK, json) } func handleExport(w http.ResponseWriter, r *http.Request) { blocks := store.getAllBlocks() log.Printf("EXPORT Blocks, %d result(s)", len(blocks)) - response := `[` + strings.Join(blocks[:], ",") + `]` - jsonResponse(w, http.StatusOK, response) + json, err := json.Marshal(blocks) + if err != nil { + log.Printf(`ERROR json.Marshal: %v`, r) + errorResponse(w, http.StatusInternalServerError, `{}`) + return + } + + jsonBytesResponse(w, http.StatusOK, json) } func handleImport(w http.ResponseWriter, r *http.Request) { @@ -195,17 +207,11 @@ func handleImport(w http.ResponseWriter, r *http.Request) { } for _, block := range blocks { - jsonBytes, err := json.Marshal(block) - if err != nil { - errorResponse(w, http.StatusInternalServerError, `{}`) - return - } - - store.insertBlock(block, string(jsonBytes)) + store.insertBlock(block) } log.Printf("IMPORT Blocks %d block(s)", len(blocks)) - jsonResponse(w, http.StatusOK, "{}") + jsonStringResponse(w, http.StatusOK, "{}") } // File upload @@ -261,23 +267,29 @@ func saveFile(w http.ResponseWriter, file multipart.File, handle *multipart.File os.MkdirAll(folderPath, os.ModePerm) err = ioutil.WriteFile(filePath, data, 0666) if err != nil { - jsonResponse(w, http.StatusInternalServerError, `{}`) + jsonStringResponse(w, http.StatusInternalServerError, `{}`) return } url := fmt.Sprintf(`%s/files/%s`, config.ServerRoot, filename) log.Printf(`saveFile, url: %s`, url) json := fmt.Sprintf(`{ "url": "%s" }`, url) - jsonResponse(w, http.StatusOK, json) + jsonStringResponse(w, http.StatusOK, json) } // Response helpers -func jsonResponse(w http.ResponseWriter, code int, message string) { +func jsonStringResponse(w http.ResponseWriter, code int, message string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) fmt.Fprint(w, message) } +func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + w.Write(json) +} + func errorResponse(w http.ResponseWriter, code int, message string) { log.Printf("%d ERROR", code) w.WriteHeader(code) diff --git a/server/main/octoDatabase.go b/server/main/octoDatabase.go index f27c25efa..7df861731 100644 --- a/server/main/octoDatabase.go +++ b/server/main/octoDatabase.go @@ -2,6 +2,7 @@ package main import ( "database/sql" + "encoding/json" "fmt" "log" "time" @@ -53,7 +54,6 @@ type Block struct { Schema int64 `json:"schema"` Type string `json:"type"` Title string `json:"title"` - Order int64 `json:"order"` Fields map[string]interface{} `json:"fields"` CreateAt int64 `json:"createAt"` UpdateAt int64 `json:"updateAt"` @@ -69,8 +69,10 @@ func (s *SQLStore) createTablesIfNotExists() error { id VARCHAR(36), insert_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), parent_id VARCHAR(36), + schema BIGINT, type TEXT, - json TEXT, + title TEXT, + fields TEXT, create_at BIGINT, update_at BIGINT, delete_at BIGINT, @@ -81,8 +83,10 @@ func (s *SQLStore) createTablesIfNotExists() error { id VARCHAR(36), insert_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), parent_id VARCHAR(36), + schema BIGINT, type TEXT, - json TEXT, + title TEXT, + fields TEXT, create_at BIGINT, update_at BIGINT, delete_at BIGINT, @@ -99,7 +103,7 @@ func (s *SQLStore) createTablesIfNotExists() error { return nil } -func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string) []string { +func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string) []Block { query := `WITH latest AS ( SELECT * FROM @@ -112,7 +116,7 @@ func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string) WHERE rn = 1 ) - SELECT COALESCE("json", '{}') + SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at FROM latest WHERE delete_at = 0 and parent_id = $1 and type = $2` @@ -125,7 +129,7 @@ func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string) return blocksFromRows(rows) } -func (s *SQLStore) getBlocksWithParent(parentID string) []string { +func (s *SQLStore) getBlocksWithParent(parentID string) []Block { query := `WITH latest AS ( SELECT * FROM @@ -138,7 +142,7 @@ func (s *SQLStore) getBlocksWithParent(parentID string) []string { WHERE rn = 1 ) - SELECT COALESCE("json", '{}') + SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at FROM latest WHERE delete_at = 0 and parent_id = $1` @@ -151,7 +155,7 @@ func (s *SQLStore) getBlocksWithParent(parentID string) []string { return blocksFromRows(rows) } -func (s *SQLStore) getBlocksWithType(blockType string) []string { +func (s *SQLStore) getBlocksWithType(blockType string) []Block { query := `WITH latest AS ( SELECT * FROM @@ -164,7 +168,7 @@ func (s *SQLStore) getBlocksWithType(blockType string) []string { WHERE rn = 1 ) - SELECT COALESCE("json", '{}') + SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at FROM latest WHERE delete_at = 0 and type = $1` @@ -177,7 +181,7 @@ func (s *SQLStore) getBlocksWithType(blockType string) []string { return blocksFromRows(rows) } -func (s *SQLStore) getSubTree(blockID string) []string { +func (s *SQLStore) getSubTree(blockID string) []Block { query := `WITH latest AS ( SELECT * FROM @@ -190,7 +194,7 @@ func (s *SQLStore) getSubTree(blockID string) []string { WHERE rn = 1 ) - SELECT COALESCE("json", '{}') + SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at FROM latest WHERE delete_at = 0 AND (id = $1 @@ -205,7 +209,7 @@ func (s *SQLStore) getSubTree(blockID string) []string { return blocksFromRows(rows) } -func (s *SQLStore) getAllBlocks() []string { +func (s *SQLStore) getAllBlocks() []Block { query := `WITH latest AS ( SELECT * FROM @@ -218,7 +222,7 @@ func (s *SQLStore) getAllBlocks() []string { WHERE rn = 1 ) - SELECT COALESCE("json", '{}') + SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at FROM latest WHERE delete_at = 0` @@ -231,21 +235,38 @@ func (s *SQLStore) getAllBlocks() []string { return blocksFromRows(rows) } -func blocksFromRows(rows *sql.Rows) []string { +func blocksFromRows(rows *sql.Rows) []Block { defer rows.Close() - var results []string + var results []Block for rows.Next() { - var json string - err := rows.Scan(&json) + var block Block + var fieldsJSON string + err := rows.Scan( + &block.ID, + &block.ParentID, + &block.Schema, + &block.Type, + &block.Title, + &fieldsJSON, + &block.CreateAt, + &block.UpdateAt, + &block.DeleteAt) if err != nil { // handle this error - log.Printf(`blocksFromRows ERROR: %v`, err) + log.Printf(`ERROR blocksFromRows: %v`, err) panic(err) } - results = append(results, json) + err = json.Unmarshal([]byte(fieldsJSON), &block.Fields) + if err != nil { + // handle this error + log.Printf(`ERROR blocksFromRows fields: %v`, err) + panic(err) + } + + results = append(results, block) } return results @@ -281,9 +302,35 @@ func (s *SQLStore) getParentID(blockID string) string { return parentID } -func (s *SQLStore) insertBlock(block Block, json string) { - statement := `INSERT INTO blocks(id, parent_id, type, json, create_at, update_at, delete_at) VALUES($1, $2, $3, $4, $5, $6, $7)` - _, err := s.db.Exec(statement, block.ID, block.ParentID, block.Type, json, block.CreateAt, block.UpdateAt, block.DeleteAt) +func (s *SQLStore) insertBlock(block Block) { + fieldsJSON, err := json.Marshal(block.Fields) + if err != nil { + panic(err) + } + + statement := `INSERT INTO blocks( + id, + parent_id, + schema, + type, + title, + fields, + create_at, + update_at, + delete_at + ) + VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9)` + _, err = s.db.Exec( + statement, + block.ID, + block.ParentID, + block.Schema, + block.Type, + block.Title, + fieldsJSON, + block.CreateAt, + block.UpdateAt, + block.DeleteAt) if err != nil { panic(err) }