github.com/qlik-oss/gopherciser@v0.18.6/senseobjects/app.go (about) 1 package senseobjects 2 3 import ( 4 "context" 5 "strings" 6 "sync" 7 8 "github.com/pkg/errors" 9 "github.com/qlik-oss/enigma-go/v4" 10 "github.com/qlik-oss/gopherciser/action" 11 ) 12 13 type ( 14 // SessionObjects for the app 15 SessionObjects struct { 16 sheetList *SheetList 17 bookmarkList *BookmarkList 18 currentSelections *CurrentSelections 19 localeInfo *enigma.LocaleInfo 20 variablelist *VariableList 21 storylist *StoryList 22 loadmodellist *LoadModelList 23 fieldlist *FieldList 24 dimensionList *DimensionList 25 appPropsList *AppPropsList 26 } 27 28 // App sense app object 29 App struct { 30 GUID string 31 Doc *enigma.Doc 32 Layout *enigma.NxAppLayout 33 bookmarks map[string]*enigma.GenericBookmark 34 mutex sync.Mutex 35 SessionObjects 36 } 37 ) 38 39 // GetSheetList update sheet list for app 40 func (app *App) GetSheetList(sessionState SessionState, actionState *action.State) (*SheetList, error) { 41 if app.sheetList != nil { 42 return app.sheetList, nil 43 } 44 45 // update sheetList to latest 46 updateSheetList := func(ctx context.Context) error { 47 sl, err := CreateSheetListObject(ctx, app.Doc) 48 if err != nil { 49 return err 50 } 51 app.setSheetList(sessionState, sl) 52 return err 53 } 54 if err := sessionState.SendRequest(actionState, updateSheetList); err != nil { 55 return nil, errors.WithStack(err) 56 } 57 58 if err := sessionState.SendRequest(actionState, app.sheetList.UpdateLayout); err != nil { 59 return nil, errors.WithStack(err) 60 } 61 62 // update sheetList layout when sheetList has a change event 63 onSheetListChanged := func(ctx context.Context, actionState *action.State) error { 64 return errors.WithStack(app.sheetList.UpdateLayout(ctx)) 65 } 66 67 sessionState.RegisterEvent(app.sheetList.enigmaObject.Handle, 68 onSheetListChanged, nil, true) 69 70 return app.sheetList, nil 71 } 72 73 func (app *App) setSheetList(sessionState SessionState, sl *SheetList) { 74 app.mutex.Lock() 75 defer app.mutex.Unlock() 76 if app.sheetList != nil && app.sheetList.enigmaObject != nil && app.sheetList.enigmaObject.Handle > 0 && sl != app.sheetList { 77 sessionState.LogDebugf("senseobjects.app.setSheetList: executing DeRegisterEvent on handle<%d>", app.GUID, app.sheetList.enigmaObject.Handle) 78 sessionState.DeRegisterEvent(app.sheetList.enigmaObject.Handle) 79 } 80 app.sheetList = sl 81 } 82 83 // GetBookmarkList update bookmark list for app 84 func (app *App) GetBookmarkList(sessionState SessionState, actionState *action.State) (*BookmarkList, error) { 85 if app.bookmarkList != nil { 86 return app.bookmarkList, nil 87 } 88 89 // create bookmark list 90 createBookmarkList := func(ctx context.Context) error { 91 bl, err := CreateBookmarkListObject(ctx, app.Doc) 92 if err != nil { 93 return err 94 } 95 app.setBookmarkList(sessionState, bl) 96 return err 97 } 98 99 if err := sessionState.SendRequest(actionState, createBookmarkList); err != nil { 100 return nil, errors.WithStack(err) 101 } 102 103 if err := sessionState.SendRequest(actionState, app.bookmarkList.UpdateLayout); err != nil { 104 return nil, errors.WithStack(err) 105 } 106 107 if err := sessionState.SendRequest(actionState, app.bookmarkList.UpdateProperties); err != nil { 108 return nil, errors.WithStack(err) 109 } 110 111 // update bookmark list layout when bookmark list has a change event 112 onBookmarkListChanged := func(ctx context.Context, actionState *action.State) error { 113 return errors.WithStack(app.bookmarkList.UpdateLayout(ctx)) 114 } 115 116 sessionState.RegisterEvent(app.bookmarkList.enigmaObject.Handle, 117 onBookmarkListChanged, nil, true) 118 119 return app.bookmarkList, nil 120 } 121 122 func (app *App) setBookmarkList(sessionState SessionState, bl *BookmarkList) { 123 app.mutex.Lock() 124 defer app.mutex.Unlock() 125 if app.bookmarkList != nil && app.bookmarkList.enigmaObject != nil && app.bookmarkList.enigmaObject.Handle > 0 && bl != app.bookmarkList { 126 sessionState.DeRegisterEvent(app.bookmarkList.enigmaObject.Handle) 127 } 128 app.bookmarkList = bl 129 } 130 131 // GetVariableList create or return existing variable list session object 132 func (app *App) GetVariableList(sessionState SessionState, actionState *action.State) (*VariableList, error) { 133 if app.variablelist != nil { 134 return app.variablelist, nil 135 } 136 137 // create variable list 138 createVariableList := func(ctx context.Context) error { 139 vl, err := CreateVariableListObject(ctx, app.Doc) 140 if err != nil { 141 return err 142 } 143 app.setVariableList(sessionState, vl) 144 return err 145 } 146 147 if err := sessionState.SendRequest(actionState, createVariableList); err != nil { 148 return nil, errors.WithStack(err) 149 } 150 151 if err := sessionState.SendRequest(actionState, app.variablelist.UpdateLayout); err != nil { 152 return nil, errors.WithStack(err) 153 } 154 155 // update variable list layout when variable list has a change event 156 onVariableListChanged := func(ctx context.Context, actionState *action.State) error { 157 return errors.WithStack(app.variablelist.UpdateLayout(ctx)) 158 } 159 sessionState.RegisterEvent(app.variablelist.enigmaObject.Handle, onVariableListChanged, nil, true) 160 161 return app.variablelist, nil 162 } 163 164 func (app *App) setVariableList(sessionState SessionState, vl *VariableList) { 165 app.mutex.Lock() 166 defer app.mutex.Unlock() 167 if app.variablelist != nil && app.variablelist.enigmaObject != nil && app.variablelist.enigmaObject.Handle > 0 && vl != app.variablelist { 168 sessionState.DeRegisterEvent(app.variablelist.enigmaObject.Handle) 169 } 170 app.variablelist = vl 171 } 172 173 // GetAppsPropsList create or return AppsPropsList 174 func (app *App) GetAppsPropsList(sessionState SessionState, actionState *action.State) (*AppPropsList, error) { 175 if app.appPropsList != nil { 176 return app.appPropsList, nil 177 } 178 179 createAppPropsList := func(ctx context.Context) error { 180 al, err := CreateAppPropsListObject(ctx, app.Doc) 181 if err != nil { 182 return err 183 } 184 app.setAppPropsList(sessionState, al) 185 return nil 186 } 187 188 if err := sessionState.SendRequest(actionState, createAppPropsList); err != nil { 189 return nil, errors.WithStack(err) 190 } 191 192 if err := sessionState.SendRequest(actionState, func(ctx context.Context) error { 193 return app.appPropsList.UpdateLayout(ctx, app.Doc, sessionState, actionState) 194 }); err != nil { 195 return nil, errors.WithStack(err) 196 } 197 198 onAppPropsListChanged := func(ctx context.Context, actionState *action.State) error { 199 return errors.WithStack(app.appPropsList.UpdateLayout(ctx, app.Doc, sessionState, actionState)) 200 } 201 sessionState.RegisterEvent(app.appPropsList.enigmaObject.Handle, onAppPropsListChanged, nil, true) 202 203 return app.appPropsList, nil 204 } 205 206 func (app *App) setAppPropsList(sessionState SessionState, al *AppPropsList) { 207 app.mutex.Lock() 208 defer app.mutex.Unlock() 209 if app.appPropsList != nil && app.appPropsList.enigmaObject != nil && app.appPropsList.enigmaObject.Handle > 0 && al != app.appPropsList { 210 sessionState.DeRegisterEvent(app.appPropsList.enigmaObject.Handle) 211 app.appPropsList.RemoveAllItems(sessionState) 212 } 213 app.appPropsList = al 214 } 215 216 // GetStoryList create or return existing story list session object 217 func (app *App) GetStoryList(sessionState SessionState, actionState *action.State) (*StoryList, error) { 218 if app.storylist != nil { 219 return app.storylist, nil 220 } 221 222 // create story list 223 createStoryList := func(ctx context.Context) error { 224 sl, err := CreateStoryListObject(ctx, app.Doc) 225 if err != nil { 226 return err 227 } 228 app.setStoryList(sessionState, sl) 229 return err 230 } 231 232 if err := sessionState.SendRequest(actionState, createStoryList); err != nil { 233 return nil, errors.WithStack(err) 234 } 235 236 if err := sessionState.SendRequest(actionState, app.storylist.UpdateLayout); err != nil { 237 return nil, errors.WithStack(err) 238 } 239 240 // update story list layout when story list has a change event 241 onStoryListChanged := func(ctx context.Context, actionState *action.State) error { 242 return errors.WithStack(app.storylist.UpdateLayout(ctx)) 243 } 244 sessionState.RegisterEvent(app.storylist.enigmaObject.Handle, onStoryListChanged, nil, true) 245 246 return app.storylist, nil 247 } 248 249 func (app *App) setStoryList(sessionState SessionState, sl *StoryList) { 250 app.mutex.Lock() 251 defer app.mutex.Unlock() 252 if app.storylist != nil && app.storylist.enigmaObject != nil && app.storylist.enigmaObject.Handle > 0 && sl != app.storylist { 253 sessionState.DeRegisterEvent(app.storylist.enigmaObject.Handle) 254 } 255 app.storylist = sl 256 } 257 258 // GetLoadModelList create or return existing load model list session object 259 func (app *App) GetLoadModelList(sessionState SessionState, actionState *action.State) (*LoadModelList, error) { 260 if app.loadmodellist != nil { 261 return app.loadmodellist, nil 262 } 263 264 // create load model list 265 createLoadModelList := func(ctx context.Context) error { 266 lml, err := CreateLoadModelListObject(ctx, app.Doc) 267 if err != nil { 268 return err 269 } 270 app.setLoadModelList(sessionState, lml) 271 return err 272 } 273 274 if err := sessionState.SendRequest(actionState, createLoadModelList); err != nil { 275 return nil, errors.WithStack(err) 276 } 277 278 if err := sessionState.SendRequest(actionState, app.loadmodellist.UpdateLayout); err != nil { 279 return nil, errors.WithStack(err) 280 } 281 282 if err := sessionState.SendRequest(actionState, app.loadmodellist.UpdateProperties); err != nil { 283 return nil, errors.WithStack(err) 284 } 285 286 // update load model list layout when load model list has a change event 287 onLoadModelListChanged := func(ctx context.Context, actionState *action.State) error { 288 return errors.WithStack(app.loadmodellist.UpdateLayout(ctx)) 289 } 290 sessionState.RegisterEvent(app.loadmodellist.enigmaObject.Handle, onLoadModelListChanged, nil, true) 291 292 return app.loadmodellist, nil 293 } 294 295 func (app *App) setLoadModelList(sessionState SessionState, lml *LoadModelList) { 296 app.mutex.Lock() 297 defer app.mutex.Unlock() 298 if app.loadmodellist != nil && app.loadmodellist.enigmaObject != nil && app.loadmodellist.enigmaObject.Handle > 0 && lml != app.loadmodellist { 299 sessionState.DeRegisterEvent(app.loadmodellist.enigmaObject.Handle) 300 } 301 app.loadmodellist = lml 302 } 303 304 // GetBookmarkObject with ID 305 func (app *App) GetBookmarkObject(sessionState SessionState, actionState *action.State, id string) (*enigma.GenericBookmark, error) { 306 // Ge id from map of bookmarks 307 bm := app.getBookmarkFromMap(id) 308 if bm != nil { 309 return bm, nil 310 } 311 312 // Bookmark object not in map, get it 313 if err := sessionState.SendRequest(actionState, func(ctx context.Context) error { 314 var err error 315 bm, err = app.Doc.GetBookmark(ctx, id) 316 if err != nil { 317 return errors.Wrap(err, "failed to get bookmark object") 318 } 319 if _, err := bm.GetLayout(ctx); err != nil { 320 return errors.Wrap(err, "failed to get bookmark layout") 321 } 322 323 app.addBookmarkToMap(bm) 324 325 // Update data when object gets a changed event 326 onBookmarkChanged := func(ctx context.Context, actionState *action.State) error { 327 _, err := bm.GetLayout(ctx) 328 return errors.WithStack(err) 329 } 330 331 // remove from list when event gets de-registered 332 onEventDeregister := func() { 333 if app == nil { 334 return 335 } 336 app.mutex.Lock() 337 defer app.mutex.Unlock() 338 if app.bookmarks != nil { 339 delete(app.bookmarks, id) 340 } 341 } 342 sessionState.RegisterEvent(bm.Handle, onBookmarkChanged, onEventDeregister, true) 343 return err 344 }); err != nil { 345 return nil, errors.WithStack(err) 346 } 347 348 return bm, nil 349 } 350 351 func (app *App) getBookmarkFromMap(id string) *enigma.GenericBookmark { 352 app.mutex.Lock() 353 defer app.mutex.Unlock() 354 if app.bookmarks == nil { 355 app.bookmarks = make(map[string]*enigma.GenericBookmark) 356 } 357 return app.bookmarks[id] 358 } 359 360 func (app *App) addBookmarkToMap(bm *enigma.GenericBookmark) { 361 if bm == nil { 362 return 363 } 364 app.mutex.Lock() 365 defer app.mutex.Unlock() 366 if app.bookmarks == nil { 367 app.bookmarks = make(map[string]*enigma.GenericBookmark) 368 } 369 app.bookmarks[bm.GenericId] = bm 370 } 371 372 func (app *App) GetAggregatedSelectionFields() string { 373 fields := make([]string, 0, len(app.currentSelections.layout.SelectionObject.Selections)) 374 for _, selection := range app.currentSelections.layout.SelectionObject.Selections { 375 fields = append(fields, selection.Field) 376 } 377 378 return strings.Join(fields, ",") 379 } 380 381 func (app *App) GetDimensionList(sessionState SessionState, actionState *action.State) (*DimensionList, error) { 382 if app.dimensionList != nil { 383 return app.dimensionList, nil 384 } 385 updateDimensionList := func(ctx context.Context) error { 386 dl, err := CreateDimensionListObject(ctx, app.Doc) 387 if err != nil { 388 return err 389 } 390 app.setDimensionList(sessionState, dl) 391 return nil 392 } 393 if err := sessionState.SendRequest(actionState, updateDimensionList); err != nil { 394 return nil, errors.WithStack(err) 395 } 396 397 // Get data 398 if err := sessionState.SendRequest(actionState, app.dimensionList.UpdateLayout); err != nil { 399 return nil, errors.WithStack(err) 400 } 401 if err := sessionState.SendRequest(actionState, app.dimensionList.UpdateProperties); err != nil { 402 return nil, errors.WithStack(err) 403 } 404 405 // setup automatic data update 406 onDimensionListChanged := func(ctx context.Context, actionState *action.State) error { 407 return errors.WithStack(app.dimensionList.UpdateLayout(ctx)) 408 } 409 sessionState.RegisterEvent(app.dimensionList.enigmaObject.Handle, onDimensionListChanged, nil, true) 410 411 return app.dimensionList, nil 412 413 } 414 415 // GetFieldList session object containing list of fields 416 func (app *App) GetFieldList(sessionState SessionState, actionState *action.State) (*FieldList, error) { 417 if app.fieldlist != nil { 418 return app.fieldlist, nil 419 } 420 421 // Create session object 422 updateFieldList := func(ctx context.Context) error { 423 fl, err := CreateFieldListObject(ctx, app.Doc) 424 if err != nil { 425 return err 426 } 427 app.setFieldList(sessionState, fl) 428 return nil 429 } 430 if err := sessionState.SendRequest(actionState, updateFieldList); err != nil { 431 return nil, errors.WithStack(err) 432 } 433 434 // Get data 435 if err := sessionState.SendRequest(actionState, app.fieldlist.UpdateLayout); err != nil { 436 return nil, errors.WithStack(err) 437 } 438 if err := sessionState.SendRequest(actionState, app.fieldlist.UpdateProperties); err != nil { 439 return nil, errors.WithStack(err) 440 } 441 442 // setup automatic data update 443 onFieldListChanged := func(ctx context.Context, actionState *action.State) error { 444 return errors.WithStack(app.fieldlist.UpdateLayout(ctx)) 445 } 446 sessionState.RegisterEvent(app.fieldlist.enigmaObject.Handle, onFieldListChanged, nil, true) 447 448 return app.fieldlist, nil 449 } 450 451 // GetCurrentSelections create current selection session object and add to list 452 func (app *App) GetCurrentSelections(sessionState SessionState, actionState *action.State, requestData bool) (*CurrentSelections, error) { 453 if app.currentSelections != nil { 454 if requestData && app.currentSelections.enigmaObject != nil { 455 if f := sessionState.GetEventFunc(app.currentSelections.enigmaObject.Handle); f != nil { 456 if err := f(sessionState.BaseContext(), actionState); err != nil { 457 return app.currentSelections, errors.WithStack(err) 458 } 459 } 460 } 461 return app.currentSelections, nil 462 } 463 464 // Create session object 465 updateCurrentSelections := func(ctx context.Context) error { 466 cs, err := CreateCurrentSelections(ctx, app.Doc) 467 if err != nil { 468 return err 469 } 470 app.setCurrentSelections(sessionState, cs) 471 return nil 472 } 473 if err := sessionState.SendRequest(actionState, updateCurrentSelections); err != nil { 474 return nil, errors.WithStack(err) 475 } 476 477 var once sync.Once 478 // update currentSelection layout when object is changed 479 onCurrentSelectionChanged := func(ctx context.Context, actionState *action.State) error { 480 var err error 481 once.Do(func() { 482 err = sessionState.SendRequest(actionState, app.currentSelections.UpdateProperties) 483 }) 484 if err != nil { 485 return errors.WithStack(err) 486 } 487 return errors.WithStack(app.currentSelections.UpdateLayout(ctx)) 488 } 489 490 if requestData { 491 if err := onCurrentSelectionChanged(sessionState.BaseContext(), actionState); err != nil { 492 return nil, errors.WithStack(err) 493 } 494 } 495 496 sessionState.RegisterEvent(app.currentSelections.enigmaObject.Handle, onCurrentSelectionChanged, nil, true) 497 498 return app.currentSelections, nil 499 } 500 501 // GetLocaleInfo send get locale info request 502 func (app *App) GetLocaleInfo(ctx context.Context) (*enigma.LocaleInfo, error) { 503 if app.localeInfo != nil { 504 return app.localeInfo, nil 505 } 506 507 localeInfo, err := app.Doc.GetLocaleInfo(ctx) 508 if err != nil { 509 return nil, errors.WithStack(err) 510 } 511 512 app.mutex.Lock() 513 defer app.mutex.Unlock() 514 app.localeInfo = localeInfo 515 516 return localeInfo, nil 517 } 518 519 func (app *App) setCurrentSelections(sessionState SessionState, cs *CurrentSelections) { 520 app.mutex.Lock() 521 defer app.mutex.Unlock() 522 if app.currentSelections != nil && app.currentSelections.enigmaObject != nil && app.currentSelections.enigmaObject.Handle > 0 && cs != app.currentSelections { 523 sessionState.DeRegisterEvent(app.currentSelections.enigmaObject.Handle) 524 } 525 app.currentSelections = cs 526 } 527 528 func (app *App) setFieldList(SessionState SessionState, fl *FieldList) { 529 app.mutex.Lock() 530 defer app.mutex.Unlock() 531 if app.fieldlist != nil && app.fieldlist.enigmaObject != nil && app.fieldlist.enigmaObject.Handle > 0 && fl != app.fieldlist { 532 SessionState.DeRegisterEvent(app.fieldlist.enigmaObject.Handle) 533 } 534 app.fieldlist = fl 535 } 536 537 func (app *App) setDimensionList(SessionState SessionState, dl *DimensionList) { 538 app.mutex.Lock() 539 defer app.mutex.Unlock() 540 if app.dimensionList != nil && app.dimensionList.enigmaObject != nil && app.dimensionList.enigmaObject.Handle > 0 && dl != app.dimensionList { 541 SessionState.DeRegisterEvent(app.dimensionList.enigmaObject.Handle) 542 } 543 app.dimensionList = dl 544 }