mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
the useWaveObject() setter now writes back to the DB. make metadata setting generic
This commit is contained in:
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{}),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user