From 516dfbbd459d0dcde963829368301f47d1cea377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Fri, 9 Oct 2020 09:53:15 +0200 Subject: [PATCH 1/6] Improvements around marshalling and status codes --- server/main/main.go | 54 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/server/main/main.go b/server/main/main.go index 82f4a5786..04af07421 100644 --- a/server/main/main.go +++ b/server/main/main.go @@ -70,13 +70,13 @@ func handleGetBlocks(w http.ResponseWriter, r *http.Request) { } log.Printf("GetBlocks parentID: %s, %d result(s)", parentID, len(blocks)) response := `[` + strings.Join(blocks[:], ",") + `]` - jsonResponse(w, 200, response) + jsonResponse(w, http.StatusOK, response) } func handlePostBlocks(w http.ResponseWriter, r *http.Request) { requestBody, err := ioutil.ReadAll(r.Body) if err != nil { - errorResponse(w, 500, `{}`) + errorResponse(w, http.StatusInternalServerError, `{}`) return } @@ -84,41 +84,33 @@ func handlePostBlocks(w http.ResponseWriter, r *http.Request) { defer func() { if r := recover(); r != nil { log.Printf(`ERROR: %v`, r) - errorResponse(w, 500, `{}`) + errorResponse(w, http.StatusInternalServerError, `{}`) return } }() - var blockMaps []map[string]interface{} - err = json.Unmarshal([]byte(requestBody), &blockMaps) + var blocks []Block + err = json.Unmarshal([]byte(requestBody), &blocks) if err != nil { - errorResponse(w, 500, ``) + errorResponse(w, http.StatusInternalServerError, ``) return } var blockIDsToNotify = []string{} uniqueBlockIDs := make(map[string]bool) - for _, blockMap := range blockMaps { - jsonBytes, err := json.Marshal(blockMap) - if err != nil { - errorResponse(w, 500, `{}`) - return - } - - block := blockFromMap(blockMap) - + for _, block := range blocks { // Error checking if len(block.Type) < 1 { - errorResponse(w, 500, fmt.Sprintf(`{"description": "missing type", "id": "%s"}`, block.ID)) + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf(`{"description": "missing type", "id": "%s"}`, block.ID)) return } if block.CreateAt < 1 { - errorResponse(w, 500, fmt.Sprintf(`{"description": "invalid createAt", "id": "%s"}`, block.ID)) + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf(`{"description": "invalid createAt", "id": "%s"}`, block.ID)) return } if block.UpdateAt < 1 { - errorResponse(w, 500, fmt.Sprintf(`{"description": "invalid updateAt", "id": "%s"}`, block.ID)) + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf(`{"description": "invalid updateAt", "id": "%s"}`, block.ID)) return } @@ -129,13 +121,19 @@ 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 + } + insertBlock(block, string(jsonBytes)) } broadcastBlockChangeToWebsocketClients(blockIDsToNotify) - log.Printf("POST Blocks %d block(s)", len(blockMaps)) - jsonResponse(w, 200, "{}") + log.Printf("POST Blocks %d block(s)", len(blocks)) + jsonResponse(w, http.StatusOK, "{}") } func handleDeleteBlock(w http.ResponseWriter, r *http.Request) { @@ -155,7 +153,7 @@ func handleDeleteBlock(w http.ResponseWriter, r *http.Request) { broadcastBlockChangeToWebsocketClients(blockIDsToNotify) log.Printf("DELETE Block %s", blockID) - jsonResponse(w, 200, "{}") + jsonResponse(w, http.StatusOK, "{}") } func handleGetSubTree(w http.ResponseWriter, r *http.Request) { @@ -166,7 +164,7 @@ func handleGetSubTree(w http.ResponseWriter, r *http.Request) { log.Printf("GetSubTree blockID: %s, %d result(s)", blockID, len(blocks)) response := `[` + strings.Join(blocks[:], ",") + `]` - jsonResponse(w, 200, response) + jsonResponse(w, http.StatusOK, response) } func handleExport(w http.ResponseWriter, r *http.Request) { @@ -174,13 +172,13 @@ func handleExport(w http.ResponseWriter, r *http.Request) { log.Printf("EXPORT Blocks, %d result(s)", len(blocks)) response := `[` + strings.Join(blocks[:], ",") + `]` - jsonResponse(w, 200, response) + jsonResponse(w, http.StatusOK, response) } func handleImport(w http.ResponseWriter, r *http.Request) { requestBody, err := ioutil.ReadAll(r.Body) if err != nil { - errorResponse(w, 500, `{}`) + errorResponse(w, http.StatusInternalServerError, `{}`) return } @@ -188,7 +186,7 @@ func handleImport(w http.ResponseWriter, r *http.Request) { defer func() { if r := recover(); r != nil { log.Printf(`ERROR: %v`, r) - errorResponse(w, 500, `{}`) + errorResponse(w, http.StatusInternalServerError, `{}`) return } }() @@ -196,14 +194,14 @@ func handleImport(w http.ResponseWriter, r *http.Request) { var blockMaps []map[string]interface{} err = json.Unmarshal([]byte(requestBody), &blockMaps) if err != nil { - errorResponse(w, 500, ``) + errorResponse(w, http.StatusInternalServerError, ``) return } for _, blockMap := range blockMaps { jsonBytes, err := json.Marshal(blockMap) if err != nil { - errorResponse(w, 500, `{}`) + errorResponse(w, http.StatusInternalServerError, `{}`) return } @@ -212,7 +210,7 @@ func handleImport(w http.ResponseWriter, r *http.Request) { } log.Printf("IMPORT Blocks %d block(s)", len(blockMaps)) - jsonResponse(w, 200, "{}") + jsonResponse(w, http.StatusOK, "{}") } // File upload From f5db60bf9cfe3bae3ba474b91831968d1c1e8cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Fri, 9 Oct 2020 10:08:38 +0200 Subject: [PATCH 2/6] Preparatory work to move the store into a different service --- server/main/main.go | 35 +++++++++++--------- server/main/octoDatabase.go | 64 ++++++++++++++++++++++--------------- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/server/main/main.go b/server/main/main.go index 04af07421..b4b0b05b7 100644 --- a/server/main/main.go +++ b/server/main/main.go @@ -21,6 +21,7 @@ import ( ) var config Configuration +var store *SQLStore // WebsocketMsg is send on block changes type WebsocketMsg struct { @@ -64,9 +65,9 @@ func handleGetBlocks(w http.ResponseWriter, r *http.Request) { var blocks []string if len(blockType) > 0 { - blocks = getBlocksWithParentAndType(parentID, blockType) + blocks = store.getBlocksWithParentAndType(parentID, blockType) } else { - blocks = getBlocksWithParent(parentID) + blocks = store.getBlocksWithParent(parentID) } log.Printf("GetBlocks parentID: %s, %d result(s)", parentID, len(blocks)) response := `[` + strings.Join(blocks[:], ",") + `]` @@ -127,7 +128,7 @@ func handlePostBlocks(w http.ResponseWriter, r *http.Request) { return } - insertBlock(block, string(jsonBytes)) + store.insertBlock(block, string(jsonBytes)) } broadcastBlockChangeToWebsocketClients(blockIDsToNotify) @@ -142,13 +143,13 @@ func handleDeleteBlock(w http.ResponseWriter, r *http.Request) { var blockIDsToNotify = []string{blockID} - parentID := getParentID(blockID) + parentID := store.getParentID(blockID) if len(parentID) > 0 { blockIDsToNotify = append(blockIDsToNotify, parentID) } - deleteBlock(blockID) + store.deleteBlock(blockID) broadcastBlockChangeToWebsocketClients(blockIDsToNotify) @@ -160,7 +161,7 @@ func handleGetSubTree(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) blockID := vars["blockID"] - blocks := getSubTree(blockID) + blocks := store.getSubTree(blockID) log.Printf("GetSubTree blockID: %s, %d result(s)", blockID, len(blocks)) response := `[` + strings.Join(blocks[:], ",") + `]` @@ -168,7 +169,7 @@ func handleGetSubTree(w http.ResponseWriter, r *http.Request) { } func handleExport(w http.ResponseWriter, r *http.Request) { - blocks := getAllBlocks() + blocks := store.getAllBlocks() log.Printf("EXPORT Blocks, %d result(s)", len(blocks)) response := `[` + strings.Join(blocks[:], ",") + `]` @@ -191,25 +192,24 @@ func handleImport(w http.ResponseWriter, r *http.Request) { } }() - var blockMaps []map[string]interface{} - err = json.Unmarshal([]byte(requestBody), &blockMaps) + var blocks []Block + err = json.Unmarshal([]byte(requestBody), &blocks) if err != nil { errorResponse(w, http.StatusInternalServerError, ``) return } - for _, blockMap := range blockMaps { - jsonBytes, err := json.Marshal(blockMap) + for _, block := range blocks { + jsonBytes, err := json.Marshal(block) if err != nil { errorResponse(w, http.StatusInternalServerError, `{}`) return } - block := blockFromMap(blockMap) - insertBlock(block, string(jsonBytes)) + store.insertBlock(block, string(jsonBytes)) } - log.Printf("IMPORT Blocks %d block(s)", len(blockMaps)) + log.Printf("IMPORT Blocks %d block(s)", len(blocks)) jsonResponse(w, http.StatusOK, "{}") } @@ -434,7 +434,12 @@ func main() { http.Handle("/", r) - connectDatabase(config.DBType, config.DBConfigString) + var err error + store, err = NewSQLStore(config.DBType, config.DBConfigString) + if err != nil { + log.Fatal("Unable to start the database", err) + panic(err) + } // Ctrl+C handling handler := make(chan os.Signal, 1) diff --git a/server/main/octoDatabase.go b/server/main/octoDatabase.go index 4a4568fa1..fbed7a58b 100644 --- a/server/main/octoDatabase.go +++ b/server/main/octoDatabase.go @@ -10,25 +10,38 @@ import ( _ "github.com/mattn/go-sqlite3" ) -var db *sql.DB +type SQLStore struct { + db *sql.DB + dbType string +} -func connectDatabase(dbType string, connectionString string) { +func NewSQLStore(dbType, connectionString string) (*SQLStore, error) { log.Println("connectDatabase") var err error - db, err = sql.Open(dbType, connectionString) + db, err := sql.Open(dbType, connectionString) if err != nil { log.Fatal("connectDatabase: ", err) - panic(err) + return nil, err } err = db.Ping() if err != nil { log.Println(`Database Ping failed`) - panic(err) + return nil, err } - createTablesIfNotExists(dbType) + store := &SQLStore{ + db: db, + dbType: dbType, + } + + err = store.createTablesIfNotExists() + if err != nil { + log.Println(`Table creation failed`) + return nil, err + } + return store, nil } // Block is the basic data unit @@ -41,11 +54,11 @@ type Block struct { DeleteAt int64 `json:"deleteAt"` } -func createTablesIfNotExists(dbType string) { +func (s *SQLStore) createTablesIfNotExists() error { // TODO: Add update_by with the user's ID // TODO: Consolidate insert_at and update_at, decide if the server of DB should set it var query string - if dbType == "sqlite3" { + if s.dbType == "sqlite3" { query = `CREATE TABLE IF NOT EXISTS blocks ( id VARCHAR(36), insert_at DATETIME NOT NULL DEFAULT current_timestamp, @@ -71,12 +84,13 @@ func createTablesIfNotExists(dbType string) { );` } - _, err := db.Exec(query) + _, err := s.db.Exec(query) if err != nil { log.Fatal("createTablesIfNotExists: ", err) - panic(err) + return err } - log.Printf("createTablesIfNotExists(%s)", dbType) + log.Printf("createTablesIfNotExists(%s)", s.dbType) + return nil } func blockFromMap(m map[string]interface{}) Block { @@ -103,7 +117,7 @@ func blockFromMap(m map[string]interface{}) Block { return b } -func getBlocksWithParentAndType(parentID string, blockType string) []string { +func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string) []string { query := `WITH latest AS ( SELECT * FROM @@ -120,7 +134,7 @@ func getBlocksWithParentAndType(parentID string, blockType string) []string { FROM latest WHERE delete_at = 0 and parent_id = $1 and type = $2` - rows, err := db.Query(query, parentID, blockType) + rows, err := s.db.Query(query, parentID, blockType) if err != nil { log.Printf(`getBlocksWithParentAndType ERROR: %v`, err) panic(err) @@ -129,7 +143,7 @@ func getBlocksWithParentAndType(parentID string, blockType string) []string { return blocksFromRows(rows) } -func getBlocksWithParent(parentID string) []string { +func (s *SQLStore) getBlocksWithParent(parentID string) []string { query := `WITH latest AS ( SELECT * FROM @@ -146,7 +160,7 @@ func getBlocksWithParent(parentID string) []string { FROM latest WHERE delete_at = 0 and parent_id = $1` - rows, err := db.Query(query, parentID) + rows, err := s.db.Query(query, parentID) if err != nil { log.Printf(`getBlocksWithParent ERROR: %v`, err) panic(err) @@ -155,7 +169,7 @@ func getBlocksWithParent(parentID string) []string { return blocksFromRows(rows) } -func getSubTree(blockID string) []string { +func (s *SQLStore) getSubTree(blockID string) []string { query := `WITH latest AS ( SELECT * FROM @@ -174,7 +188,7 @@ func getSubTree(blockID string) []string { AND (id = $1 OR parent_id = $1)` - rows, err := db.Query(query, blockID) + rows, err := s.db.Query(query, blockID) if err != nil { log.Printf(`getSubTree ERROR: %v`, err) panic(err) @@ -183,7 +197,7 @@ func getSubTree(blockID string) []string { return blocksFromRows(rows) } -func getAllBlocks() []string { +func (s *SQLStore) getAllBlocks() []string { query := `WITH latest AS ( SELECT * FROM @@ -200,7 +214,7 @@ func getAllBlocks() []string { FROM latest WHERE delete_at = 0` - rows, err := db.Query(query) + rows, err := s.db.Query(query) if err != nil { log.Printf(`getAllBlocks ERROR: %v`, err) panic(err) @@ -229,7 +243,7 @@ func blocksFromRows(rows *sql.Rows) []string { return results } -func getParentID(blockID string) string { +func (s *SQLStore) getParentID(blockID string) string { statement := `WITH latest AS ( @@ -248,7 +262,7 @@ func getParentID(blockID string) string { WHERE delete_at = 0 AND id = $1` - row := db.QueryRow(statement, blockID) + row := s.db.QueryRow(statement, blockID) var parentID string err := row.Scan(&parentID) @@ -259,19 +273,19 @@ func getParentID(blockID string) string { return parentID } -func insertBlock(block Block, json string) { +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 := db.Exec(statement, block.ID, block.ParentID, block.Type, json, block.CreateAt, block.UpdateAt, block.DeleteAt) + _, err := s.db.Exec(statement, block.ID, block.ParentID, block.Type, json, block.CreateAt, block.UpdateAt, block.DeleteAt) if err != nil { panic(err) } } -func deleteBlock(blockID string) { +func (s *SQLStore) deleteBlock(blockID string) { now := time.Now().Unix() json := fmt.Sprintf(`{"id":"%s","updateAt":%d,"deleteAt":%d}`, blockID, now, now) statement := `INSERT INTO blocks(id, json, update_at, delete_at) VALUES($1, $2, $3, $4)` - _, err := db.Exec(statement, blockID, json, now, now) + _, err := s.db.Exec(statement, blockID, json, now, now) if err != nil { panic(err) } From 6e2d9dd02abeffb1d2050c3bf64d4db14b9b8a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Fri, 9 Oct 2020 10:09:49 +0200 Subject: [PATCH 3/6] Remove unnecesary blockFromMap --- server/main/octoDatabase.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/server/main/octoDatabase.go b/server/main/octoDatabase.go index fbed7a58b..377218ac3 100644 --- a/server/main/octoDatabase.go +++ b/server/main/octoDatabase.go @@ -93,30 +93,6 @@ func (s *SQLStore) createTablesIfNotExists() error { return nil } -func blockFromMap(m map[string]interface{}) Block { - var b Block - b.ID = m["id"].(string) - // Parent ID can be nil (for now) - if m["parentId"] != nil { - b.ParentID = m["parentId"].(string) - } - // Allow nil type for imports - if m["type"] != nil { - b.Type = m["type"].(string) - } - if m["createAt"] != nil { - b.CreateAt = int64(m["createAt"].(float64)) - } - if m["updateAt"] != nil { - b.UpdateAt = int64(m["updateAt"].(float64)) - } - if m["deleteAt"] != nil { - b.DeleteAt = int64(m["deleteAt"].(float64)) - } - - return b -} - func (s *SQLStore) getBlocksWithParentAndType(parentID string, blockType string) []string { query := `WITH latest AS ( From 82b5a52abe621c0bb8d1682c76e6b2f3299fc92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 13 Oct 2020 20:16:44 +0200 Subject: [PATCH 4/6] Adding missed fields --- server/main/octoDatabase.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/server/main/octoDatabase.go b/server/main/octoDatabase.go index 377218ac3..809c605d0 100644 --- a/server/main/octoDatabase.go +++ b/server/main/octoDatabase.go @@ -46,12 +46,14 @@ func NewSQLStore(dbType, connectionString string) (*SQLStore, error) { // Block is the basic data unit type Block struct { - ID string `json:"id"` - ParentID string `json:"parentId"` - Type string `json:"type"` - CreateAt int64 `json:"createAt"` - UpdateAt int64 `json:"updateAt"` - DeleteAt int64 `json:"deleteAt"` + ID string `json:"id"` + ParentID string `json:"parentId"` + Type string `json:"type"` + Title string `json:"title"` + Properties map[string]interface{} `json:"properties"` + CreateAt int64 `json:"createAt"` + UpdateAt int64 `json:"updateAt"` + DeleteAt int64 `json:"deleteAt"` } func (s *SQLStore) createTablesIfNotExists() error { @@ -64,7 +66,8 @@ func (s *SQLStore) createTablesIfNotExists() error { insert_at DATETIME NOT NULL DEFAULT current_timestamp, parent_id VARCHAR(36), type TEXT, - json TEXT, + title TEXT, + properties TEXT, create_at BIGINT, update_at BIGINT, delete_at BIGINT, @@ -76,7 +79,8 @@ func (s *SQLStore) createTablesIfNotExists() error { insert_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), parent_id VARCHAR(36), type TEXT, - json TEXT, + title TEXT, + properties TEXT, create_at BIGINT, update_at BIGINT, delete_at BIGINT, From 94cc769d4b116173da51f4ebe1299072eeaba741 Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Wed, 14 Oct 2020 12:51:37 -0700 Subject: [PATCH 5/6] Merge from main --- server/main/main.go | 3 +-- server/main/octoDatabase.go | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/server/main/main.go b/server/main/main.go index 1a511b9f4..21e082894 100644 --- a/server/main/main.go +++ b/server/main/main.go @@ -19,7 +19,7 @@ import ( "github.com/gorilla/mux" ) -var config Configuration +var config *Configuration var wsServer *WSServer var store *SQLStore @@ -376,7 +376,6 @@ func main() { http.Handle("/", r) - var err error store, err = NewSQLStore(config.DBType, config.DBConfigString) if err != nil { log.Fatal("Unable to start the database", err) diff --git a/server/main/octoDatabase.go b/server/main/octoDatabase.go index c4dd3fb63..4d8bf5e76 100644 --- a/server/main/octoDatabase.go +++ b/server/main/octoDatabase.go @@ -10,11 +10,13 @@ import ( _ "github.com/mattn/go-sqlite3" ) +// SQLStore is a SQL database type SQLStore struct { db *sql.DB dbType string } +// NewSQLStore creates a new SQLStore func NewSQLStore(dbType, connectionString string) (*SQLStore, error) { log.Println("connectDatabase") var err error @@ -149,7 +151,7 @@ func (s *SQLStore) getBlocksWithParent(parentID string) []string { return blocksFromRows(rows) } -func (s *SQLStore) getBlocksWithType(blockID string) []string { +func (s *SQLStore) getBlocksWithType(blockType string) []string { query := `WITH latest AS ( SELECT * FROM @@ -166,7 +168,7 @@ func (s *SQLStore) getBlocksWithType(blockID string) []string { FROM latest WHERE delete_at = 0 and type = $1` - rows, err := db.Query(query, blockType) + rows, err := s.db.Query(query, blockType) if err != nil { log.Printf(`getBlocksWithParentAndType ERROR: %v`, err) panic(err) From 96b9d6a5d230772de1e3a0e66cb8d9e4bc4732a1 Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Wed, 14 Oct 2020 13:44:35 -0700 Subject: [PATCH 6/6] Add generic fields to IBlock --- server/main/octoDatabase.go | 8 ++++---- src/client/block.ts | 25 +++++++++++++++++------- src/client/board.ts | 23 +++++++++++++++++----- src/client/boardView.ts | 39 +++++++++++++++++++++++++++---------- src/client/octoTypes.ts | 2 ++ 5 files changed, 71 insertions(+), 26 deletions(-) diff --git a/server/main/octoDatabase.go b/server/main/octoDatabase.go index 4d8bf5e76..dfae0d2f9 100644 --- a/server/main/octoDatabase.go +++ b/server/main/octoDatabase.go @@ -50,9 +50,11 @@ func NewSQLStore(dbType, connectionString string) (*SQLStore, error) { type Block struct { ID string `json:"id"` ParentID string `json:"parentId"` + Schema int64 `json:"schema"` Type string `json:"type"` Title string `json:"title"` Properties map[string]interface{} `json:"properties"` + Fields map[string]interface{} `json:"fields"` CreateAt int64 `json:"createAt"` UpdateAt int64 `json:"updateAt"` DeleteAt int64 `json:"deleteAt"` @@ -68,8 +70,7 @@ func (s *SQLStore) createTablesIfNotExists() error { insert_at DATETIME NOT NULL DEFAULT current_timestamp, parent_id VARCHAR(36), type TEXT, - title TEXT, - properties TEXT, + json TEXT, create_at BIGINT, update_at BIGINT, delete_at BIGINT, @@ -81,8 +82,7 @@ func (s *SQLStore) createTablesIfNotExists() error { insert_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), parent_id VARCHAR(36), type TEXT, - title TEXT, - properties TEXT, + json TEXT, create_at BIGINT, update_at BIGINT, delete_at BIGINT, diff --git a/src/client/block.ts b/src/client/block.ts index 10258cfa6..08aba81e3 100644 --- a/src/client/block.ts +++ b/src/client/block.ts @@ -3,6 +3,7 @@ import { Utils } from "./utils" class Block implements IBlock { id: string = Utils.createGuid() + schema: number parentId: string type: string title: string @@ -10,6 +11,7 @@ class Block implements IBlock { url?: string order: number properties: Record = {} + fields: Record = {} createAt: number = Date.now() updateAt: number = 0 deleteAt: number = 0 @@ -31,26 +33,35 @@ class Block implements IBlock { const now = Date.now() this.id = block.id || Utils.createGuid() + this.schema = 1 this.parentId = block.parentId this.type = block.type + + this.fields = block.fields ? { ...block.fields } : {} + this.title = block.title this.icon = block.icon this.url = block.url this.order = block.order + this.createAt = block.createAt || now this.updateAt = block.updateAt || now this.deleteAt = block.deleteAt || 0 - if (Array.isArray(block.properties)) { - // HACKHACK: Port from old schema - this.properties = {} - for (const property of block.properties) { - if (property.id) { - this.properties[property.id] = property.value + if (block.schema !== 1) { + if (Array.isArray(block.properties)) { + // HACKHACK: Port from old schema + this.properties = {} + for (const property of block.properties) { + if (property.id) { + this.properties[property.id] = property.value + } } + } else { + this.properties = { ...block.properties || {} } } } else { - this.properties = { ...block.properties || {} } + this.properties = { ...block.properties } // Shallow copy here. Derived classes must make deep copies of their known properties in their constructors. } } } diff --git a/src/client/board.ts b/src/client/board.ts index b1387d8a9..f01ba7f61 100644 --- a/src/client/board.ts +++ b/src/client/board.ts @@ -16,24 +16,37 @@ interface IPropertyTemplate { } class Board extends Block { - cardProperties: IPropertyTemplate[] = [] + get cardProperties(): IPropertyTemplate[] { return this.fields.cardProperties as IPropertyTemplate[] } + set cardProperties(value: IPropertyTemplate[]) { this.fields.cardProperties = value } constructor(block: any = {}) { super(block) this.type = "board" - if (block.cardProperties) { - // Deep clone of properties and their options - this.cardProperties = block.cardProperties.map((o: IPropertyTemplate) => { + + if (block.fields?.cardProperties) { + // Deep clone of card properties and their options + this.cardProperties = block.fields?.cardProperties.map((o: IPropertyTemplate) => { return { id: o.id, name: o.name, type: o.type, - options: o.options ? o.options.map(option => ({...option})): [] + options: o.options ? o.options.map(option => ({ ...option })) : [] } }) } else { this.cardProperties = [] } + + if (block.schema !== 1) { + this.cardProperties = block.cardProperties?.map((o: IPropertyTemplate) => { + return { + id: o.id, + name: o.name, + type: o.type, + options: o.options ? o.options.map(option => ({ ...option })) : [] + } + }) || [] + } } } diff --git a/src/client/boardView.ts b/src/client/boardView.ts index 95e4e74d2..c1aed1b23 100644 --- a/src/client/boardView.ts +++ b/src/client/boardView.ts @@ -5,21 +5,40 @@ type IViewType = "board" | "table" | "calendar" | "list" | "gallery" type ISortOption = { propertyId: "__name" | string, reversed: boolean } class BoardView extends Block { - viewType: IViewType - groupById?: string - sortOptions: ISortOption[] - visiblePropertyIds: string[] - filter?: FilterGroup + get viewType(): IViewType { return this.fields.viewType } + set viewType(value: IViewType) { this.fields.viewType = value } + + get groupById(): string | undefined { return this.fields.groupById } + set groupById(value: string | undefined) { this.fields.groupById = value } + + get sortOptions(): ISortOption[] { return this.fields.sortOptions } + set sortOptions(value: ISortOption[]) { this.fields.sortOptions = value } + + get visiblePropertyIds(): string[] { return this.fields.visiblePropertyIds } + set visiblePropertyIds(value: string[]) { this.fields.visiblePropertyIds = value } + + get filter(): FilterGroup | undefined { return this.fields.filter } + set filter(value: FilterGroup | undefined) { this.fields.filter = value } constructor(block: any = {}) { super(block) this.type = "view" - this.viewType = block.viewType || "board" - this.groupById = block.groupById - this.sortOptions = block.sortOptions ? block.sortOptions.map((o: ISortOption) => ({...o})) : [] // Deep clone - this.visiblePropertyIds = block.visiblePropertyIds ? block.visiblePropertyIds.slice() : [] - this.filter = new FilterGroup(block.filter) + + this.sortOptions = block.properties?.sortOptions?.map((o: ISortOption) => ({ ...o })) || [] // Deep clone + this.visiblePropertyIds = block.properties?.visiblePropertyIds?.slice() || [] + this.filter = new FilterGroup(block.properties?.filter) + + // TODO: Remove this fixup code + if (block.schema !== 1) { + this.viewType = block.viewType || "board" + this.groupById = block.groupById + this.sortOptions = block.sortOptions ? block.sortOptions.map((o: ISortOption) => ({ ...o })) : [] // Deep clone + this.visiblePropertyIds = block.visiblePropertyIds ? block.visiblePropertyIds.slice() : [] + this.filter = new FilterGroup(block.filter) + } + + if (!this.viewType) { this.viewType = "board" } } } diff --git a/src/client/octoTypes.ts b/src/client/octoTypes.ts index d02c379e5..7047c87c6 100644 --- a/src/client/octoTypes.ts +++ b/src/client/octoTypes.ts @@ -3,12 +3,14 @@ interface IBlock { id: string parentId: string + schema: number type: string title?: string url?: string // TODO: Move to properties (_url) icon?: string order: number properties: Record + fields: Record createAt: number updateAt: number