From cc509ec22a7670747efe5f1a6c9771cc116678ca Mon Sep 17 00:00:00 2001 From: sergiotarxz Date: Wed, 10 Feb 2021 00:59:49 +0100 Subject: [PATCH] Adding group creation support. --- .../groupcontroller/groupcontroller.go | 177 ++++++++++++++ wwwshop/dao/groupmanager/groupmanager.go | 217 ++++++++++++++++++ wwwshop/model/group/group.go | 33 +++ wwwshop/wwwshop.go | 4 +- 4 files changed, 430 insertions(+), 1 deletion(-) create mode 100644 wwwshop/controller/groupcontroller/groupcontroller.go create mode 100644 wwwshop/dao/groupmanager/groupmanager.go create mode 100644 wwwshop/model/group/group.go diff --git a/wwwshop/controller/groupcontroller/groupcontroller.go b/wwwshop/controller/groupcontroller/groupcontroller.go new file mode 100644 index 0000000..c4d0f07 --- /dev/null +++ b/wwwshop/controller/groupcontroller/groupcontroller.go @@ -0,0 +1,177 @@ +package groupcontroller + +import ( + d "WWWShop/wwwshop/dao/database" + u "WWWShop/wwwshop/dao/groupmanager" + "WWWShop/wwwshop/model/group" + "bytes" + "encoding/json" + "fmt" + "net/http" + "strconv" + "strings" + "errors" +) + +type GroupController struct{} + +func New() GroupController { + return GroupController{} +} + +func (self GroupController) DELETE(w http.ResponseWriter, r *http.Request) { + ids, groupnames, _, err := self.SetupInputForFilter(w, r) + group_manager := u.New(d.DB()) + + if err != nil { + if err != nil { + json_response,_ := EncodeJSONResponse(NewResponseError(err.Error())) + fmt.Fprintf(w, json_response) + return + } + } + deleted_groups, err := group_manager.Delete(ids, groupnames) + if err != nil { + json_response, _ := EncodeJSONResponse(NewResponseError(err.Error())) + fmt.Fprintf(w, json_response) + return + } + deleted_groups_json, _ := EncodeJSONResponse(deleted_groups) + fmt.Fprintf(w, deleted_groups_json) +} + +func (self GroupController) SetupInputForFilter(w http.ResponseWriter, r *http.Request) ([]int64, []string, *int64, error) { + query := r.URL.Query() + groupnames, _ := query["groupname"] + ids, _ := query["id"] + pages, _ := query["page"] + var page int64 + if len(pages) == 0 { + page = 0 + } else { + var err error + page, err = strconv.ParseInt(pages[0], 0, 64) + if err != nil { + return nil, nil, nil, errors.New("Unable to parse int page.") + } + } + ids_int := make([]int64, 0) + for _, id := range ids { + id_int, err := strconv.ParseInt(id, 0, 64) + if err != nil { + return nil, nil, nil, errors.New("Unable to parse int id.") + } + ids_int = append(ids_int, id_int) + } + return ids_int, groupnames, &page, nil +} + +func (self GroupController) PUT(w http.ResponseWriter, r *http.Request) { + var decoded_group_struct group.Group + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&decoded_group_struct) + if err != nil { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + json_response, _ := EncodeJSONResponse(NewResponseError("Failed to decode json group on update.")) + fmt.Fprintf(w, json_response) + return + } + if decoded_group_struct.Id == nil && decoded_group_struct.Groupname == nil { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + json_response, _ := EncodeJSONResponse(NewResponseError("Failed to get groupname or id needed to update groups.")) + fmt.Fprintf(w, json_response) + return + } + if decoded_group_struct.Permissions == nil { + decoded_group_struct.Permissions = make(map[string]string) + } + group_manager := u.New(d.DB()) + group, err := group_manager.Update(decoded_group_struct) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + json_response, _ := EncodeJSONResponse(NewResponseError(strings.Join([]string{"Failed to update group with the given data, may be our fault: ", err.Error()}, ""))) + fmt.Fprintf(w, json_response) + return + } + json_response, _ := EncodeJSONResponse(group) + fmt.Fprintf(w, json_response) +} + +func (self GroupController) GET(w http.ResponseWriter, r *http.Request) { + group_manager := u.New(d.DB()) + ids, groupnames, page, err := self.SetupInputForFilter(w, r) + if err != nil { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + json_response, _ := EncodeJSONResponse(NewResponseError(err.Error())) + fmt.Fprintf(w, json_response) + return + } + groups, err := group_manager.Retrieve(*page, ids, groupnames) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + json_response, _ := EncodeJSONResponse(NewResponseError(strings.Join([]string{"Unable to retrieve groups ", err.Error()}, ""))) + fmt.Fprintf(w, json_response) + return + } + json_response, _ := EncodeJSONResponse(groups) + fmt.Fprintf(w, json_response) +} + +func (self GroupController) POST(w http.ResponseWriter, r *http.Request) { + var decoded_group_struct POSTGroup + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&decoded_group_struct) + if err != nil { + json_response, _ := EncodeJSONResponse(NewResponseError("Unable to decode json")) + fmt.Fprintf(w, json_response) + return + } + if decoded_group_struct.Groupname == "" { + http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) + json_response, _ := EncodeJSONResponse(NewResponseError("Unable to get groupname")) + fmt.Fprintf(w, json_response) + return + } + if decoded_group_struct.Permissions == nil { + decoded_group_struct.Permissions = make(map[string]string) + } + group_manager := u.New(d.DB()) + group, err := group_manager.Add(decoded_group_struct.Groupname, decoded_group_struct.Permissions) + if err != nil { + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + json_response, _ := EncodeJSONResponse(NewResponseError(strings.Join( + []string{"Unable to create group ", err.Error()}, + ""))) + fmt.Fprintf(w, json_response) + return + } + json_response, err := EncodeJSONResponse(group) + fmt.Fprintf(w, json_response) +} + +type Response interface{} + +type ResponseError struct { + Error string `json:"error"` +} + +func NewResponseError(error_str string) ResponseError { + return ResponseError{ + Error: error_str, + } +} + +type POSTGroup struct { + Groupname string `json:"groupname"` + Permissions map[string]string `json:"permissions"` +} + +func EncodeJSONResponse(response Response) (string, error) { + var buffer bytes.Buffer + encoder := json.NewEncoder(&buffer) + err := encoder.Encode(response) + if err != nil { + return "", err + } + return buffer.String(), nil +} diff --git a/wwwshop/dao/groupmanager/groupmanager.go b/wwwshop/dao/groupmanager/groupmanager.go new file mode 100644 index 0000000..9d17837 --- /dev/null +++ b/wwwshop/dao/groupmanager/groupmanager.go @@ -0,0 +1,217 @@ +package groupmanager + +import ( + "WWWShop/wwwshop/model/group" + "database/sql" + "errors" + "fmt" + "github.com/lib/pq/hstore" + "log" + "strings" +) + +type GroupManager struct { + db *sql.DB +} + +func New(db *sql.DB) GroupManager { + return GroupManager{db} +} + +func (self GroupManager) Add(groupname string, permissions map[string]string) (*group.Group, error) { + var id int64 + if permissions == nil { + permissions = make(map[string]string) + } + permissions_null := hstore.Hstore{ + Map: make(map[string]sql.NullString), + } + for key, value := range permissions { + permissions_null.Map[key] = sql.NullString{ + String: value, + Valid: true, + } + } + result := self.db.QueryRow("INSERT INTO groups(groupname, permissions) VALUES ($1, $2) RETURNING id", groupname, permissions_null) + err := result.Scan(&id) + if err != nil { + return nil, err + } + group := group.New(id, groupname, permissions) + return &group, nil +} + +type QueryParameter interface{} + +func (self GroupManager) GenerateWhere(cur_key *int64, ids []int64, groupnames []string) string { + where_string := "WHERE false " + if groupnames != nil && len(groupnames) > 0 { + where_string = strings.Join([]string{where_string, "OR ( false"}, "") + for range groupnames { + where_string = strings.Join([]string{where_string, fmt.Sprintf(" OR groupname = $%d", *cur_key)}, "") + (*cur_key)++ + } + where_string = strings.Join([]string{where_string, " ) "}, "") + } + if ids != nil && len(ids) > 0 { + where_string = strings.Join([]string{where_string, "OR ( false"}, "") + for range ids { + where_string = strings.Join([]string{where_string, fmt.Sprintf(" OR id = $%d", *cur_key)}, "") + (*cur_key)++ + } + where_string = strings.Join([]string{where_string, " ) "}, "") + } + return where_string +} + +func (self GroupManager) Update(group group.Group) (*group.Group, error) { + if group.Id == nil && group.Groupname == nil { + return nil, errors.New("Both Id and Groupname cannot be null on the same time on UPDATE.") + } + if group.Permissions == nil { + return nil, errors.New("Should be at least a field for update .") + } + query := "UPDATE groups SET permissions = $1 WHERE true " + parameters := make([]interface{}, 0) + permissions_null := hstore.Hstore{ + Map: make(map[string]sql.NullString), + } + for key, value := range group.Permissions { + log.Println(value) + permissions_null.Map[key] = sql.NullString{ + String: value, + Valid: true, + } + } + parameters = append(parameters, permissions_null) + if group.Id != nil { + query = strings.Join([]string{query, "AND id = $2 "}, "") + parameters = append(parameters, *(group.Id)) + } else { + query = strings.Join([]string{query, "AND groupname = $2 "}, "") + + parameters = append(parameters, *(group.Groupname)) + } + query = strings.Join([]string{query, " RETURNING id"}, "") + var id int64 + result := self.db.QueryRow(query, parameters...) + err := result.Scan(&id) + if err != nil { + return nil, err + } + groups, err := self.Retrieve(0, []int64{id}, nil) + group = groups[0] + if err != nil { + return nil, err + } + return &group, nil +} + +func (self GroupManager) Delete(ids []int64, groupnames []string) ([]string, error) { + var where bool + var cur_key int64 + var where_string string + cur_key = 1 + + if ids != nil && len(ids) > 0 { + where = true + } + if groupnames != nil && len(groupnames) > 0 { + where = true + } + + if where { + where_string = self.GenerateWhere(&cur_key, ids, groupnames) + } else { + return nil, errors.New("Cannot delete without a clause") + } + + query_parameters := make([]interface{}, 0) + for _, groupname := range groupnames { + query_parameters = append(query_parameters, groupname) + } + for _, id := range ids { + query_parameters = append(query_parameters, id) + } + query_string := fmt.Sprintf("DELETE FROM groups %s RETURNING groupname", where_string) + result, err := self.db.Query( + query_string, + query_parameters..., + ) + if err != nil { + return nil, err + } + defer result.Close() + var deleted_groups []string + for result.Next() { + var groupname string + err := result.Scan(&groupname) + if err != nil { + return nil, err + } + deleted_groups = append(deleted_groups, groupname) + } + if deleted_groups == nil { + deleted_groups = make([]string, 0) + } + return deleted_groups, nil +} + +func (self GroupManager) Retrieve(page int64, ids []int64, groupnames []string) ([]group.Group, error) { + var where bool + var cur_key int64 + + if ids != nil && len(ids) > 0 { + where = true + } + if groupnames != nil && len(groupnames) > 0 { + where = true + } + cur_key = 1 + where_string := "" + if where { + where_string = self.GenerateWhere(&cur_key, ids, groupnames) + } + page = (10 * page) + page_string := fmt.Sprintf("LIMIT 10 OFFSET %d", page) + query_parameters := make([]interface{}, 0) + for _, groupname := range groupnames { + query_parameters = append(query_parameters, groupname) + } + for _, id := range ids { + query_parameters = append(query_parameters, id) + } + query_string := fmt.Sprintf("SELECT id, groupname, permissions from groups %s %s;", where_string, page_string) + result, err := self.db.Query( + query_string, + query_parameters..., + ) + if err != nil { + return nil, err + } + defer result.Close() + var groups []group.Group + for result.Next() { + var ( + id_group int64 + groupname_group string + permissions_group_null hstore.Hstore + ) + err := result.Scan(&id_group, &groupname_group, &permissions_group_null) + permissions_group := make(map[string]string) + for key, permission_null := range permissions_group_null.Map { + if permission_null.Valid { + permissions_group[key] = permission_null.String + } + } + if err != nil { + return nil, err + } + u := group.New(id_group, groupname_group, permissions_group) + groups = append(groups, u) + } + if groups == nil { + groups = make([]group.Group, 0) + } + return groups, nil +} diff --git a/wwwshop/model/group/group.go b/wwwshop/model/group/group.go new file mode 100644 index 0000000..8ab2e23 --- /dev/null +++ b/wwwshop/model/group/group.go @@ -0,0 +1,33 @@ +package group + +import ( + "log" +) + +type Group struct { + Id *int64 `json:"id"` + Groupname *string `json:"groupname"` + Permissions map[string]string `json:"permissions"` +} + +func New(id int64, groupname string, permissions map[string]string) Group { + return Group{ + Id: &id, + Groupname: &groupname, + Permissions: permissions, + } +} + +func (self Group) HasPermission(permission string) string { + perm_value, ok := self.Permissions[permission] + if ok { + if perm_value == "yes" { + return "yes" + } + if perm_value == "no" { + return "no" + } + log.Printf("Invalid permission value for %s in group %s.\n", permission, self.Groupname) + } + return "" +} diff --git a/wwwshop/wwwshop.go b/wwwshop/wwwshop.go index 06c31f4..3795545 100644 --- a/wwwshop/wwwshop.go +++ b/wwwshop/wwwshop.go @@ -4,6 +4,7 @@ import ( "net/http" c "WWWShop/wwwshop/controller" u "WWWShop/wwwshop/controller/usercontroller" + g "WWWShop/wwwshop/controller/groupcontroller" d "WWWShop/wwwshop/dao/database" ) @@ -18,7 +19,8 @@ func New() WWWShop { func (self WWWShop) Init() { d.Migrate(d.DB()) user_controller := c.GenerateController(u.New()) + group_controller := c.GenerateController(g.New()) + http.Handle("/group", group_controller) http.Handle("/user", user_controller) - http.Handle("/user/", user_controller) http.ListenAndServe(":8080", nil) }