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  }