From 974203240552dfe8a19fcde2c687bdf707aa4857 Mon Sep 17 00:00:00 2001 From: blacktop Date: Thu, 7 Sep 2023 21:29:34 -0600 Subject: [PATCH] fix: failure when config contains apps that aren't installed --- go.mod | 2 +- internal/command/command.go | 18 ++--- internal/database/database.go | 127 ++++++++++++++++++++-------------- 3 files changed, 82 insertions(+), 65 deletions(-) diff --git a/go.mod b/go.mod index de71836..05399c1 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.7.0 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 gopkg.in/yaml.v3 v3.0.1 gorm.io/gorm v1.25.4 howett.net/plist v1.0.0 @@ -29,7 +30,6 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.8.4 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/term v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/internal/command/command.go b/internal/command/command.go index bdeb056..a866f78 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -241,7 +241,7 @@ func DefaultOrg(c *Config) (err error) { // Create default config file var apps []database.App var categories []database.Category - var dbconf database.Config + var config database.Config page := database.Page{Number: 1} @@ -265,7 +265,7 @@ func DefaultOrg(c *Config) (err error) { page.Items = append(page.Items, folder) } - dbconf.Apps.Pages = append(dbconf.Apps.Pages, page) + config.Apps.Pages = append(config.Apps.Pages, page) //////////////////////////////////////////////////////////////////// // Place Widgets /////////////////////////////////////////////////// @@ -283,14 +283,12 @@ func DefaultOrg(c *Config) (err error) { ///////////////////////////////////////////////////////////////////// // Place Apps /////////////////////////////////////////////////////// - utils.Indent(log.Info)("creating App folders and adding apps to them") - missing, err := lpad.GetMissing(dbconf.Apps, database.ApplicationType) - if err != nil { + if err := lpad.GetMissing(&config.Apps, database.ApplicationType); err != nil { return fmt.Errorf("failed to GetMissing=>Apps: %v", err) } - dbconf.Apps.Pages = parseMissing(missing, dbconf.Apps.Pages) - groupID, err = lpad.ApplyConfig(dbconf.Apps, database.ApplicationType, groupID, 1) + utils.Indent(log.Info)("creating App folders and adding apps to them") + groupID, err = lpad.ApplyConfig(config.Apps, database.ApplicationType, groupID, 1) if err != nil { return fmt.Errorf("failed to ApplyConfig: %v", err) } @@ -540,13 +538,11 @@ func LoadConfig(c *Config) error { ///////////////////////////////////////////////////////////////////// // Place Apps /////////////////////////////////////////////////////// - utils.Indent(log.Info)("creating App folders and adding apps to them") - missing, err := lpad.GetMissing(config.Apps, database.ApplicationType) - if err != nil { + if err := lpad.GetMissing(&config.Apps, database.ApplicationType); err != nil { return fmt.Errorf("failed to GetMissing=>Apps: %v", err) } - config.Apps.Pages = parseMissing(missing, config.Apps.Pages) + utils.Indent(log.Info)("creating App folders and adding apps to them") groupID, err = lpad.ApplyConfig(config.Apps, database.ApplicationType, groupID, 1) if err != nil { return fmt.Errorf("failed to ApplyConfig=>Apps: %v", err) diff --git a/internal/database/database.go b/internal/database/database.go index a93781d..4743a04 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -2,81 +2,113 @@ package database import ( - "crypto/rand" "fmt" - "io" "sort" "github.com/apex/log" "github.com/blacktop/lporg/internal/utils" + "github.com/google/uuid" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" + "golang.org/x/exp/slices" "gorm.io/gorm" ) // GetMissing returns a list of the rest of the apps not in the config -func (lp *LaunchPad) GetMissing(apps Apps, appType int) ([]string, error) { +func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { - missing := []string{} - appsFromConfig := []string{} + var ( + dbApps []string + configApps []string + ) + + // get all apps from database + switch appType { + case ApplicationType: + var apps []App + err := lp.DB.Table("apps"). + Select("apps.item_id, apps.title"). + Joins("left join items on items.rowid = apps.item_id"). + Not("items.parent_id = ?", 6). + Scan(&apps).Error + if err != nil { + return fmt.Errorf("query all apps failed: %w", err) + } + for _, app := range apps { + dbApps = append(dbApps, app.Title) + } + default: + return fmt.Errorf("GetMissing: unsupported app type: %d", appType) + } + + sort.Strings(dbApps) // get all apps from config file for _, page := range apps.Pages { for _, item := range page.Items { switch item.(type) { case string: - appsFromConfig = append(appsFromConfig, item.(string)) + configApps = append(configApps, item.(string)) default: var folder AppFolder if err := mapstructure.Decode(item, &folder); err != nil { - return nil, errors.Wrap(err, "mapstructure unable to decode config folder") + return fmt.Errorf("mapstructure unable to decode config folder: %w", err) } - for _, fpage := range folder.Pages { for _, fitem := range fpage.Items { - appsFromConfig = append(appsFromConfig, fitem) + configApps = append(configApps, fitem) } } } } } - switch appType { - case ApplicationType: - var apps []App - err := lp.DB.Table("apps"). - Select("apps.item_id, apps.title"). - Joins("left join items on items.rowid = apps.item_id"). - Not("items.parent_id = ?", 6). - Scan(&apps).Error - if err != nil { - return nil, err - } - for _, app := range apps { - if !utils.StringInSlice(app.Title, appsFromConfig) { - missing = utils.AppendIfMissing(missing, app.Title) - } - } - case WidgetType: - var widgets []Widget - err := lp.DB.Table("widgets").Select("widgets.item_id, widgets.title").Joins("left join items on items.rowid = widgets.item_id").Scan(&widgets).Error - if err != nil { - return nil, err - } - for _, widget := range widgets { - if !utils.StringInSlice(widget.Title, appsFromConfig) { - missing = utils.AppendIfMissing(missing, widget.Title) + sort.Strings(configApps) + + for _, app := range dbApps { + if !slices.Contains(configApps, app) { + utils.DoubleIndent(log.WithField("app", app).Warn)("found installed apps that are not in supplied config") + if len(apps.Pages[len(apps.Pages)-1].Items) < 35 { + apps.Pages[len(apps.Pages)-1].Items = append(apps.Pages[len(apps.Pages)-1].Items, app) + } else { + newPage := Page{ + Number: len(apps.Pages) + 1, + Items: []any{app}, + } + apps.Pages = append(apps.Pages, newPage) } } } - sort.Strings(missing) - - if len(missing) > 0 { - utils.DoubleIndent(log.WithField("count", len(missing)).Info)("found apps/widgets that are not in supplied config") + // check all apps from config file exist on system + for idx, page := range apps.Pages { + for iidx, item := range page.Items { + switch item.(type) { + case string: + if !slices.Contains(dbApps, item.(string)) { + utils.DoubleIndent(log.WithField("app", item.(string)).Warn)("found app in config that are is not on system") + apps.Pages[idx].Items = append(apps.Pages[idx].Items[:iidx], apps.Pages[idx].Items[iidx+1:]...) + } + default: + var folder AppFolder + if err := mapstructure.Decode(item, &folder); err != nil { + return fmt.Errorf("mapstructure unable to decode config folder: %w", err) + } + for fpIdx, fpage := range folder.Pages { + for fpiIdx, fitem := range fpage.Items { + if !slices.Contains(dbApps, fitem) { + utils.DoubleIndent(log.WithField("app", fitem).Warn)("found app in config that are is not on system") + apps.Pages[idx].Items[iidx].(map[string]any)["pages"].([]any)[fpIdx].(map[string]any)["items"] = append( + apps.Pages[idx].Items[iidx].(map[string]any)["pages"].([]any)[fpIdx].(map[string]any)["items"].([]any)[:fpiIdx], + apps.Pages[idx].Items[iidx].(map[string]any)["pages"].([]any)[fpIdx].(map[string]any)["items"].([]any)[fpiIdx+1:]...) + } + } + } + } + } } - return missing, nil + return nil } // ClearGroups clears out items related to groups @@ -122,7 +154,7 @@ func (lp *LaunchPad) createNewPage(pageNumber, groupID, pageParentID int) error item := Item{ ID: groupID, - UUID: newUUID(), + UUID: uuid.New().String(), Flags: 2, Type: PageType, ParentID: pageParentID, @@ -153,7 +185,7 @@ func (lp *LaunchPad) createNewFolder(folderName string, folderNumber, groupID, f item := Item{ ID: groupID, - UUID: newUUID(), + UUID: uuid.New().String(), Flags: 0, Type: FolderRootType, ParentID: folderParentID, @@ -189,7 +221,7 @@ func (lp *LaunchPad) createNewFolderPage(folderPageNumber, groupID, folderPagePa item := Item{ ID: groupID, - UUID: newUUID(), + UUID: uuid.New().String(), Flags: 2, Type: PageType, ParentID: folderPageParentID, @@ -384,14 +416,3 @@ func (lp *LaunchPad) GetMaxWidgetID() int { return maxID } - -// newUUID generates a random UUID according to RFC 4122 -func newUUID() string { - uuid := make([]byte, 16) - _, _ = io.ReadFull(rand.Reader, uuid) - // variant bits; see section 4.1.1 - uuid[8] = uuid[8]&^0xc0 | 0x80 - // version 4 (pseudo-random); see section 4.1.3 - uuid[6] = uuid[6]&^0xf0 | 0x40 - return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]) -}