github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/dashboards/dashboards.go (about)

     1  /*
     2  Copyright 2023.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package dashboards
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  
    28  	"github.com/google/uuid"
    29  	"github.com/valyala/fasthttp"
    30  
    31  	"github.com/siglens/siglens/pkg/blob"
    32  	"github.com/siglens/siglens/pkg/config"
    33  	"github.com/siglens/siglens/pkg/utils"
    34  	log "github.com/sirupsen/logrus"
    35  )
    36  
    37  var allidsBaseFname string
    38  var allDashIdsLock map[uint64]*sync.Mutex = make(map[uint64]*sync.Mutex)
    39  var latestDashboardReadTimeMillis map[uint64]uint64
    40  
    41  // map of "orgid" => "dashboardId" ==> "dashboardName"
    42  // e.g. "1234567890" => "11812083241622924684" => "dashboard-1"
    43  var allDashboardsIds map[uint64]map[string]string = make(map[uint64]map[string]string)
    44  var allDashboardsIdsLock *sync.RWMutex = &sync.RWMutex{}
    45  
    46  func readSavedDashboards(orgid uint64) ([]byte, error) {
    47  	var dashboardData []byte
    48  	allidsFname := getDashboardFileName(orgid)
    49  	_, err := os.Stat(allidsFname)
    50  	if err != nil {
    51  		if errors.Is(err, os.ErrNotExist) {
    52  			return nil, nil
    53  		}
    54  	}
    55  
    56  	dashboardData, err = os.ReadFile(allidsFname)
    57  	if err != nil {
    58  		if errors.Is(err, os.ErrNotExist) {
    59  			return nil, nil
    60  		}
    61  		log.Errorf("readSavedDashboards: Failed to read dashboard file fname=%v, err=%v", allidsFname, err)
    62  		return nil, err
    63  	}
    64  
    65  	allDashboardsIdsLock.Lock()
    66  	if _, ok := allDashboardsIds[orgid]; !ok {
    67  		allDashboardsIds[orgid] = make(map[string]string)
    68  	}
    69  	var dashboardDetails map[string]string
    70  	err = json.Unmarshal(dashboardData, &dashboardDetails)
    71  	if err != nil {
    72  		allDashboardsIdsLock.Unlock()
    73  		log.Errorf("readSavedDashboards: Failed to unmarshall dashboard file fname=%v, err=%v", allidsFname, err)
    74  		return nil, err
    75  	}
    76  	allDashboardsIds[orgid] = dashboardDetails
    77  	latestDashboardReadTimeMillis[orgid] = utils.GetCurrentTimeInMs()
    78  	allDashboardsIdsLock.Unlock()
    79  	return dashboardData, nil
    80  }
    81  
    82  func readSavedDefaultDashboards(orgid uint64) ([]byte, error) {
    83  	var dashboardData []byte
    84  	allidsFname := getDefaultDashboardFileName()
    85  	_, err := os.Stat(allidsFname)
    86  	if err != nil {
    87  		if errors.Is(err, os.ErrNotExist) {
    88  			return nil, nil
    89  		}
    90  		log.Errorf("readSavedDashboards: Failed to stat dashboard file fname=%v, err=%v", allidsFname, err)
    91  		return nil, err
    92  	}
    93  
    94  	dashboardData, err = os.ReadFile(allidsFname)
    95  	if err != nil {
    96  		if errors.Is(err, os.ErrNotExist) {
    97  			return nil, nil
    98  		}
    99  		log.Errorf("readSavedDashboards: Failed to read dashboard file fname=%v, err=%v", allidsFname, err)
   100  		return nil, err
   101  	}
   102  
   103  	allDashboardsIdsLock.Lock()
   104  	if _, ok := allDashboardsIds[orgid]; !ok {
   105  		allDashboardsIds[orgid] = make(map[string]string)
   106  	}
   107  	var dashboardDetails map[string]string
   108  	err = json.Unmarshal(dashboardData, &dashboardDetails)
   109  	if err != nil {
   110  		allDashboardsIdsLock.Unlock()
   111  		log.Errorf("readSavedDashboards: Failed to unmarshall dashboard file fname=%v, err=%v", allidsFname, err)
   112  		return nil, err
   113  	}
   114  	allDashboardsIds[orgid] = dashboardDetails
   115  	latestDashboardReadTimeMillis[orgid] = utils.GetCurrentTimeInMs()
   116  	allDashboardsIdsLock.Unlock()
   117  	return dashboardData, nil
   118  }
   119  
   120  func getDashboardFileName(orgid uint64) string {
   121  	var allidsFname string
   122  	if orgid == 0 {
   123  		allidsFname = allidsBaseFname + ".json"
   124  	} else {
   125  		allidsFname = allidsBaseFname + "-" + strconv.FormatUint(orgid, 10) + ".json"
   126  	}
   127  	return allidsFname
   128  }
   129  
   130  func getDefaultDashboardFileName() string {
   131  	var allidsFname string
   132  	var defaultDBsAllidsBaseFname string = "defaultDBs/allids"
   133  	allidsFname = defaultDBsAllidsBaseFname + ".json"
   134  	return allidsFname
   135  }
   136  
   137  func InitDashboards() error {
   138  	var sb strings.Builder
   139  
   140  	sb.WriteString(config.GetDataPath() + "querynodes/" + config.GetHostID() + "/dashboards")
   141  	baseDir := sb.String()
   142  	allidsBaseFname = baseDir + "/allids"
   143  	latestDashboardReadTimeMillis = make(map[uint64]uint64)
   144  
   145  	err := os.MkdirAll(baseDir, 0764)
   146  	if err != nil {
   147  		log.Errorf("InitDashboard: failed to create basedir=%v, err=%v", baseDir, err)
   148  		return err
   149  	}
   150  
   151  	err = os.MkdirAll(baseDir+"/details", 0764)
   152  	if err != nil {
   153  		log.Errorf("InitDashboard: failed to create basedir=%v, err=%v", baseDir, err)
   154  		return err
   155  	}
   156  
   157  	createOrAcquireLock(0)
   158  	_, err = readSavedDashboards(0)
   159  	if err != nil {
   160  		releaseLock(0)
   161  		log.Errorf("InitDashboard: failed to read saved dashboards, err=%v", err)
   162  		return err
   163  	}
   164  	releaseLock(0)
   165  
   166  	return nil
   167  }
   168  
   169  func createOrAcquireLock(orgid uint64) {
   170  	if _, ok := allDashIdsLock[orgid]; !ok {
   171  		allDashIdsLock[orgid] = &sync.Mutex{}
   172  	}
   173  	allDashIdsLock[orgid].Lock()
   174  }
   175  
   176  func releaseLock(orgid uint64) {
   177  	allDashIdsLock[orgid].Unlock()
   178  }
   179  
   180  func getAllDashboardIds(orgid uint64) (map[string]string, error) {
   181  	createOrAcquireLock(orgid)
   182  	_, err := readSavedDashboards(orgid)
   183  	if err != nil {
   184  		releaseLock(orgid)
   185  		log.Errorf("getAllDashboardIds: failed to read, err=%v", err)
   186  		return nil, err
   187  	}
   188  	releaseLock(orgid)
   189  	allDashboardsIdsLock.RLock()
   190  	defer allDashboardsIdsLock.RUnlock()
   191  	return allDashboardsIds[orgid], nil
   192  }
   193  
   194  func getAllDefaultDashboardIds(orgid uint64) (map[string]string, error) {
   195  	createOrAcquireLock(orgid)
   196  	_, err := readSavedDefaultDashboards(orgid)
   197  	if err != nil {
   198  		releaseLock(orgid)
   199  		log.Errorf("getAllDashboardIds: failed to read, err=%v", err)
   200  		return nil, err
   201  	}
   202  	releaseLock(orgid)
   203  	allDashboardsIdsLock.RLock()
   204  	defer allDashboardsIdsLock.RUnlock()
   205  	return allDashboardsIds[orgid], nil
   206  }
   207  
   208  // Generate the uniq uuid for the dashboard
   209  func createUniqId(dname string) string {
   210  	newId := uuid.New().String()
   211  	return newId
   212  }
   213  
   214  // method to check if the dashboard name already exists
   215  func dashboardNameExists(dname string, orgid uint64) bool {
   216  	allDashboardIds, err := getAllDashboardIds(orgid)
   217  	if err != nil {
   218  		log.Errorf("Error getting all dashboard IDs: %v", err)
   219  		return false
   220  	}
   221  	for _, name := range allDashboardIds {
   222  		if name == dname {
   223  			return true
   224  		}
   225  	}
   226  	return false
   227  }
   228  
   229  func createDashboard(dname string, orgid uint64) (map[string]string, error) {
   230  	if dname == "" {
   231  		log.Errorf("createDashboard: failed to create Dashboard, with empty dashboard name")
   232  		return nil, errors.New("createDashboard: failed to create Dashboard, with empty dashboard name")
   233  	}
   234  
   235  	newId := createUniqId(dname)
   236  
   237  	if dashboardNameExists(dname, orgid) {
   238  		log.Errorf("Dashboard with name %s already exists", dname)
   239  		return nil, errors.New("dashboard name already exists")
   240  	}
   241  
   242  	dashBoardIds, err := getAllDashboardIds(orgid)
   243  	if err != nil {
   244  		log.Errorf("createDashboard: Failed to get all dashboard ids err=%v", err)
   245  		return nil, err
   246  	}
   247  	for _, dId := range dashBoardIds {
   248  		if dId == newId {
   249  			log.Errorf("createDashboard: Failed to create dashboard, dashboard id already exists=%v", dname)
   250  			return nil, errors.New("createDashboard: Failed to create dashboard, dashboard id already exists")
   251  		}
   252  	}
   253  
   254  	allDashboardsIdsLock.Lock()
   255  	if _, ok := allDashboardsIds[orgid]; !ok {
   256  		allDashboardsIds[orgid] = make(map[string]string)
   257  	}
   258  	allDashboardsIds[orgid][newId] = dname
   259  	orgDashboards := allDashboardsIds[orgid]
   260  	jdata, err := json.Marshal(&orgDashboards)
   261  	allDashboardsIdsLock.Unlock()
   262  	if err != nil {
   263  		log.Errorf("createDashboard: Failed to marshall err=%v", err)
   264  		return nil, err
   265  	}
   266  
   267  	allidsFname := getDashboardFileName(orgid)
   268  	err = os.WriteFile(allidsFname, jdata, 0644)
   269  	if err != nil {
   270  		log.Errorf("createDashboard: Failed to write file=%v, err=%v", allidsFname, err)
   271  		return nil, err
   272  	}
   273  
   274  	dashboardDetailsFname := config.GetDataPath() + "querynodes/" + config.GetHostID() + "/dashboards/details/" + newId + ".json"
   275  
   276  	err = os.WriteFile(dashboardDetailsFname, []byte("{}"), 0644)
   277  	if err != nil {
   278  		log.Errorf("createDashboard: Error creating empty local file %s: %v", dashboardDetailsFname, err)
   279  		return nil, err
   280  	}
   281  
   282  	log.Infof("createDashboard: Successfully created file %v", dashboardDetailsFname)
   283  	err = blob.UploadQueryNodeDir()
   284  	if err != nil {
   285  		log.Errorf("createDashboard: Failed to upload query nodes dir  err=%v", err)
   286  		return nil, err
   287  	}
   288  
   289  	retval := make(map[string]string)
   290  	allDashboardsIdsLock.RLock()
   291  	orgDashboardsIds := allDashboardsIds[orgid]
   292  	allDashboardsIdsLock.RUnlock()
   293  	for k, v := range orgDashboardsIds {
   294  		if k == newId {
   295  			retval[k] = v
   296  			break
   297  		}
   298  	}
   299  
   300  	return retval, nil
   301  }
   302  
   303  func dashboardIsDefault(id string) bool {
   304  	defaultIds := []string{
   305  		"10329b95-47a8-48df-8b1d-0a0a01ec6c42",
   306  		"a28f485c-4747-4024-bb6b-d230f101f852",
   307  		"bd74f11e-26c8-4827-bf65-c0b464e1f2a4",
   308  		"53cb3dde-fd78-4253-808c-18e4077ef0f1",
   309  	}
   310  
   311  	for _, defaultId := range defaultIds {
   312  		if id == defaultId {
   313  			return true
   314  		}
   315  	}
   316  
   317  	return false
   318  }
   319  
   320  func toggleFavorite(id string) (bool, error) {
   321  	// Load the dashboard JSON file
   322  	var dashboardDetailsFname string
   323  	if dashboardIsDefault(id) {
   324  		dashboardDetailsFname = "defaultDBs/details/" + id + ".json"
   325  	} else {
   326  		dashboardDetailsFname = config.GetDataPath() + "querynodes/" + config.GetHostID() + "/dashboards/details/" + id + ".json"
   327  	}
   328  	dashboardJson, err := os.ReadFile(dashboardDetailsFname)
   329  	if err != nil {
   330  		log.Errorf("toggleFavorite: Failed to read file=%v, err=%v", dashboardDetailsFname, err)
   331  		return false, err
   332  	}
   333  
   334  	// Unmarshal JSON file into a map
   335  	var dashboard map[string]interface{}
   336  	err = json.Unmarshal(dashboardJson, &dashboard)
   337  	if err != nil {
   338  		log.Errorf("toggleFavorite: Failed to unmarshal json, err=%v", err)
   339  		return false, err
   340  	}
   341  
   342  	// Toggle the "isFavorite" field
   343  	isFavorite, ok := dashboard["isFavorite"].(bool)
   344  	if !ok {
   345  		// If the "isFavorite" field does not exist or is not a bool, treat the dashboard as not favorited
   346  		isFavorite = false
   347  	}
   348  	dashboard["isFavorite"] = !isFavorite
   349  
   350  	// Marshal the updated dashboard back into JSON
   351  	updatedDashboardJson, err := json.Marshal(dashboard)
   352  	if err != nil {
   353  		log.Errorf("toggleFavorite: Failed to marshal json, err=%v", err)
   354  		return false, err
   355  	}
   356  
   357  	// Save the updated dashboard back to the JSON file
   358  	err = os.WriteFile(dashboardDetailsFname, updatedDashboardJson, 0644)
   359  	if err != nil {
   360  		log.Errorf("toggleFavorite: Failed to write file=%v, err=%v", dashboardDetailsFname, err)
   361  		return false, err
   362  	}
   363  
   364  	return !isFavorite, nil
   365  }
   366  func getDashboard(id string) (map[string]interface{}, error) {
   367  	var dashboardDetailsFname string
   368  	if dashboardIsDefault(id) {
   369  		dashboardDetailsFname = "defaultDBs/details/" + id + ".json"
   370  	} else {
   371  		dashboardDetailsFname = config.GetDataPath() + "querynodes/" + config.GetHostID() + "/dashboards/details/" + id + ".json"
   372  	}
   373  	rdata, err := os.ReadFile(dashboardDetailsFname)
   374  	if err != nil {
   375  		if errors.Is(err, os.ErrNotExist) {
   376  			return nil, err
   377  		}
   378  		log.Errorf("getDashboard: Failed to read dashboard file fname=%v, err=%v", dashboardDetailsFname, err)
   379  		return nil, err
   380  	}
   381  
   382  	var detailDashboardInfo map[string]interface{} = make(map[string]interface{})
   383  
   384  	err = json.Unmarshal(rdata, &detailDashboardInfo)
   385  	if err != nil {
   386  		log.Errorf("getDashboard: Failed to unmarshall dashboard file fname=%v, err=%v", dashboardDetailsFname, err)
   387  		return nil, err
   388  	}
   389  
   390  	retval := detailDashboardInfo
   391  	return retval, nil
   392  }
   393  
   394  func updateDashboard(id string, dName string, dashboardDetails map[string]interface{}, orgid uint64) error {
   395  
   396  	// Check if the dashboard exists
   397  	allDashboards, err := getAllDashboardIds(orgid)
   398  	if err != nil {
   399  		log.Errorf("updateDashboard: Failed to get all dashboard ids err=%v", err)
   400  		return err
   401  	}
   402  	_, ok := allDashboards[id]
   403  	if !ok {
   404  		log.Errorf("updateDashboard: Dashboard id %v does not exist", id)
   405  		return errors.New("updateDashboard: Dashboard id does not exist")
   406  	}
   407  
   408  	currentDashboardDetails, err := getDashboard(id)
   409  	if err != nil {
   410  		log.Errorf("ProcessGetDashboardRequest: could not get Dashboard=%v, err=%v", id, err)
   411  		return errors.New("updateDashboard: Error fetching dashboard details")
   412  	}
   413  
   414  	// Check if isFavorite is provided in the update
   415  	if _, exists := currentDashboardDetails["isFavorite"]; !exists {
   416  		// If isFavorite does not exist in currentDashboardDetails, set it to false
   417  		dashboardDetails["isFavorite"] = false
   418  		currentDashboardDetails["isFavorite"] = false
   419  	} else if _, ok := dashboardDetails["isFavorite"].(bool); !ok {
   420  		// If isFavorite is not provided in the update, keep the current value
   421  		dashboardDetails["isFavorite"] = currentDashboardDetails["isFavorite"]
   422  	}
   423  	// Update the dashboard name if it is different
   424  	if allDashboards[id] != dName {
   425  		if dashboardNameExists(dName, orgid) {
   426  			log.Errorf("Dashboard with name %s already exists", dName)
   427  			return errors.New("dashboard name already exists")
   428  		} else {
   429  			allDashboardsIds[orgid][id] = dName
   430  		}
   431  	}
   432  	allDashboardsIdsLock.RLock()
   433  	orgDashboards := allDashboardsIds[orgid]
   434  	allDashboardsIdsLock.RUnlock()
   435  	jdata, err := json.Marshal(&orgDashboards)
   436  	if err != nil {
   437  		log.Errorf("updateDashboard: Failed to marshall err=%v", err)
   438  		return err
   439  	}
   440  
   441  	allidsFname := getDashboardFileName(orgid)
   442  	err = os.WriteFile(allidsFname, jdata, 0644)
   443  	if err != nil {
   444  		log.Errorf("updateDashboard: Failed to write file=%v, err=%v", allidsFname, err)
   445  		return err
   446  	}
   447  
   448  	dashboardDetailsFname := config.GetDataPath() + "querynodes/" + config.GetHostID() + "/dashboards/details/" + id + ".json"
   449  
   450  	jdata, err = json.Marshal(&dashboardDetails)
   451  	if err != nil {
   452  		log.Errorf("updateDashboard: Failed to marshall err=%v", err)
   453  		return err
   454  	}
   455  
   456  	err = os.WriteFile(dashboardDetailsFname, jdata, 0644)
   457  	if err != nil {
   458  		log.Errorf("updateDashboard: Failed to writefile fullname=%v, err=%v", dashboardDetailsFname, err)
   459  		return err
   460  	}
   461  	log.Infof("updateDashboard: Successfully updated dashboard details in file %v", dashboardDetailsFname)
   462  
   463  	// Update the query node dir
   464  	err = blob.UploadQueryNodeDir()
   465  	if err != nil {
   466  		log.Errorf("updateDashboard: Failed to upload query nodes dir  err=%v", err)
   467  		return err
   468  	}
   469  
   470  	return nil
   471  }
   472  
   473  func deleteDashboard(id string, orgid uint64) error {
   474  
   475  	createOrAcquireLock(orgid)
   476  	dashboardData, err := readSavedDashboards(orgid)
   477  	if err != nil {
   478  		releaseLock(orgid)
   479  		log.Errorf("deleteDashboard: failed to read saved dashboards, err=%v", err)
   480  		return err
   481  	}
   482  	releaseLock(orgid)
   483  
   484  	var dashboardDetails map[string]string
   485  	err = json.Unmarshal(dashboardData, &dashboardDetails)
   486  	if err != nil {
   487  		log.Errorf("deleteDashboard: Failed to unmarshall dashboard file for orgid=%v, err=%v", orgid, err)
   488  		return err
   489  	}
   490  
   491  	// Delete entry from dashboardInfo and write to file allids.json
   492  	allDashboardsIdsLock.Lock()
   493  	delete(allDashboardsIds[orgid], id)
   494  	allDashboardsIdsLock.Unlock()
   495  
   496  	// Update the file with latest dashboard info
   497  	allDashboardsIdsLock.RLock()
   498  	orgDashboardIds := allDashboardsIds[orgid]
   499  	allDashboardsIdsLock.RUnlock()
   500  	jdata, err := json.Marshal(&orgDashboardIds)
   501  	if err != nil {
   502  		log.Errorf("deleteDashboard: Failed to marshall err=%v", err)
   503  		return err
   504  	}
   505  
   506  	allidsFname := getDashboardFileName(orgid)
   507  	err = os.WriteFile(allidsFname, jdata, 0644)
   508  	if err != nil {
   509  		log.Errorf("deleteDashboard: Failed to write file=%v, err=%v", allidsFname, err)
   510  		return err
   511  	}
   512  
   513  	// Delete dashboard details file
   514  	dashboardDetailsFname := config.GetDataPath() + "querynodes/" + config.GetHostID() + "/dashboards/details/" + id + ".json"
   515  	err = os.Remove(dashboardDetailsFname)
   516  	if err != nil {
   517  		log.Errorf("deleteDashboard:  Error deleting file %s: %v", dashboardDetailsFname, err)
   518  		return err
   519  	}
   520  
   521  	// Update the query node dir
   522  	err = blob.UploadQueryNodeDir()
   523  	if err != nil {
   524  		log.Errorf("deleteDashboard: Failed to upload query nodes dir  err=%v", err)
   525  		return err
   526  	}
   527  
   528  	return nil
   529  }
   530  
   531  // method to set conflict message and 409 status code
   532  func setConflictMsg(ctx *fasthttp.RequestCtx) {
   533  	var httpResp utils.HttpServerResponse
   534  	ctx.SetStatusCode(fasthttp.StatusConflict)
   535  	httpResp.Message = "Conflict: Dashboard name already exists"
   536  	httpResp.StatusCode = fasthttp.StatusConflict
   537  	utils.WriteResponse(ctx, httpResp)
   538  }
   539  
   540  func ProcessCreateDashboardRequest(ctx *fasthttp.RequestCtx, myid uint64) {
   541  	rawJSON := ctx.PostBody()
   542  	if rawJSON == nil {
   543  		log.Errorf("ProcessCreateDashboardRequest: received empty user query")
   544  		utils.SetBadMsg(ctx, "")
   545  		return
   546  	}
   547  
   548  	var dname string
   549  
   550  	err := json.Unmarshal(rawJSON, &dname)
   551  	if err != nil {
   552  		log.Errorf("ProcessCreateDashboardRequest: could not unmarshall user query, err=%v", err)
   553  		utils.SetBadMsg(ctx, "")
   554  		return
   555  	}
   556  	dashboardInfo, err := createDashboard(dname, myid)
   557  
   558  	if err != nil {
   559  		if err.Error() == "dashboard name already exists" {
   560  			setConflictMsg(ctx)
   561  			return
   562  		} else {
   563  			log.Errorf("ProcessCreateDashboardRequest: could not create Dashboard=%v, err=%v", dname, err)
   564  			utils.SetBadMsg(ctx, "")
   565  			return
   566  		}
   567  	}
   568  
   569  	utils.WriteJsonResponse(ctx, dashboardInfo)
   570  	ctx.SetStatusCode(fasthttp.StatusOK)
   571  }
   572  
   573  func ProcessFavoriteRequest(ctx *fasthttp.RequestCtx) {
   574  	dId := utils.ExtractParamAsString(ctx.UserValue("dashboard-id"))
   575  	if dId == "" {
   576  		log.Errorf("ProcessFavoriteRequest: received empty dashboard id")
   577  		utils.SetBadMsg(ctx, "")
   578  		return
   579  	}
   580  
   581  	isFavorite, err := toggleFavorite(dId)
   582  	if err != nil {
   583  		log.Errorf("ProcessFavoriteRequest: could not toggle favorite status for Dashboard=%v, err=%v", dId, err)
   584  		utils.SetBadMsg(ctx, "")
   585  		return
   586  	}
   587  
   588  	response := map[string]bool{"isFavorite": isFavorite}
   589  	utils.WriteJsonResponse(ctx, response)
   590  	ctx.SetStatusCode(fasthttp.StatusOK)
   591  }
   592  
   593  func ProcessListFavoritesRequest(ctx *fasthttp.RequestCtx, myid uint64) {
   594  	dIds, err := getAllFavoriteDashboardIds(myid)
   595  
   596  	if err != nil {
   597  		log.Errorf("ProcessListFavoritesRequest: could not get favorite dashboard ids, err=%v", err)
   598  		utils.SetBadMsg(ctx, "")
   599  		return
   600  	}
   601  	utils.WriteJsonResponse(ctx, dIds)
   602  	ctx.SetStatusCode(fasthttp.StatusOK)
   603  }
   604  
   605  func getAllFavoriteDashboardIds(orgId uint64) (map[string]string, error) {
   606  	allDashboards, err := getAllDashboardIds(orgId)
   607  	if err != nil {
   608  		return nil, err
   609  	}
   610  
   611  	favoriteDashboards := make(map[string]string)
   612  	for id, name := range allDashboards {
   613  		isFavorite, err := isDashboardFavorite(id)
   614  		if err != nil {
   615  			return nil, err
   616  		}
   617  
   618  		if isFavorite {
   619  			favoriteDashboards[id] = name
   620  		}
   621  	}
   622  
   623  	return favoriteDashboards, nil
   624  }
   625  
   626  func isDashboardFavorite(id string) (bool, error) {
   627  	var dashboardDetailsFname string
   628  
   629  	if dashboardIsDefault(id) {
   630  		dashboardDetailsFname = "defaultDBs/details/" + id + ".json"
   631  	} else {
   632  		dashboardDetailsFname = config.GetDataPath() + "querynodes/" + config.GetHostID() + "/dashboards/details/" + id + ".json"
   633  	}
   634  
   635  	dashboardJson, err := os.ReadFile(dashboardDetailsFname)
   636  	if err != nil {
   637  		return false, err
   638  	}
   639  
   640  	var dashboard map[string]interface{}
   641  	err = json.Unmarshal(dashboardJson, &dashboard)
   642  	if err != nil {
   643  		log.Errorf("isDashboardFavorite: Failed to unmarshal json, err=%v", err)
   644  		return false, err
   645  	}
   646  
   647  	isFav, ok := dashboard["isFavorite"].(bool)
   648  	if !ok {
   649  		isFav = false
   650  	}
   651  
   652  	return isFav, nil
   653  }
   654  
   655  func ProcessListAllRequest(ctx *fasthttp.RequestCtx, myid uint64) {
   656  	dIds, err := getAllDashboardIds(myid)
   657  
   658  	if err != nil {
   659  		log.Errorf("ProcessListAllRequest: could not get dashboard ids, err=%v", err)
   660  		utils.SetBadMsg(ctx, "")
   661  		return
   662  	}
   663  	utils.WriteJsonResponse(ctx, dIds)
   664  	ctx.SetStatusCode(fasthttp.StatusOK)
   665  }
   666  
   667  func ProcessListAllDefaultDBRequest(ctx *fasthttp.RequestCtx, myid uint64) {
   668  	dIds, err := getAllDefaultDashboardIds(myid)
   669  
   670  	if err != nil {
   671  		log.Errorf("ProcessListAllRequest: could not get dashboard ids, err=%v", err)
   672  		utils.SetBadMsg(ctx, "")
   673  		return
   674  	}
   675  	utils.WriteJsonResponse(ctx, dIds)
   676  	ctx.SetStatusCode(fasthttp.StatusOK)
   677  }
   678  
   679  func ProcessUpdateDashboardRequest(ctx *fasthttp.RequestCtx, myid uint64) {
   680  	rawJSON := ctx.PostBody()
   681  	if rawJSON == nil {
   682  		log.Errorf("ProcessCreateDashboardRequest: received empty user query")
   683  		utils.SetBadMsg(ctx, "")
   684  		return
   685  	}
   686  
   687  	readJSON := make(map[string]interface{})
   688  
   689  	err := json.Unmarshal(rawJSON, &readJSON)
   690  	if err != nil {
   691  		log.Errorf("ProcessCreateDashboardRequest: could not unmarshall user query, err=%v", err)
   692  		utils.SetBadMsg(ctx, "")
   693  		return
   694  	}
   695  
   696  	dId := readJSON["id"].(string)
   697  	dName := readJSON["name"].(string)
   698  	dashboardDetails := readJSON["details"].(map[string]interface{})
   699  	err = updateDashboard(dId, dName, dashboardDetails, myid)
   700  	if err != nil {
   701  		if err.Error() == "dashboard name already exists" {
   702  			setConflictMsg(ctx)
   703  			return
   704  		} else {
   705  			log.Errorf("ProcessCreateDashboardRequest: could not create Dashboard=%v, err=%v", dId, err)
   706  			utils.SetBadMsg(ctx, "")
   707  			return
   708  		}
   709  	}
   710  	response := "Dashboard updated successfully"
   711  	utils.WriteJsonResponse(ctx, response)
   712  	ctx.SetStatusCode(fasthttp.StatusOK)
   713  }
   714  
   715  func ProcessGetDashboardRequest(ctx *fasthttp.RequestCtx) {
   716  	dId := utils.ExtractParamAsString(ctx.UserValue("dashboard-id"))
   717  	dashboardDetails, err := getDashboard(dId)
   718  	if err != nil {
   719  		log.Errorf("ProcessGetDashboardRequest: could not get Dashboard=%v, err=%v", dId, err)
   720  		utils.SetBadMsg(ctx, "")
   721  		return
   722  	}
   723  	utils.WriteJsonResponse(ctx, dashboardDetails)
   724  	ctx.SetStatusCode(fasthttp.StatusOK)
   725  }
   726  
   727  func ProcessDeleteDashboardRequest(ctx *fasthttp.RequestCtx, myid uint64) {
   728  	dId := utils.ExtractParamAsString(ctx.UserValue("dashboard-id"))
   729  	err := deleteDashboard(dId, myid)
   730  	if err != nil {
   731  		log.Errorf("ProcessDeleteDashboardRequest: Failed to delete dashboard=%v, err=%v", dId, err)
   732  		utils.SetBadMsg(ctx, "")
   733  		return
   734  	}
   735  
   736  	log.Infof("ProcessDeleteDashboardRequest: Successfully deleted dashboard %v", dId)
   737  	err = blob.UploadQueryNodeDir()
   738  	if err != nil {
   739  		log.Errorf("ProcessDeleteDashboardRequest: Failed to upload query nodes dir  err=%v", err)
   740  		return
   741  	}
   742  	response := "Dashboard deleted successfully"
   743  	utils.WriteJsonResponse(ctx, response)
   744  	ctx.SetStatusCode(fasthttp.StatusOK)
   745  }
   746  
   747  func ProcessDeleteDashboardsByOrgId(orgid uint64) error {
   748  	dIds, err := getAllDashboardIds(orgid)
   749  	if err != nil {
   750  		log.Errorf("ProcessDeleteDashboardsByOrgId: Failed to get all dashboard ids err=%v", err)
   751  		return err
   752  	}
   753  	for dId := range dIds {
   754  		err = deleteDashboard(dId, orgid)
   755  		if err != nil {
   756  			log.Errorf("ProcessDeleteDashboardsByOrgId: Failed to delete dashboard=%v, err=%v", dId, err)
   757  		}
   758  
   759  		log.Infof("ProcessDeleteDashboardsByOrgId: Successfully deleted dashboard %v", dId)
   760  		err = blob.UploadQueryNodeDir()
   761  		if err != nil {
   762  			log.Errorf("ProcessDeleteDashboardsByOrgId: Failed to upload query nodes dir, err=%v", err)
   763  			// Move on to the next dashboard for now
   764  		}
   765  	}
   766  
   767  	dashboardAllIdsFilename := config.GetDataPath() + "querynodes/" + config.GetHostname() + "/dashboards/allids-" + fmt.Sprint(orgid) + ".json"
   768  
   769  	err = os.Remove(dashboardAllIdsFilename)
   770  	if err != nil {
   771  		log.Warnf("ProcessDeleteDashboardsByOrgId: Failed to delete the dashboard allids file: %v", dashboardAllIdsFilename)
   772  	}
   773  	return nil
   774  }