diff --git a/json_utility.go b/json_utility.go index 3ef16e3..3474fc7 100644 --- a/json_utility.go +++ b/json_utility.go @@ -249,7 +249,8 @@ func initMetadata(project string) error { } count++ } - logger.Log("webutility: successfully loaded %d/%d (%.1f%%) entities\n", success, count, float32(success)/float32(count)*100.0) + perc := float32(success/count) * 100.0 + logger.Log("loaded %d/%d (%.1f%%) entities\n", success, count, perc) return nil } diff --git a/list_config.go b/list_config.go new file mode 100644 index 0000000..c81ca64 --- /dev/null +++ b/list_config.go @@ -0,0 +1,613 @@ +package webutility + +import "gopkg.in/rana/ora.v4" + +type ListOptions struct { + GlobalFilter bool `json:"globalFilter"` + LocalFilters bool `json:"localFilters"` + RemoteFilters bool `json:"remoteFilters"` + Pagination bool `json:"pagination"` + PageSize uint64 `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 *ora.Ses, 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 *ora.Ses, otype string) string { + var resp string + var err error + var stmt *ora.Stmt + + stmt, err = db.Prep(`SELECT + ID_FIELD + FROM LIST_CONFIG_ID_FIELD + WHERE OBJECT_TYPE = '`+otype+`'`, + ora.S) + + defer stmt.Close() + + if err != nil { + return "" + } + + rset, err := stmt.Qry() + if rset.Next() { + resp = rset.Row[0].(string) + } + + if rset.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 *ora.Ses, list *ListConfig, objType string) error { + var err error + var stmt *ora.Stmt + query := `SELECT + OBJECT_TYPE, TITLE, LAZY_LOAD, INLINE_EDIT + FROM LIST_CONFIG + WHERE OBJECT_TYPE = '` + objType + `'` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.U32, ora.U32) + if err != nil { + return err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return err + } + if rset.Next() { + otype := rset.Row[0].(string) + if otype != "" { + list.ObjectType = otype + } + + title := rset.Row[1].(string) + if title != "" { + list.Title = title + } + list.LazyLoad = rset.Row[2].(uint32) != 0 + list.InlineEdit = rset.Row[3].(uint32) != 0 + } + if rset.Err() != nil { + return rset.Err() + } + return nil +} + +// getListNavigation returns list navigation node slice for the provided objectType. +func getListNavigation(db *ora.Ses, listObjType string) ([]ListNavNode, error) { + resp := make([]ListNavNode, 0) + var err error + var stmt *ora.Stmt + query := `SELECT + a.OBJECT_TYPE, a.PARENT_OBJECT_TYPE, a.LABEL, a.ICON, a.PARENT_FILTER_FIELD, b.PARENT_ID_FIELD, b.RB + FROM LIST_CONFIG_NAVIGATION b + JOIN LIST_CONFIG_CHILD a ON b.PARENT_CHILD_ID = a.PARENT_CHILD_ID + WHERE b.LIST_OBJECT_TYPE = '` + listObjType + `' + ORDER BY b.RB ASC` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S) + if err != nil { + return resp, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return resp, err + } + for rset.Next() { + resp = append(resp, ListNavNode{ + ObjectType: rset.Row[0].(string), + ParentObjectType: rset.Row[1].(string), + LabelField: rset.Row[2].(string), + Icon: rset.Row[3].(string), + ParentFilterField: rset.Row[4].(string), + ParentIDField: rset.Row[5].(string), + // RB is ignored + }) + } + if rset.Err() != nil { + return nil, rset.Err() + } + + return resp, nil +} + +// getListActions returns list actions for the provided object type. +func getListActions(db *ora.Ses, objType string) (ListActions, error) { + var resp ListActions + var err error + var stmt *ora.Stmt + query := `SELECT + ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT, + ACTION_PRINT, ACTION_GRAPH + FROM LIST_CONFIG + WHERE OBJECT_TYPE = '` + objType + `'` + + stmt, err = db.Prep(query, ora.U32, ora.U32, ora.U32, ora.U32, + ora.U32, ora.U32) + if err != nil { + return ListActions{}, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return ListActions{}, err + } + if rset.Next() { + resp.Create = rset.Row[0].(uint32) != 0 + resp.Update = rset.Row[1].(uint32) != 0 + resp.Delete = rset.Row[2].(uint32) != 0 + resp.Export = rset.Row[3].(uint32) != 0 + resp.Print = rset.Row[4].(uint32) != 0 + resp.Graph = rset.Row[5].(uint32) != 0 + } + if rset.Err() != nil { + return ListActions{}, rset.Err() + } + return resp, nil +} + +// getListFiters returns list filter slice for the provided object type. +func getListFilters(db *ora.Ses, 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 *ora.Ses, objType string) (map[string]uint32, error) { + filtersField := make(map[string]uint32, 0) + var err error + var stmt *ora.Stmt + query := `SELECT + FILTERS_FIELD, RB + FROM LIST_CONFIG_FILTERS + WHERE OBJECT_TYPE = '` + objType + `'` + + stmt, err = db.Prep(query, ora.S, ora.U32) + if err != nil { + return nil, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return nil, err + } + for rset.Next() { + filtersField[rset.Row[0].(string)] = rset.Row[1].(uint32) + } + if rset.Err() != nil { + return nil, rset.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 *ora.Ses, filtersField string) ([]_filter, error) { + resp := make([]_filter, 0) + var err error + var stmt *ora.Stmt + query := `SELECT + FILTERS_TYPE, FILTERS_LABEL, DEFAULT_VALUES + FROM LIST_FILTERS_FIELD + WHERE FILTERS_FIELD = '` + filtersField + `'` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.S) + if err != nil { + return resp, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return resp, err + } + for rset.Next() { + resp = append(resp, _filter{ + Type: rset.Row[0].(string), + Label: rset.Row[1].(string), + DefaultValues: rset.Row[2].(string), + }) + } + if rset.Err() != nil { + return resp, rset.Err() + } + return resp, nil +} + +// getFilterDropdownConfig returns dropdown menu for the provided filter field. +func getFilterDropdownConfig(db *ora.Ses, filtersField string) (Dropdown, error) { + var resp Dropdown + var err error + var stmt *ora.Stmt + query := `SELECT + FILTERS_FIELD, OBJECT_TYPE, ID_FIELD, LABEL_FIELD + FROM LIST_DROPDOWN_FILTER + WHERE FILTERS_FIELD = '` + filtersField + `'` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) + if err != nil { + return resp, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return resp, err + } + if rset.Next() { + resp.FiltersField = rset.Row[0].(string) + resp.ObjectType = rset.Row[1].(string) + resp.IDField = rset.Row[2].(string) + resp.LabelField = rset.Row[3].(string) + } + if rset.Err() != nil { + return resp, rset.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 *ora.Ses, objType string) ([]ListGraph, error) { + resp := make([]ListGraph, 0) + var err error + var stmt *ora.Stmt + query := `SELECT + OBJECT_TYPE, X_FIELD, Y_FIELD, GROUP_FIELD, LABEL + FROM LIST_GRAPHS + WHERE OBJECT_TYPE = '` + objType + `'` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) + if err != nil { + return resp, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return resp, err + } + for rset.Next() { + resp = append(resp, ListGraph{ + ObjectType: rset.Row[0].(string), + X: rset.Row[1].(string), + Y: rset.Row[2].(string), + GroupField: rset.Row[3].(string), + Label: rset.Row[4].(string), + }) + } + if rset.Err() != nil { + return resp, rset.Err() + } + return resp, nil +} + +// getListOptions returns list options for the provided object type. +func getListOptions(db *ora.Ses, objType string) (ListOptions, error) { + var resp ListOptions + var err error + var stmt *ora.Stmt + query := `SELECT + GLOBAL_FILTER, LOCAL_FILTER, REMOTE_FILTER, PAGINATION, + PAGE_SIZE, PIVOT, DETAIL, TOTAL + FROM LIST_CONFIG + WHERE OBJECT_TYPE = '` + objType + `'` + + stmt, err = db.Prep(query, ora.U32, ora.U32, ora.U32, ora.U32, + ora.U64, ora.U64, ora.U32, ora.U32) + if err != nil { + return ListOptions{}, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return ListOptions{}, err + } + if rset.Next() { + resp.GlobalFilter = rset.Row[0].(uint32) != 0 + resp.LocalFilters = rset.Row[1].(uint32) != 0 + resp.RemoteFilters = rset.Row[2].(uint32) != 0 + resp.Pagination = rset.Row[3].(uint32) != 0 + resp.PageSize = rset.Row[4].(uint64) + resp.Pivot = rset.Row[5].(uint64) != 0 + resp.Detail = rset.Row[6].(uint32) != 0 + resp.Total = rset.Row[7].(uint32) != 0 + } + if rset.Err() != nil { + return ListOptions{}, rset.Err() + } + return resp, nil +} + +// getListParent returns list parent node slice for the provided object type. +func getListParent(db *ora.Ses, objType string) ([]ListParentNode, error) { + resp := make([]ListParentNode, 0) + var err error + var stmt *ora.Stmt + query := `SELECT + PARENT_OBJECT_TYPE, PARENT_LABEL_FIELD, PARENT_FILTER_FIELD + FROM LIST_CONFIG_CHILD + WHERE OBJECT_TYPE = '` + objType + `'` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.S) + if err != nil { + return resp, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return resp, err + } + for rset.Next() { + resp = append(resp, ListParentNode{ + ObjectType: rset.Row[0].(string), + LabelField: rset.Row[1].(string), + FilterField: rset.Row[2].(string), + }) + } + if rset.Err() != nil { + return nil, rset.Err() + } + + return resp, nil +} + +// getListPivot list pivot slice for the provided object type. +func getListPivot(db *ora.Ses, objType string) ([]ListPivot, error) { + resp := make([]ListPivot, 0) + var err error + var stmt *ora.Stmt + query := `SELECT + OBJECT_TYPE, GROUP_FIELD, DISTINCT_FIELD, VALUE_FIELD + FROM LIST_PIVOTS + WHERE OBJECT_TYPE = '` + objType + `'` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) + if err != nil { + return resp, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return resp, err + } + for rset.Next() { + resp = append(resp, ListPivot{ + ObjectType: rset.Row[0].(string), + GroupField: rset.Row[1].(string), + DistinctField: rset.Row[2].(string), + Value: rset.Row[3].(string), + }) + } + if rset.Err() != nil { + return nil, rset.Err() + } + + return resp, nil +} + +// getListDetails returns list details for the provided object type. +func getListDetails(db *ora.Ses, objType string) (ListDetails, error) { + var resp ListDetails + var err error + var stmt *ora.Stmt + query := `SELECT + OBJECT_TYPE, PARENT_OBJECT_TYPE, PARENT_FILTER_FIELD, SINGLE_DETAIL + FROM LIST_CONFIG_DETAIL + WHERE PARENT_OBJECT_TYPE = '` + objType + `'` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.U32) + if err != nil { + return resp, err + } + defer stmt.Close() + + rset, err := stmt.Qry() + if err != nil { + return resp, err + } + if rset.Next() { + resp.ObjectType = rset.Row[0].(string) + resp.ParentObjectType = rset.Row[1].(string) + resp.ParentFilterField = rset.Row[2].(string) + resp.SingleDetail = rset.Row[3].(uint32) != 0 + } + if rset.Err() != nil { + return resp, rset.Err() + } + + return resp, nil +} diff --git a/select_config.go b/select_config.go new file mode 100644 index 0000000..7797503 --- /dev/null +++ b/select_config.go @@ -0,0 +1,56 @@ +package webutility + +import "gopkg.in/rana/ora.v4" + +type SelectConfig struct { + ListObjType string `json:"listObjectType"` + ObjType string `json:"objectType"` + Type string `json:"type"` + IdField string `json:"idField"` + LabelField string `json:"labelField"` + ValueField string `json:"valueField"` +} + +// GetSelectConfig returns select configuration slice for the given object type. +func GetSelectConfig(db *ora.Ses, otype string) ([]SelectConfig, error) { + resp := make([]SelectConfig, 0) + var err error + var stmt *ora.Stmt + query := `SELECT + a.LIST_OBJECT_TYPE, + a.OBJECT_TYPE, + a.ID_FIELD, + a.LABEL_FIELD, + a.TYPE, + b.FIELD + FROM LIST_SELECT_CONFIG a, LIST_VALUE_FIELD b + WHERE a.LIST_OBJECT_TYPE` + otype + ` + AND b.LIST_TYPE = a.LIST_OBJECT_TYPE + AND b.OBJECT_TYPE = a.OBJECT_TYPE` + + stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S, ora.S) + defer stmt.Close() + if err != nil { + return nil, err + } + + rset, err := stmt.Qry() + if err != nil { + return nil, err + } + for rset.Next() { + resp = append(resp, SelectConfig{ + ListObjType: rset.Row[0].(string), + ObjType: rset.Row[1].(string), + IdField: rset.Row[2].(string), + LabelField: rset.Row[3].(string), + Type: rset.Row[4].(string), + ValueField: rset.Row[5].(string), + }) + } + if rset.Err() != nil { + return nil, rset.Err() + } + + return resp, nil +}