github.com/qlik-oss/gopherciser@v0.18.6/senseobjects/listbox.go (about) 1 package senseobjects 2 3 import ( 4 "context" 5 "sync" 6 7 "github.com/goccy/go-json" 8 "github.com/pkg/errors" 9 "github.com/qlik-oss/enigma-go/v4" 10 "github.com/qlik-oss/gopherciser/senseobjdef" 11 ) 12 13 type ( 14 // ListBoxProperties listbox properties 15 ListBoxProperties struct { 16 Info enigma.NxInfo `json:"qInfo,omitempty"` 17 MetaDef enigma.NxMetaDef `json:"qMetaDef,omitempty"` 18 } 19 20 // ListBox container with listbox in sense app 21 ListBox struct { 22 Dirty bool // Set flag to have object update before accessing layout next time 23 24 EnigmaObject *enigma.GenericObject 25 layout *enigma.GenericObjectLayout 26 listobject *enigma.ListObject 27 properties *ListBoxProperties 28 mutex sync.Mutex 29 } 30 ) 31 32 var ( 33 DefaultListboxInitialDataFetch = []*enigma.NxPage{{Height: 20, Width: 1}} 34 ) 35 36 func (listBox *ListBox) setLayout(layout *enigma.GenericObjectLayout, lock bool) { 37 if lock { 38 listBox.mutex.Lock() 39 defer listBox.mutex.Unlock() 40 } 41 listBox.layout = layout 42 listBox.listobject = layout.ListObject 43 } 44 45 func (listBox *ListBox) setProperties(properties *ListBoxProperties) { 46 listBox.mutex.Lock() 47 defer listBox.mutex.Unlock() 48 listBox.properties = properties 49 } 50 51 func (listBox *ListBox) setDataPages(datapages []*enigma.NxDataPage, lock bool) error { 52 if lock { 53 listBox.mutex.Lock() 54 defer listBox.mutex.Unlock() 55 } 56 if listBox.listobject == nil { 57 return errors.Errorf("listbox has no listobject") 58 } 59 listBox.listobject.DataPages = datapages 60 return nil 61 } 62 63 // UpdateLayout get and set a new layout for listbox 64 func (listBox *ListBox) UpdateLayout(ctx context.Context) error { 65 return listBox.updateLayout(ctx, true) 66 } 67 68 // UpdateLayout get and set a new layout for listbox 69 func (listBox *ListBox) updateLayout(ctx context.Context, lock bool) error { 70 if listBox.EnigmaObject == nil { 71 return errors.Errorf("listBox enigma object is nil") 72 } 73 74 layoutRaw, err := listBox.EnigmaObject.GetLayoutRaw(ctx) 75 if err != nil { 76 return errors.Wrap(err, "Failed to get listBox layout") 77 } 78 79 var layout enigma.GenericObjectLayout 80 err = json.Unmarshal(layoutRaw, &layout) 81 if err != nil { 82 return errors.Wrap(err, "Failed to unmarshal listBox layout") 83 } 84 85 listBox.setLayout(&layout, lock) 86 87 _, err = listBox.getListObjectData(ctx, lock) 88 return errors.WithStack(err) 89 } 90 91 // UpdateProperties get and set properties for listBox 92 func (listBox *ListBox) UpdateProperties(ctx context.Context) error { 93 if listBox.EnigmaObject == nil { 94 return errors.Errorf("listBox enigma object is nil") 95 } 96 97 propertiesRaw, err := listBox.EnigmaObject.GetEffectivePropertiesRaw(ctx) 98 if err != nil { 99 return errors.Wrapf(err, "Failed to unmarshal listBox properties") 100 } 101 102 var properties ListBoxProperties 103 err = json.Unmarshal(propertiesRaw, &properties) 104 if err != nil { 105 return errors.Wrap(err, "Failed to unmarshal listBox properties") 106 } 107 108 listBox.setProperties(&properties) 109 return nil 110 } 111 112 // GetListObjectData get datapages 113 func (listBox *ListBox) getListObjectData(ctx context.Context, lock bool) ([]*enigma.NxDataPage, error) { 114 objDef, err := senseobjdef.GetObjectDef("listbox") 115 if err != nil { 116 return nil, err 117 } 118 119 datapages, err := listBox.EnigmaObject.GetListObjectData(ctx, string(objDef.Data[0].Requests[0].Path), []*enigma.NxPage{ 120 { 121 Left: 0, 122 Top: 0, 123 Width: 1, 124 Height: listBox.layout.ListObject.Size.Cy, 125 }, 126 }) 127 if err != nil { 128 return nil, err 129 } 130 131 if err := listBox.setDataPages(datapages, lock); err != nil { 132 return nil, errors.WithStack(err) 133 } 134 135 return datapages, nil 136 } 137 138 // Layout for listBox, if layout needs updating this will lock object and update synchronously 139 func (listBox *ListBox) Layout(ctx context.Context) (*enigma.GenericObjectLayout, error) { 140 listBox.mutex.Lock() 141 defer listBox.mutex.Unlock() 142 143 if listBox.Dirty { 144 if err := listBox.updateLayout(ctx, false); err != nil { 145 return nil, errors.WithStack(err) 146 } 147 } 148 149 return listBox.layout, nil 150 } 151 152 // Properties for listBox 153 func (listBox *ListBox) Properties() *ListBoxProperties { 154 listBox.mutex.Lock() 155 defer listBox.mutex.Unlock() 156 return listBox.properties 157 } 158 159 // ListObject for listBox 160 func (listBox *ListBox) ListObject(ctx context.Context) (*enigma.ListObject, error) { 161 listBox.mutex.Lock() 162 defer listBox.mutex.Unlock() 163 164 if listBox.Dirty { 165 if err := listBox.updateLayout(ctx, false); err != nil { 166 return nil, errors.WithStack(err) 167 } 168 } 169 return listBox.listobject, nil 170 } 171 172 // CreateFieldListBoxObject create listbox session object 173 func CreateFieldListBoxObject(ctx context.Context, doc *enigma.Doc, name string) (*ListBox, error) { 174 return createListBoxObject(ctx, doc, &enigma.ListObjectDef{ 175 Def: &enigma.NxInlineDimensionDef{ 176 FieldDefs: []string{name}, 177 FieldLabels: []string{name}, 178 }, 179 InitialDataFetch: DefaultListboxInitialDataFetch, 180 }) 181 } 182 183 // CreateLibraryBoxObject create listbox session object from library ID 184 func CreateLibraryBoxObject(ctx context.Context, doc *enigma.Doc, id string) (*ListBox, error) { 185 return createListBoxObject(ctx, doc, &enigma.ListObjectDef{ 186 LibraryId: id, 187 InitialDataFetch: DefaultListboxInitialDataFetch, 188 }) 189 } 190 191 func createListBoxObject(ctx context.Context, doc *enigma.Doc, objDef *enigma.ListObjectDef) (*ListBox, error) { 192 properties := &enigma.GenericObjectProperties{ 193 Info: &enigma.NxInfo{ 194 Type: "listbox", 195 }, 196 ListObjectDef: objDef, 197 } 198 199 obj, err := doc.CreateSessionObjectRaw(ctx, properties) 200 if err != nil { 201 return nil, errors.Wrapf(err, "Failed to create listbox session object in app<%s>", doc.GenericId) 202 } 203 204 return &ListBox{ 205 EnigmaObject: obj, 206 }, nil 207 }