1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-07-15 23:54:29 +02:00

Refactor database to store Block fields

This commit is contained in:
Chen-I Lim
2020-10-15 14:56:15 -07:00
parent 4f7272ebd1
commit 15c4c98cd3
2 changed files with 108 additions and 49 deletions

View File

@ -55,7 +55,7 @@ func handleGetBlocks(w http.ResponseWriter, r *http.Request) {
parentID := query.Get("parent_id") parentID := query.Get("parent_id")
blockType := query.Get("type") blockType := query.Get("type")
var blocks []string var blocks []Block
if len(blockType) > 0 && len(parentID) > 0 { if len(blockType) > 0 && len(parentID) > 0 {
blocks = store.getBlocksWithParentAndType(parentID, blockType) blocks = store.getBlocksWithParentAndType(parentID, blockType)
} else if len(blockType) > 0 { } 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)) log.Printf("GetBlocks parentID: %s, type: %s, %d result(s)", parentID, blockType, len(blocks))
response := `[` + strings.Join(blocks[:], ",") + `]` json, err := json.Marshal(blocks)
jsonResponse(w, http.StatusOK, response) 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) { 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) blockIDsToNotify = append(blockIDsToNotify, block.ParentID)
} }
jsonBytes, err := json.Marshal(block) store.insertBlock(block)
if err != nil {
errorResponse(w, http.StatusInternalServerError, `{}`)
return
}
store.insertBlock(block, string(jsonBytes))
} }
wsServer.broadcastBlockChangeToWebsocketClients(blockIDsToNotify) wsServer.broadcastBlockChangeToWebsocketClients(blockIDsToNotify)
log.Printf("POST Blocks %d block(s)", len(blocks)) 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) { func handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
@ -149,7 +149,7 @@ func handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
wsServer.broadcastBlockChangeToWebsocketClients(blockIDsToNotify) wsServer.broadcastBlockChangeToWebsocketClients(blockIDsToNotify)
log.Printf("DELETE Block %s", blockID) log.Printf("DELETE Block %s", blockID)
jsonResponse(w, http.StatusOK, "{}") jsonStringResponse(w, http.StatusOK, "{}")
} }
func handleGetSubTree(w http.ResponseWriter, r *http.Request) { func handleGetSubTree(w http.ResponseWriter, r *http.Request) {
@ -159,16 +159,28 @@ func handleGetSubTree(w http.ResponseWriter, r *http.Request) {
blocks := store.getSubTree(blockID) blocks := store.getSubTree(blockID)
log.Printf("GetSubTree blockID: %s, %d result(s)", blockID, len(blocks)) log.Printf("GetSubTree blockID: %s, %d result(s)", blockID, len(blocks))
response := `[` + strings.Join(blocks[:], ",") + `]` json, err := json.Marshal(blocks)
jsonResponse(w, http.StatusOK, response) 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) { func handleExport(w http.ResponseWriter, r *http.Request) {
blocks := store.getAllBlocks() blocks := store.getAllBlocks()
log.Printf("EXPORT Blocks, %d result(s)", len(blocks)) log.Printf("EXPORT Blocks, %d result(s)", len(blocks))
response := `[` + strings.Join(blocks[:], ",") + `]` json, err := json.Marshal(blocks)
jsonResponse(w, http.StatusOK, response) 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) { func handleImport(w http.ResponseWriter, r *http.Request) {
@ -195,17 +207,11 @@ func handleImport(w http.ResponseWriter, r *http.Request) {
} }
for _, block := range blocks { for _, block := range blocks {
jsonBytes, err := json.Marshal(block) store.insertBlock(block)
if err != nil {
errorResponse(w, http.StatusInternalServerError, `{}`)
return
}
store.insertBlock(block, string(jsonBytes))
} }
log.Printf("IMPORT Blocks %d block(s)", len(blocks)) log.Printf("IMPORT Blocks %d block(s)", len(blocks))
jsonResponse(w, http.StatusOK, "{}") jsonStringResponse(w, http.StatusOK, "{}")
} }
// File upload // File upload
@ -261,23 +267,29 @@ func saveFile(w http.ResponseWriter, file multipart.File, handle *multipart.File
os.MkdirAll(folderPath, os.ModePerm) os.MkdirAll(folderPath, os.ModePerm)
err = ioutil.WriteFile(filePath, data, 0666) err = ioutil.WriteFile(filePath, data, 0666)
if err != nil { if err != nil {
jsonResponse(w, http.StatusInternalServerError, `{}`) jsonStringResponse(w, http.StatusInternalServerError, `{}`)
return return
} }
url := fmt.Sprintf(`%s/files/%s`, config.ServerRoot, filename) url := fmt.Sprintf(`%s/files/%s`, config.ServerRoot, filename)
log.Printf(`saveFile, url: %s`, url) log.Printf(`saveFile, url: %s`, url)
json := fmt.Sprintf(`{ "url": "%s" }`, url) json := fmt.Sprintf(`{ "url": "%s" }`, url)
jsonResponse(w, http.StatusOK, json) jsonStringResponse(w, http.StatusOK, json)
} }
// Response helpers // 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.Header().Set("Content-Type", "application/json")
w.WriteHeader(code) w.WriteHeader(code)
fmt.Fprint(w, message) 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) { func errorResponse(w http.ResponseWriter, code int, message string) {
log.Printf("%d ERROR", code) log.Printf("%d ERROR", code)
w.WriteHeader(code) w.WriteHeader(code)

View File

@ -2,6 +2,7 @@ package main
import ( import (
"database/sql" "database/sql"
"encoding/json"
"fmt" "fmt"
"log" "log"
"time" "time"
@ -53,7 +54,6 @@ type Block struct {
Schema int64 `json:"schema"` Schema int64 `json:"schema"`
Type string `json:"type"` Type string `json:"type"`
Title string `json:"title"` Title string `json:"title"`
Order int64 `json:"order"`
Fields map[string]interface{} `json:"fields"` Fields map[string]interface{} `json:"fields"`
CreateAt int64 `json:"createAt"` CreateAt int64 `json:"createAt"`
UpdateAt int64 `json:"updateAt"` UpdateAt int64 `json:"updateAt"`
@ -69,8 +69,10 @@ func (s *SQLStore) createTablesIfNotExists() error {
id VARCHAR(36), id VARCHAR(36),
insert_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), insert_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
parent_id VARCHAR(36), parent_id VARCHAR(36),
schema BIGINT,
type TEXT, type TEXT,
json TEXT, title TEXT,
fields TEXT,
create_at BIGINT, create_at BIGINT,
update_at BIGINT, update_at BIGINT,
delete_at BIGINT, delete_at BIGINT,
@ -81,8 +83,10 @@ func (s *SQLStore) createTablesIfNotExists() error {
id VARCHAR(36), id VARCHAR(36),
insert_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), insert_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
parent_id VARCHAR(36), parent_id VARCHAR(36),
schema BIGINT,
type TEXT, type TEXT,
json TEXT, title TEXT,
fields TEXT,
create_at BIGINT, create_at BIGINT,
update_at BIGINT, update_at BIGINT,
delete_at BIGINT, delete_at BIGINT,
@ -99,7 +103,7 @@ func (s *SQLStore) createTablesIfNotExists() error {
return nil return nil
} }
func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string) []string { func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string) []Block {
query := `WITH latest AS query := `WITH latest AS
( (
SELECT * FROM SELECT * FROM
@ -112,7 +116,7 @@ func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string)
WHERE rn = 1 WHERE rn = 1
) )
SELECT COALESCE("json", '{}') SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at
FROM latest FROM latest
WHERE delete_at = 0 and parent_id = $1 and type = $2` 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) return blocksFromRows(rows)
} }
func (s *SQLStore) getBlocksWithParent(parentID string) []string { func (s *SQLStore) getBlocksWithParent(parentID string) []Block {
query := `WITH latest AS query := `WITH latest AS
( (
SELECT * FROM SELECT * FROM
@ -138,7 +142,7 @@ func (s *SQLStore) getBlocksWithParent(parentID string) []string {
WHERE rn = 1 WHERE rn = 1
) )
SELECT COALESCE("json", '{}') SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at
FROM latest FROM latest
WHERE delete_at = 0 and parent_id = $1` WHERE delete_at = 0 and parent_id = $1`
@ -151,7 +155,7 @@ func (s *SQLStore) getBlocksWithParent(parentID string) []string {
return blocksFromRows(rows) return blocksFromRows(rows)
} }
func (s *SQLStore) getBlocksWithType(blockType string) []string { func (s *SQLStore) getBlocksWithType(blockType string) []Block {
query := `WITH latest AS query := `WITH latest AS
( (
SELECT * FROM SELECT * FROM
@ -164,7 +168,7 @@ func (s *SQLStore) getBlocksWithType(blockType string) []string {
WHERE rn = 1 WHERE rn = 1
) )
SELECT COALESCE("json", '{}') SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at
FROM latest FROM latest
WHERE delete_at = 0 and type = $1` WHERE delete_at = 0 and type = $1`
@ -177,7 +181,7 @@ func (s *SQLStore) getBlocksWithType(blockType string) []string {
return blocksFromRows(rows) return blocksFromRows(rows)
} }
func (s *SQLStore) getSubTree(blockID string) []string { func (s *SQLStore) getSubTree(blockID string) []Block {
query := `WITH latest AS query := `WITH latest AS
( (
SELECT * FROM SELECT * FROM
@ -190,7 +194,7 @@ func (s *SQLStore) getSubTree(blockID string) []string {
WHERE rn = 1 WHERE rn = 1
) )
SELECT COALESCE("json", '{}') SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at
FROM latest FROM latest
WHERE delete_at = 0 WHERE delete_at = 0
AND (id = $1 AND (id = $1
@ -205,7 +209,7 @@ func (s *SQLStore) getSubTree(blockID string) []string {
return blocksFromRows(rows) return blocksFromRows(rows)
} }
func (s *SQLStore) getAllBlocks() []string { func (s *SQLStore) getAllBlocks() []Block {
query := `WITH latest AS query := `WITH latest AS
( (
SELECT * FROM SELECT * FROM
@ -218,7 +222,7 @@ func (s *SQLStore) getAllBlocks() []string {
WHERE rn = 1 WHERE rn = 1
) )
SELECT COALESCE("json", '{}') SELECT id, parent_id, schema, type, title, COALESCE("fields", '{}'), create_at, update_at, delete_at
FROM latest FROM latest
WHERE delete_at = 0` WHERE delete_at = 0`
@ -231,21 +235,38 @@ func (s *SQLStore) getAllBlocks() []string {
return blocksFromRows(rows) return blocksFromRows(rows)
} }
func blocksFromRows(rows *sql.Rows) []string { func blocksFromRows(rows *sql.Rows) []Block {
defer rows.Close() defer rows.Close()
var results []string var results []Block
for rows.Next() { for rows.Next() {
var json string var block Block
err := rows.Scan(&json) 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 { if err != nil {
// handle this error // handle this error
log.Printf(`blocksFromRows ERROR: %v`, err) log.Printf(`ERROR blocksFromRows: %v`, err)
panic(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 return results
@ -281,9 +302,35 @@ func (s *SQLStore) getParentID(blockID string) string {
return parentID return parentID
} }
func (s *SQLStore) insertBlock(block Block, json string) { func (s *SQLStore) insertBlock(block Block) {
statement := `INSERT INTO blocks(id, parent_id, type, json, create_at, update_at, delete_at) VALUES($1, $2, $3, $4, $5, $6, $7)` fieldsJSON, err := json.Marshal(block.Fields)
_, err := s.db.Exec(statement, block.ID, block.ParentID, block.Type, json, block.CreateAt, block.UpdateAt, block.DeleteAt) 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 { if err != nil {
panic(err) panic(err)
} }