the useWaveObject() setter now writes back to the DB. make metadata setting generic

This commit is contained in:
sawka
2024-05-28 15:41:03 -07:00
parent f184723354
commit 609f2ec85c
8 changed files with 467 additions and 314 deletions
+3 -3
View File
@@ -27,21 +27,21 @@ const clientAtom: jotai.Atom<Client> = jotai.atom((get) => {
if (clientId == null) {
return null;
}
return WOS.getStaticObjectValue(WOS.makeORef("client", clientId), get);
return WOS.getObjectValue(WOS.makeORef("client", clientId), get);
});
const windowDataAtom: jotai.Atom<WaveWindow> = jotai.atom((get) => {
const windowId = get(windowIdAtom);
if (windowId == null) {
return null;
}
return WOS.getStaticObjectValue(WOS.makeORef("window", windowId), get);
return WOS.getObjectValue(WOS.makeORef("window", windowId), get);
});
const workspaceAtom: jotai.Atom<Workspace> = jotai.atom((get) => {
const windowData = get(windowDataAtom);
if (windowData == null) {
return null;
}
return WOS.getStaticObjectValue(WOS.makeORef("workspace", windowData.workspaceid), get);
return WOS.getObjectValue(WOS.makeORef("workspace", windowData.workspaceid), get);
});
const atoms = {
+35 -5
View File
@@ -147,7 +147,7 @@ function useWaveObjectValue<T>(oref: string): [T, boolean] {
return [atomVal.value, atomVal.loading];
}
function useWaveObject<T>(oref: string): [T, boolean, (T) => void] {
function useWaveObject<T extends WaveObj>(oref: string): [T, boolean, (T) => void] {
let wov = waveObjectValueCache.get(oref);
if (wov == null) {
wov = createWaveValueObject(oref, true);
@@ -162,6 +162,7 @@ function useWaveObject<T>(oref: string): [T, boolean, (T) => void] {
const [atomVal, setAtomVal] = jotai.useAtom(wov.dataAtom);
const simpleSet = (val: T) => {
setAtomVal({ value: val, loading: false });
UpdateObject(val, false);
};
return [atomVal.value, atomVal.loading, simpleSet];
}
@@ -243,15 +244,39 @@ function wrapObjectServiceCall<T>(fnName: string, ...args: any[]): Promise<T> {
return prtn;
}
function getStaticObjectValue<T>(oref: string, getFn: jotai.Getter): T {
// gets the value of a WaveObject from the cache.
// should provide getFn if it is available (e.g. inside of a jotai atom)
// otherwise it will use the globalStore.get function
function getObjectValue<T>(oref: string, getFn?: jotai.Getter): T {
let wov = waveObjectValueCache.get(oref);
if (wov == null) {
return null;
}
if (getFn == null) {
getFn = globalStore.get;
}
const atomVal = getFn(wov.dataAtom);
return atomVal.value;
}
// sets the value of a WaveObject in the cache.
// should provide setFn if it is available (e.g. inside of a jotai atom)
// otherwise it will use the globalStore.set function
function setObjectValue<T>(value: WaveObj, setFn?: jotai.Setter, pushToServer?: boolean) {
const oref = makeORef(value.otype, value.oid);
let wov = waveObjectValueCache.get(oref);
if (wov == null) {
return;
}
if (setFn == null) {
setFn = globalStore.set;
}
setFn(wov.dataAtom, { value: value, loading: false });
if (pushToServer) {
UpdateObject(value, false);
}
}
export function AddTabToWorkspace(tabName: string, activateTab: boolean): Promise<{ tabId: string }> {
return wrapObjectServiceCall("AddTabToWorkspace", tabName, activateTab);
}
@@ -272,16 +297,21 @@ export function CloseTab(tabId: string): Promise<void> {
return wrapObjectServiceCall("CloseTab", tabId);
}
export function UpdateBlockMeta(blockId: string, meta: MetadataType): Promise<void> {
return wrapObjectServiceCall("UpdateBlockMeta", blockId, meta);
export function UpdateObjectMeta(blockId: string, meta: MetadataType): Promise<void> {
return wrapObjectServiceCall("UpdateObjectMeta", blockId, meta);
}
export function UpdateObject(waveObj: WaveObj, returnUpdates: boolean): Promise<WaveObjUpdate[]> {
return wrapObjectServiceCall("UpdateObject", waveObj, returnUpdates);
}
export {
cleanWaveObjectCache,
clearWaveObjectCache,
getStaticObjectValue,
getObjectValue,
loadAndPinWaveObject,
makeORef,
setObjectValue,
updateWaveObject,
updateWaveObjects,
useWaveObject,
+37 -3
View File
@@ -180,13 +180,47 @@ func (svc *ObjectService) CloseTab(uiContext wstore.UIContext, tabId string) (an
return updatesRtn(ctx, nil)
}
func (svc *ObjectService) UpdateBlockMeta(uiContext wstore.UIContext, blockId string, meta map[string]any) (any, error) {
func (svc *ObjectService) UpdateObjectMeta(uiContext wstore.UIContext, orefStr string, meta map[string]any) (any, error) {
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancelFn()
ctx = wstore.ContextWithUpdates(ctx)
err := wstore.UpdateBlockMeta(ctx, blockId, meta)
oref, err := parseORef(orefStr)
if err != nil {
return nil, fmt.Errorf("error merging block meta: %w", err)
return nil, fmt.Errorf("error parsing object reference: %w", err)
}
err = wstore.UpdateObjectMeta(ctx, *oref, meta)
if err != nil {
return nil, fmt.Errorf("error updateing %q meta: %w", orefStr, err)
}
return updatesRtn(ctx, nil)
}
func (svc *ObjectService) UpdateObject(uiContext wstore.UIContext, objData map[string]any, returnUpdates bool) (any, error) {
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancelFn()
ctx = wstore.ContextWithUpdates(ctx)
oref, err := waveobj.ORefFromMap(objData)
if err != nil {
return nil, fmt.Errorf("objData is not a valid object, requires otype and oid: %w", err)
}
found, err := wstore.DBExistsORef(ctx, *oref)
if err != nil {
return nil, fmt.Errorf("error getting object: %w", err)
}
if !found {
return nil, fmt.Errorf("object not found: %s", oref)
}
newObj, err := waveobj.FromJsonMap(objData)
if err != nil {
return nil, fmt.Errorf("error converting data to valid wave object: %w", err)
}
err = wstore.DBUpdate(ctx, newObj)
if err != nil {
return nil, fmt.Errorf("error updating object: %w", err)
}
if returnUpdates {
return updatesRtn(ctx, nil)
}
return nil, nil
}
+49 -153
View File
@@ -4,11 +4,9 @@
package waveobj
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"strings"
"sync"
"github.com/mitchellh/mapstructure"
@@ -18,14 +16,20 @@ const (
OTypeKeyName = "otype"
OIDKeyName = "oid"
VersionKeyName = "version"
MetaKeyName = "meta"
OIDGoFieldName = "OID"
VersionGoFieldName = "Version"
MetaGoFieldName = "Meta"
)
type ORef struct {
OType string `json:"otype"`
OID string `json:"oid"`
OType string `json:"otype" mapstructure:"otype"`
OID string `json:"oid" mapstructure:"oid"`
}
func (oref ORef) String() string {
return fmt.Sprintf("%s:%s", oref.OType, oref.OID)
}
type WaveObj interface {
@@ -36,6 +40,7 @@ type waveObjDesc struct {
RType reflect.Type
OIDField reflect.StructField
VersionField reflect.StructField
MetaField reflect.StructField
}
var waveObjMap = sync.Map{}
@@ -73,6 +78,15 @@ func RegisterType(rtype reflect.Type) {
if versionField.Tag.Get("json") != VersionKeyName {
panic(fmt.Sprintf("Version field json tag must be %q for %v", VersionKeyName, rtype))
}
metaField, found := rtype.Elem().FieldByName(MetaGoFieldName)
if !found {
panic(fmt.Sprintf("missing Meta field for %v", rtype))
}
if metaField.Type.Kind() != reflect.Map ||
metaField.Type.Elem().Kind() != reflect.Interface ||
metaField.Type.Key().Kind() != reflect.String {
panic(fmt.Sprintf("Meta field must be map[string]any for %v", rtype))
}
_, found = waveObjMap.Load(otype)
if found {
panic(fmt.Sprintf("otype %q already registered", otype))
@@ -81,6 +95,7 @@ func RegisterType(rtype reflect.Type) {
RType: rtype,
OIDField: oidField,
VersionField: versionField,
MetaField: metaField,
})
}
@@ -124,6 +139,22 @@ func SetVersion(waveObj WaveObj, version int) {
reflect.ValueOf(waveObj).Elem().FieldByIndex(desc.VersionField.Index).SetInt(int64(version))
}
func GetMeta(waveObj WaveObj) map[string]any {
desc := getWaveObjDesc(waveObj.GetOType())
if desc == nil {
return nil
}
return reflect.ValueOf(waveObj).Elem().FieldByIndex(desc.MetaField.Index).Interface().(map[string]any)
}
func SetMeta(waveObj WaveObj, meta map[string]any) {
desc := getWaveObjDesc(waveObj.GetOType())
if desc == nil {
return
}
reflect.ValueOf(waveObj).Elem().FieldByIndex(desc.MetaField.Index).Set(reflect.ValueOf(meta))
}
func ToJsonMap(w WaveObj) (map[string]any, error) {
m := make(map[string]any)
dconfig := &mapstructure.DecoderConfig{
@@ -158,6 +189,10 @@ func FromJson(data []byte) (WaveObj, error) {
if err != nil {
return nil, err
}
return FromJsonMap(m)
}
func FromJsonMap(m map[string]any) (WaveObj, error) {
otype, ok := m[OTypeKeyName].(string)
if !ok {
return nil, fmt.Errorf("missing otype")
@@ -182,6 +217,16 @@ func FromJson(data []byte) (WaveObj, error) {
return wobj, nil
}
func ORefFromMap(m map[string]any) (*ORef, error) {
oref := ORef{}
err := mapstructure.Decode(m, &oref)
if err != nil {
return nil, err
}
return &oref, nil
}
func FromJsonGen[T WaveObj](data []byte) (T, error) {
obj, err := FromJson(data)
if err != nil {
@@ -195,152 +240,3 @@ func FromJsonGen[T WaveObj](data []byte) (T, error) {
}
return rtn, nil
}
func getTSFieldName(field reflect.StructField) string {
jsonTag := field.Tag.Get("json")
if jsonTag != "" {
parts := strings.Split(jsonTag, ",")
namePart := parts[0]
if namePart != "" {
if namePart == "-" {
return ""
}
return namePart
}
// if namePart is empty, still uses default
}
return field.Name
}
func isFieldOmitEmpty(field reflect.StructField) bool {
jsonTag := field.Tag.Get("json")
if jsonTag != "" {
parts := strings.Split(jsonTag, ",")
if len(parts) > 1 {
for _, part := range parts[1:] {
if part == "omitempty" {
return true
}
}
}
}
return false
}
func typeToTSType(t reflect.Type) (string, []reflect.Type) {
switch t.Kind() {
case reflect.String:
return "string", nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
return "number", nil
case reflect.Bool:
return "boolean", nil
case reflect.Slice, reflect.Array:
elemType, subTypes := typeToTSType(t.Elem())
if elemType == "" {
return "", nil
}
return fmt.Sprintf("%s[]", elemType), subTypes
case reflect.Map:
if t.Key().Kind() != reflect.String {
return "", nil
}
elemType, subTypes := typeToTSType(t.Elem())
if elemType == "" {
return "", nil
}
return fmt.Sprintf("{[key: string]: %s}", elemType), subTypes
case reflect.Struct:
return t.Name(), []reflect.Type{t}
case reflect.Ptr:
return typeToTSType(t.Elem())
case reflect.Interface:
return "any", nil
default:
return "", nil
}
}
var tsRenameMap = map[string]string{
"Window": "WaveWindow",
}
func generateTSTypeInternal(rtype reflect.Type) (string, []reflect.Type) {
var buf bytes.Buffer
waveObjType := reflect.TypeOf((*WaveObj)(nil)).Elem()
tsTypeName := rtype.Name()
if tsRename, ok := tsRenameMap[tsTypeName]; ok {
tsTypeName = tsRename
}
var isWaveObj bool
if rtype.Implements(waveObjType) || reflect.PointerTo(rtype).Implements(waveObjType) {
isWaveObj = true
buf.WriteString(fmt.Sprintf("type %s = WaveObj & {\n", tsTypeName))
} else {
buf.WriteString(fmt.Sprintf("type %s = {\n", tsTypeName))
}
var subTypes []reflect.Type
for i := 0; i < rtype.NumField(); i++ {
field := rtype.Field(i)
if field.PkgPath != "" {
continue
}
fieldName := getTSFieldName(field)
if fieldName == "" {
continue
}
if isWaveObj && (fieldName == OTypeKeyName || fieldName == OIDKeyName || fieldName == VersionKeyName) {
continue
}
optMarker := ""
if isFieldOmitEmpty(field) {
optMarker = "?"
}
tsTypeTag := field.Tag.Get("tstype")
if tsTypeTag != "" {
buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsTypeTag))
continue
}
tsType, fieldSubTypes := typeToTSType(field.Type)
if tsType == "" {
continue
}
subTypes = append(subTypes, fieldSubTypes...)
buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsType))
}
buf.WriteString("};\n")
return buf.String(), subTypes
}
func GenerateWaveObjTSType() string {
var buf bytes.Buffer
buf.WriteString("type WaveObj = {\n")
buf.WriteString(" otype: string;\n")
buf.WriteString(" oid: string;\n")
buf.WriteString(" version: number;\n")
buf.WriteString("};\n")
return buf.String()
}
func GenerateTSType(rtype reflect.Type, tsTypesMap map[reflect.Type]string) {
if rtype == nil {
return
}
if rtype.Kind() == reflect.Ptr {
rtype = rtype.Elem()
}
if _, ok := tsTypesMap[rtype]; ok {
return
}
if rtype == waveObjRType {
tsTypesMap[rtype] = GenerateWaveObjTSType()
return
}
tsType, subTypes := generateTSTypeInternal(rtype)
tsTypesMap[rtype] = tsType
for _, subType := range subTypes {
GenerateTSType(subType, tsTypesMap)
}
}
+160
View File
@@ -0,0 +1,160 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package waveobj
import (
"bytes"
"fmt"
"reflect"
"strings"
)
func getTSFieldName(field reflect.StructField) string {
jsonTag := field.Tag.Get("json")
if jsonTag != "" {
parts := strings.Split(jsonTag, ",")
namePart := parts[0]
if namePart != "" {
if namePart == "-" {
return ""
}
return namePart
}
// if namePart is empty, still uses default
}
return field.Name
}
func isFieldOmitEmpty(field reflect.StructField) bool {
jsonTag := field.Tag.Get("json")
if jsonTag != "" {
parts := strings.Split(jsonTag, ",")
if len(parts) > 1 {
for _, part := range parts[1:] {
if part == "omitempty" {
return true
}
}
}
}
return false
}
func typeToTSType(t reflect.Type) (string, []reflect.Type) {
switch t.Kind() {
case reflect.String:
return "string", nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
return "number", nil
case reflect.Bool:
return "boolean", nil
case reflect.Slice, reflect.Array:
elemType, subTypes := typeToTSType(t.Elem())
if elemType == "" {
return "", nil
}
return fmt.Sprintf("%s[]", elemType), subTypes
case reflect.Map:
if t.Key().Kind() != reflect.String {
return "", nil
}
elemType, subTypes := typeToTSType(t.Elem())
if elemType == "" {
return "", nil
}
return fmt.Sprintf("{[key: string]: %s}", elemType), subTypes
case reflect.Struct:
return t.Name(), []reflect.Type{t}
case reflect.Ptr:
return typeToTSType(t.Elem())
case reflect.Interface:
return "any", nil
default:
return "", nil
}
}
var tsRenameMap = map[string]string{
"Window": "WaveWindow",
}
func generateTSTypeInternal(rtype reflect.Type) (string, []reflect.Type) {
var buf bytes.Buffer
waveObjType := reflect.TypeOf((*WaveObj)(nil)).Elem()
tsTypeName := rtype.Name()
if tsRename, ok := tsRenameMap[tsTypeName]; ok {
tsTypeName = tsRename
}
var isWaveObj bool
if rtype.Implements(waveObjType) || reflect.PointerTo(rtype).Implements(waveObjType) {
isWaveObj = true
buf.WriteString(fmt.Sprintf("type %s = WaveObj & {\n", tsTypeName))
} else {
buf.WriteString(fmt.Sprintf("type %s = {\n", tsTypeName))
}
var subTypes []reflect.Type
for i := 0; i < rtype.NumField(); i++ {
field := rtype.Field(i)
if field.PkgPath != "" {
continue
}
fieldName := getTSFieldName(field)
if fieldName == "" {
continue
}
if isWaveObj && (fieldName == OTypeKeyName || fieldName == OIDKeyName || fieldName == VersionKeyName) {
continue
}
optMarker := ""
if isFieldOmitEmpty(field) {
optMarker = "?"
}
tsTypeTag := field.Tag.Get("tstype")
if tsTypeTag != "" {
buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsTypeTag))
continue
}
tsType, fieldSubTypes := typeToTSType(field.Type)
if tsType == "" {
continue
}
subTypes = append(subTypes, fieldSubTypes...)
buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsType))
}
buf.WriteString("};\n")
return buf.String(), subTypes
}
func GenerateWaveObjTSType() string {
var buf bytes.Buffer
buf.WriteString("type WaveObj = {\n")
buf.WriteString(" otype: string;\n")
buf.WriteString(" oid: string;\n")
buf.WriteString(" version: number;\n")
buf.WriteString("};\n")
return buf.String()
}
func GenerateTSType(rtype reflect.Type, tsTypesMap map[reflect.Type]string) {
if rtype == nil {
return
}
if rtype.Kind() == reflect.Ptr {
rtype = rtype.Elem()
}
if _, ok := tsTypesMap[rtype]; ok {
return
}
if rtype == waveObjRType {
tsTypesMap[rtype] = GenerateWaveObjTSType()
return
}
tsType, subTypes := generateTSTypeInternal(rtype)
tsTypesMap[rtype] = tsType
for _, subType := range subTypes {
GenerateTSType(subType, tsTypesMap)
}
}
+24 -150
View File
@@ -6,14 +6,11 @@ package wstore
import (
"bytes"
"context"
"encoding/json"
"fmt"
"log"
"reflect"
"time"
"github.com/google/uuid"
"github.com/wavetermdev/thenextwave/pkg/shellexec"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
)
@@ -137,146 +134,6 @@ func ContextUpdatesRollbackTx(ctx context.Context) {
updates.UpdatesStack = updates.UpdatesStack[:len(updates.UpdatesStack)-1]
}
type WaveObjTombstone struct {
OType string `json:"otype"`
OID string `json:"oid"`
}
const (
UpdateType_Update = "update"
UpdateType_Delete = "delete"
)
type WaveObjUpdate struct {
UpdateType string `json:"updatetype"`
OType string `json:"otype"`
OID string `json:"oid"`
Obj waveobj.WaveObj `json:"obj,omitempty"`
}
func (update WaveObjUpdate) MarshalJSON() ([]byte, error) {
rtn := make(map[string]any)
rtn["updatetype"] = update.UpdateType
rtn["otype"] = update.OType
rtn["oid"] = update.OID
if update.Obj != nil {
var err error
rtn["obj"], err = waveobj.ToJsonMap(update.Obj)
if err != nil {
return nil, err
}
}
return json.Marshal(rtn)
}
type UIContext struct {
WindowId string `json:"windowid"`
ActiveTabId string `json:"activetabid"`
}
type Client struct {
OID string `json:"oid"`
Version int `json:"version"`
MainWindowId string `json:"mainwindowid"`
}
func (*Client) GetOType() string {
return "client"
}
func AllWaveObjTypes() []reflect.Type {
return []reflect.Type{
reflect.TypeOf(&Client{}),
reflect.TypeOf(&Window{}),
reflect.TypeOf(&Workspace{}),
reflect.TypeOf(&Tab{}),
reflect.TypeOf(&Block{}),
}
}
// stores the ui-context of the window
// workspaceid, active tab, active block within each tab, window size, etc.
type Window struct {
OID string `json:"oid"`
Version int `json:"version"`
WorkspaceId string `json:"workspaceid"`
ActiveTabId string `json:"activetabid"`
ActiveBlockMap map[string]string `json:"activeblockmap"` // map from tabid to blockid
Pos Point `json:"pos"`
WinSize WinSize `json:"winsize"`
LastFocusTs int64 `json:"lastfocusts"`
}
func (*Window) GetOType() string {
return "window"
}
type Workspace struct {
OID string `json:"oid"`
Version int `json:"version"`
Name string `json:"name"`
TabIds []string `json:"tabids"`
}
func (*Workspace) GetOType() string {
return "workspace"
}
type Tab struct {
OID string `json:"oid"`
Version int `json:"version"`
Name string `json:"name"`
BlockIds []string `json:"blockids"`
}
func (*Tab) GetOType() string {
return "tab"
}
type FileDef struct {
FileType string `json:"filetype,omitempty"`
Path string `json:"path,omitempty"`
Url string `json:"url,omitempty"`
Content string `json:"content,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
type BlockDef struct {
Controller string `json:"controller,omitempty"`
View string `json:"view,omitempty"`
Files map[string]*FileDef `json:"files,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
type RuntimeOpts struct {
TermSize shellexec.TermSize `json:"termsize,omitempty"`
WinSize WinSize `json:"winsize,omitempty"`
}
type Point struct {
X int `json:"x"`
Y int `json:"y"`
}
type WinSize struct {
Width int `json:"width"`
Height int `json:"height"`
}
type Block struct {
OID string `json:"oid"`
Version int `json:"version"`
BlockDef *BlockDef `json:"blockdef"`
Controller string `json:"controller"`
View string `json:"view"`
Meta map[string]any `json:"meta,omitempty"`
RuntimeOpts *RuntimeOpts `json:"runtimeopts,omitempty"`
}
func (*Block) GetOType() string {
return "block"
}
func CreateTab(ctx context.Context, workspaceId string, name string) (*Tab, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (*Tab, error) {
ws, _ := DBGet[*Workspace](tx.Context(), workspaceId)
@@ -395,20 +252,37 @@ func CloseTab(ctx context.Context, workspaceId string, tabId string) error {
})
}
func UpdateBlockMeta(ctx context.Context, blockId string, meta map[string]any) error {
func UpdateMeta(ctx context.Context, oref waveobj.ORef, meta map[string]any) error {
return WithTx(ctx, func(tx *TxWrap) error {
block, _ := DBGet[*Block](tx.Context(), blockId)
if block == nil {
return fmt.Errorf("block not found: %q", blockId)
obj, _ := DBGetORef(tx.Context(), oref)
if obj == nil {
return fmt.Errorf("object not found: %q", oref)
}
// obj.SetMeta(meta)
DBUpdate(tx.Context(), obj)
return nil
})
}
func UpdateObjectMeta(ctx context.Context, oref waveobj.ORef, meta map[string]any) error {
return WithTx(ctx, func(tx *TxWrap) error {
obj, _ := DBGetORef(tx.Context(), oref)
if obj == nil {
return fmt.Errorf("object not found: %q", oref)
}
objMeta := waveobj.GetMeta(obj)
if objMeta == nil {
objMeta = make(map[string]any)
}
for k, v := range meta {
if v == nil {
delete(block.Meta, k)
delete(objMeta, k)
continue
}
block.Meta[k] = v
objMeta[k] = v
}
DBUpdate(tx.Context(), block)
waveobj.SetMeta(obj, objMeta)
DBUpdate(tx.Context(), obj)
return nil
})
}
+8
View File
@@ -72,6 +72,14 @@ func DBGetSingletonByType(ctx context.Context, otype string) (waveobj.WaveObj, e
})
}
func DBExistsORef(ctx context.Context, oref waveobj.ORef) (bool, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (bool, error) {
table := tableNameFromOType(oref.OType)
query := fmt.Sprintf("SELECT oid FROM %s WHERE oid = ?", table)
return tx.Exists(query, oref.OID), nil
})
}
func DBGet[T waveobj.WaveObj](ctx context.Context, id string) (T, error) {
rtn, err := DBGetORef(ctx, waveobj.ORef{OType: getOTypeGen[T](), OID: id})
return genericCastWithErr[T](rtn, err)
+151
View File
@@ -0,0 +1,151 @@
// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package wstore
import (
"encoding/json"
"reflect"
"github.com/wavetermdev/thenextwave/pkg/shellexec"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
)
type UIContext struct {
WindowId string `json:"windowid"`
ActiveTabId string `json:"activetabid"`
}
const (
UpdateType_Update = "update"
UpdateType_Delete = "delete"
)
type WaveObjUpdate struct {
UpdateType string `json:"updatetype"`
OType string `json:"otype"`
OID string `json:"oid"`
Obj waveobj.WaveObj `json:"obj,omitempty"`
}
func (update WaveObjUpdate) MarshalJSON() ([]byte, error) {
rtn := make(map[string]any)
rtn["updatetype"] = update.UpdateType
rtn["otype"] = update.OType
rtn["oid"] = update.OID
if update.Obj != nil {
var err error
rtn["obj"], err = waveobj.ToJsonMap(update.Obj)
if err != nil {
return nil, err
}
}
return json.Marshal(rtn)
}
type Client struct {
OID string `json:"oid"`
Version int `json:"version"`
MainWindowId string `json:"mainwindowid"`
Meta map[string]any `json:"meta"`
}
func (*Client) GetOType() string {
return "client"
}
// stores the ui-context of the window
// workspaceid, active tab, active block within each tab, window size, etc.
type Window struct {
OID string `json:"oid"`
Version int `json:"version"`
WorkspaceId string `json:"workspaceid"`
ActiveTabId string `json:"activetabid"`
ActiveBlockMap map[string]string `json:"activeblockmap"` // map from tabid to blockid
Pos Point `json:"pos"`
WinSize WinSize `json:"winsize"`
LastFocusTs int64 `json:"lastfocusts"`
Meta map[string]any `json:"meta"`
}
func (*Window) GetOType() string {
return "window"
}
type Workspace struct {
OID string `json:"oid"`
Version int `json:"version"`
Name string `json:"name"`
TabIds []string `json:"tabids"`
Meta map[string]any `json:"meta"`
}
func (*Workspace) GetOType() string {
return "workspace"
}
type Tab struct {
OID string `json:"oid"`
Version int `json:"version"`
Name string `json:"name"`
BlockIds []string `json:"blockids"`
Meta map[string]any `json:"meta"`
}
func (*Tab) GetOType() string {
return "tab"
}
type FileDef struct {
FileType string `json:"filetype,omitempty"`
Path string `json:"path,omitempty"`
Url string `json:"url,omitempty"`
Content string `json:"content,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
type BlockDef struct {
Controller string `json:"controller,omitempty"`
View string `json:"view,omitempty"`
Files map[string]*FileDef `json:"files,omitempty"`
Meta map[string]any `json:"meta,omitempty"`
}
type RuntimeOpts struct {
TermSize shellexec.TermSize `json:"termsize,omitempty"`
WinSize WinSize `json:"winsize,omitempty"`
}
type Point struct {
X int `json:"x"`
Y int `json:"y"`
}
type WinSize struct {
Width int `json:"width"`
Height int `json:"height"`
}
type Block struct {
OID string `json:"oid"`
Version int `json:"version"`
BlockDef *BlockDef `json:"blockdef"`
Controller string `json:"controller"`
View string `json:"view"`
RuntimeOpts *RuntimeOpts `json:"runtimeopts,omitempty"`
Meta map[string]any `json:"meta"`
}
func (*Block) GetOType() string {
return "block"
}
func AllWaveObjTypes() []reflect.Type {
return []reflect.Type{
reflect.TypeOf(&Client{}),
reflect.TypeOf(&Window{}),
reflect.TypeOf(&Workspace{}),
reflect.TypeOf(&Tab{}),
reflect.TypeOf(&Block{}),
}
}