package webutility import ( "database/sql" "fmt" ) type ListOptions struct { GlobalFilter bool `json:"globalFilter"` LocalFilters bool `json:"localFilters"` RemoteFilters bool `json:"remoteFilters"` Pagination bool `json:"pagination"` PageSize uint32 `json:"pageSize"` Pivot bool `json:"pivot"` Detail bool `json:"detail"` Total bool `json:"total"` } type ListFilter struct { Position uint32 `json:"-"` ObjectType string `json:"-"` FiltersField string `json:"filtersField"` DefaultValues string `json:"defaultValues"` FiltersType string `json:"filtersType"` FiltersLabel string `json:"filtersLabel"` DropdownConfig Dropdown `json:"dropdownConfig"` } type Dropdown struct { ObjectType string `json:"objectType"` FiltersField string `json:"filtersField"` IDField string `json:"idField"` LabelField string `json:"labelField"` } type ListGraph struct { ObjectType string `json:"objectType"` X string `json:"xField"` Y string `json:"yField"` GroupField string `json:"groupField"` Label string `json:"label"` } type ListActions struct { Create bool `json:"create"` Update bool `json:"update"` Delete bool `json:"delete"` Export bool `json:"export"` Print bool `json:"print"` Graph bool `json:"graph"` } type ListNavNode struct { ObjectType string `json:"objectType"` LabelField string `json:"label"` Icon string `json:"icon"` ParentObjectType string `json:"parentObjectType"` ParentIDField string `json:"parentIdField"` ParentFilterField string `json:"parentFilterField"` } type ListParentNode struct { ObjectType string `json:"objectType"` LabelField string `json:"labelField"` FilterField string `json:"filterField"` } type ListPivot struct { ObjectType string `json:"objectType"` GroupField string `json:"groupField"` DistinctField string `json:"distinctField"` Value string `json:"valueField"` } type ListDetails struct { ObjectType string `json:"objectType"` ParentObjectType string `json:"parentObjectType"` ParentFilterField string `json:"parentFilterField"` SingleDetail bool `json:"singleDetail"` } type ListConfig struct { ObjectType string `json:"objectType"` Title string `json:"title"` LazyLoad bool `json:"lazyLoad"` InlineEdit bool `json:"inlineEdit"` Options ListOptions `json:"options"` Filters []ListFilter `json:"defaultFilters"` Graphs []ListGraph `json:"graphs"` Actions ListActions `json:"actions"` Parent []ListParentNode `json:"parent"` Navigation []ListNavNode `json:"navigation"` Pivots []ListPivot `json:"pivots"` Details ListDetails `json:"details"` } // GetListConfig returns list configuration for the provided object type for the front-end application // or an error if it fails. func GetListConfig(db *sql.DB, objType string) (ListConfig, error) { resp := newDefaultList(objType) var err error err = setListParams(db, &resp, objType) resp.Navigation, err = getListNavigation(db, objType) resp.Actions, err = getListActions(db, objType) resp.Filters, err = getListFilters(db, objType) resp.Options, err = getListOptions(db, objType) resp.Parent, err = getListParent(db, objType) resp.Graphs, err = getListGraph(db, objType) resp.Pivots, err = getListPivot(db, objType) resp.Details, err = getListDetails(db, objType) if err != nil { return ListConfig{}, err } return resp, nil } // GetListConfigObjectIDField takes in database connection and an object type and it returns the // ID field name for the provided object type. func GetListConfigObjectIDField(db *sql.DB, otype string) string { var resp string rows, err := db.Query(`SELECT ID_FIELD FROM LIST_CONFIG_ID_FIELD WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", otype)) if err != nil { return "" } defer rows.Close() if rows.Next() { rows.Scan(&resp) } if rows.Err() != nil { return "" } return resp } // newDefaultList returns default configuration for the provided object type. func newDefaultList(objType string) ListConfig { list := ListConfig{ ObjectType: objType, Title: objType, LazyLoad: false, Options: ListOptions{ GlobalFilter: true, LocalFilters: true, RemoteFilters: false, Pagination: true, PageSize: 20, }, Filters: nil, Actions: ListActions{ Create: false, Update: false, Delete: false, Export: false, Print: false, Graph: false, }, Parent: nil, Navigation: nil, } return list } // setListParams sets the default parameters of the provided configuration list for the provided object type. func setListParams(db *sql.DB, list *ListConfig, objType string) error { rows, err := db.Query(`SELECT OBJECT_TYPE, TITLE, LAZY_LOAD, INLINE_EDIT FROM LIST_CONFIG WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) if err != nil { return err } defer rows.Close() if rows.Next() { otype, title := "", "" lazyLoad, inlineEdit := 0, 0 rows.Scan(&otype, &title, &lazyLoad, &inlineEdit) if otype != "" { list.ObjectType = otype } if title != "" { list.Title = title } list.LazyLoad = lazyLoad != 0 list.InlineEdit = inlineEdit != 0 } if rows.Err() != nil { return rows.Err() } return nil } // getListNavigation returns list navigation node slice for the provided objectType. func getListNavigation(db *sql.DB, listObjType string) ([]ListNavNode, error) { resp := make([]ListNavNode, 0) rows, err := db.Query(`SELECT a.OBJECT_TYPE, a.PARENT_OBJECT_TYPE, a.LABEL, a.ICON, a.PARENT_FILTER_FIELD, b.PARENT_ID_FIELD FROM LIST_CONFIG_NAVIGATION b JOIN LIST_CONFIG_CHILD a ON b.PARENT_CHILD_ID = a.PARENT_CHILD_ID WHERE b.LIST_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", listObjType) + ` ORDER BY b.RB ASC`) if err != nil { return resp, err } defer rows.Close() var node ListNavNode for rows.Next() { rows.Scan(&node.ObjectType, &node.ParentObjectType, &node.LabelField, &node.Icon, &node.ParentFilterField, &node.ParentIDField) resp = append(resp, node) } if rows.Err() != nil { return nil, rows.Err() } return resp, nil } // getListActions returns list actions for the provided object type. func getListActions(db *sql.DB, objType string) (ListActions, error) { var resp ListActions rows, err := db.Query(`SELECT ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT, ACTION_PRINT, ACTION_GRAPH FROM LIST_CONFIG WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) if err != nil { return ListActions{}, err } defer rows.Close() var create, update, delete, export, print, graph uint32 if rows.Next() { rows.Scan(&create, &update, &delete, &export, &print, &graph) resp.Create = create != 0 resp.Update = update != 0 resp.Delete = delete != 0 resp.Export = export != 0 resp.Print = print != 0 resp.Graph = graph != 0 } if rows.Err() != nil { return ListActions{}, rows.Err() } return resp, nil } // getListFiters returns list filter slice for the provided object type. func getListFilters(db *sql.DB, objType string) ([]ListFilter, error) { resp := make([]ListFilter, 0) filtersFields, err := getFilterFieldsAndPosition(db, objType) if err != nil { return nil, err } for field, pos := range filtersFields { filters, _ := getFiltersByFilterField(db, field) for _, filter := range filters { var f ListFilter f.Position = pos f.ObjectType = objType f.FiltersField = field f.DefaultValues = filter.DefaultValues f.FiltersLabel = filter.Label f.FiltersType = filter.Type if filter.Type == "dropdown" { f.DropdownConfig, err = getFilterDropdownConfig(db, field) if err != nil { return nil, err } } resp = append(resp, f) } } sortFilters(resp) return resp, nil } // getFilterFieldsAndPosition returns a map of filter fields and their respective position in the menu. func getFilterFieldsAndPosition(db *sql.DB, objType string) (map[string]uint32, error) { filtersField := make(map[string]uint32, 0) rows, err := db.Query(`SELECT FILTERS_FIELD, RB FROM LIST_CONFIG_FILTERS WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var field string var rb uint32 rows.Scan(&field, &rb) filtersField[field] = rb } if rows.Err() != nil { return nil, rows.Err() } return filtersField, nil } type _filter struct { DefaultValues string Label string Type string } // getFiltersByFilterField returns filter slice for the provided filter field. func getFiltersByFilterField(db *sql.DB, filtersField string) ([]_filter, error) { resp := make([]_filter, 0) rows, err := db.Query(`SELECT FILTERS_TYPE, FILTERS_LABEL, DEFAULT_VALUES FROM LIST_FILTERS_FIELD WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) if err != nil { return resp, err } defer rows.Close() var f _filter for rows.Next() { rows.Scan(&f.Type, &f.Label, &f.DefaultValues) resp = append(resp, f) } if rows.Err() != nil { return resp, rows.Err() } return resp, nil } // getFilterDropdownConfig returns dropdown menu for the provided filter field. func getFilterDropdownConfig(db *sql.DB, filtersField string) (Dropdown, error) { var resp Dropdown rows, err := db.Query(`SELECT FILTERS_FIELD, OBJECT_TYPE, ID_FIELD, LABEL_FIELD FROM LIST_DROPDOWN_FILTER WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) if err != nil { return resp, err } defer rows.Close() if rows.Next() { rows.Scan(&resp.FiltersField, &resp.ObjectType, &resp.IDField, &resp.LabelField) } if rows.Err() != nil { return resp, rows.Err() } return resp, nil } // sortFilters bubble sorts provided filters slice by position field. func sortFilters(filters []ListFilter) { done := false var temp ListFilter for !done { done = true for i := 0; i < len(filters)-1; i++ { if filters[i].Position > filters[i+1].Position { done = false temp = filters[i] filters[i] = filters[i+1] filters[i+1] = temp } } } } // getListGraph return list graph slice for the provided object type. func getListGraph(db *sql.DB, objType string) ([]ListGraph, error) { resp := make([]ListGraph, 0) rows, err := db.Query(`SELECT OBJECT_TYPE, X_FIELD, Y_FIELD, GROUP_FIELD, LABEL FROM LIST_GRAPHS WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) if err != nil { return resp, err } defer rows.Close() var lg ListGraph for rows.Next() { rows.Scan(&lg.ObjectType, &lg.X, &lg.Y, &lg.GroupField, &lg.Label) resp = append(resp, lg) } if rows.Err() != nil { return resp, rows.Err() } return resp, nil } // getListOptions returns list options for the provided object type. func getListOptions(db *sql.DB, objType string) (ListOptions, error) { var resp ListOptions rows, err := db.Query(`SELECT GLOBAL_FILTER, LOCAL_FILTER, REMOTE_FILTER, PAGINATION, PAGE_SIZE, PIVOT, DETAIL, TOTAL FROM LIST_CONFIG WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) if err != nil { return ListOptions{}, err } defer rows.Close() if rows.Next() { var gfilter, lfilters, rfilters, pagination, pageSize, pivot, detail, total uint32 rows.Scan(&gfilter, &lfilters, &rfilters, &pagination, &pageSize, &pivot, &detail, &total) resp.GlobalFilter = gfilter != 0 resp.LocalFilters = lfilters != 0 resp.RemoteFilters = rfilters != 0 resp.Pagination = pagination != 0 resp.PageSize = pageSize resp.Pivot = pivot != 0 resp.Detail = detail != 0 resp.Total = total != 0 } if rows.Err() != nil { return ListOptions{}, rows.Err() } return resp, nil } // getListParent returns list parent node slice for the provided object type. func getListParent(db *sql.DB, objType string) ([]ListParentNode, error) { resp := make([]ListParentNode, 0) rows, err := db.Query(`SELECT PARENT_OBJECT_TYPE, PARENT_LABEL_FIELD, PARENT_FILTER_FIELD FROM LIST_CONFIG_CHILD WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) if err != nil { return resp, err } defer rows.Close() var pnode ListParentNode for rows.Next() { rows.Scan(&pnode.ObjectType, &pnode.LabelField, &pnode.FilterField) resp = append(resp, pnode) } if rows.Err() != nil { return nil, rows.Err() } return resp, nil } // getListPivot list pivot slice for the provided object type. func getListPivot(db *sql.DB, objType string) ([]ListPivot, error) { resp := make([]ListPivot, 0) rows, err := db.Query(`SELECT OBJECT_TYPE, GROUP_FIELD, DISTINCT_FIELD, VALUE_FIELD FROM LIST_PIVOTS WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) if err != nil { return resp, err } defer rows.Close() var p ListPivot for rows.Next() { rows.Scan(&p.ObjectType, &p.GroupField, &p.DistinctField, &p.Value) resp = append(resp, p) } if rows.Err() != nil { return nil, rows.Err() } return resp, nil } // getListDetails returns list details for the provided object type. func getListDetails(db *sql.DB, objType string) (ListDetails, error) { var resp ListDetails rows, err := db.Query(`SELECT OBJECT_TYPE, PARENT_OBJECT_TYPE, PARENT_FILTER_FIELD, SINGLE_DETAIL FROM LIST_CONFIG_DETAIL WHERE PARENT_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) if err != nil { return resp, err } defer rows.Close() if rows.Next() { var singleDetail uint32 rows.Scan(&resp.ObjectType, &resp.ParentObjectType, &resp.ParentFilterField, &singleDetail) resp.SingleDetail = singleDetail != 0 } if rows.Err() != nil { return resp, rows.Err() } return resp, nil }