From 5d8b9f426e5adb1d795082822b5e9ba08487f307 Mon Sep 17 00:00:00 2001
From: William Enright <william.enright@savoirfairelinux.com>
Date: Thu, 20 Aug 2020 12:28:30 -0400
Subject: [PATCH] added blueprint tracking to group object

Change-Id: Id8ebe5d20a5ef566f3d031dfaf6f1a3ab421b114
---
 .../java/net/jami/datastore/dao/GroupDao.java |  8 +-
 .../resources/db/migration/V25__Group.sql     |  1 +
 .../resources/db/migration/V25__Group.sql     |  1 +
 .../jami/jams/common/objects/user/Group.java  |  3 +
 .../src/components/Drawer/Drawer.js           | 17 +++-
 .../src/views/Groups/EditGroup.js             | 90 ++++++++++---------
 jams-react-client/src/views/Groups/Groups.js  | 28 ++++--
 .../api/admin/group/GroupServlet.java         | 27 ++++--
 8 files changed, 114 insertions(+), 61 deletions(-)
 create mode 100644 datastore/src/main/resources/db/migration/V25__Group.sql
 create mode 100644 datastore/src/test/resources/db/migration/V25__Group.sql

diff --git a/datastore/src/main/java/net/jami/datastore/dao/GroupDao.java b/datastore/src/main/java/net/jami/datastore/dao/GroupDao.java
index 76a9dc26..e33bc036 100644
--- a/datastore/src/main/java/net/jami/datastore/dao/GroupDao.java
+++ b/datastore/src/main/java/net/jami/datastore/dao/GroupDao.java
@@ -22,7 +22,7 @@ public class GroupDao extends AbstractDao<Group>{
         SQLConnection connection = DataStore.connectionPool.getConnection();
         try{
             PreparedStatement ps = connection.getConnection().prepareStatement("INSERT INTO groups " +
-                    "(name) VALUES (?)");
+                    "(name, blueprint) VALUES (?, ?)");
             ps = object.getInsert(ps);
             return ps.executeUpdate() != 0;
         }
@@ -39,14 +39,16 @@ public class GroupDao extends AbstractDao<Group>{
     public boolean updateObject(StatementList update, StatementList constraints) {
 
         String name = update.getStatements().get(0).getValue();
+        String blueprint = update.getStatements().get(1).getValue();
         String oldName = constraints.getStatements().get(0).getValue();
 
         SQLConnection connection = DataStore.connectionPool.getConnection();
 
         try{
-            PreparedStatement ps = connection.getConnection().prepareStatement("UPDATE groups SET name = ? WHERE name = ?");
+            PreparedStatement ps = connection.getConnection().prepareStatement("UPDATE groups SET name = ?, blueprint = ? WHERE name = ?");
             ps.setString(1, name);
-            ps.setString(2, oldName);
+            ps.setString(2, blueprint);
+            ps.setString(3, oldName);
             return ps.executeUpdate() != 0;
         }
         catch (SQLException e){
diff --git a/datastore/src/main/resources/db/migration/V25__Group.sql b/datastore/src/main/resources/db/migration/V25__Group.sql
new file mode 100644
index 00000000..a5116570
--- /dev/null
+++ b/datastore/src/main/resources/db/migration/V25__Group.sql
@@ -0,0 +1 @@
+ALTER TABLE groups ADD COLUMN blueprint varchar(255);
\ No newline at end of file
diff --git a/datastore/src/test/resources/db/migration/V25__Group.sql b/datastore/src/test/resources/db/migration/V25__Group.sql
new file mode 100644
index 00000000..a5116570
--- /dev/null
+++ b/datastore/src/test/resources/db/migration/V25__Group.sql
@@ -0,0 +1 @@
+ALTER TABLE groups ADD COLUMN blueprint varchar(255);
\ No newline at end of file
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/Group.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/Group.java
index 42e6c699..e01e18c4 100644
--- a/jams-common/src/main/java/net/jami/jams/common/objects/user/Group.java
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/Group.java
@@ -21,16 +21,19 @@ import java.util.List;
 @Setter
 public class Group implements DatabaseObject {
     private String name;
+    private String blueprint;
     private List<String> groupMembers;
 
     public Group(ResultSet rs) throws SQLException {
         this.name = rs.getString("name");
+        this.blueprint = rs.getString("blueprint");
         this.groupMembers = new ArrayList<>();
     }
 
     @Override
     public PreparedStatement getInsert(PreparedStatement ps) throws SQLException {
         ps.setString(1, name);
+        ps.setString(2, blueprint);
         return ps;
     }
 
diff --git a/jams-react-client/src/components/Drawer/Drawer.js b/jams-react-client/src/components/Drawer/Drawer.js
index 33159cab..783b20c6 100644
--- a/jams-react-client/src/components/Drawer/Drawer.js
+++ b/jams-react-client/src/components/Drawer/Drawer.js
@@ -74,10 +74,19 @@ export default function TemporaryDrawer(props) {
     }
 
     const addUserToGroup = (username) => {
-        axios(configApiCall(api_path_put_update_group+"?newName="+props.groupName+"&groupMembers="+username, 'PUT', null, null)).then((response)=>{
+        let url = '';
+        if(props.selectedBlueprint == ''){
+            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+props.groupName+"&blueprintName=&groupMembers="+username;
+        }else{
+            console.log(props.blueprintsOptions)
+            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+props.groupName+"&blueprintName="+props.blueprintsOptions[props.selectedBlueprint].label+"&groupMembers="+username;
+        }
+
+        axios(configApiCall(url, 'PUT', null, null)).then((response) => {
             setUserAdded(true);
+            props.getGroup()
             props.setOpenDrawer(false)
-            props.getAllUsers()
+            
         }).catch((error) =>{
             console.log("Error adding user: " + error)
             props.setOpenDrawer(false)
@@ -106,11 +115,11 @@ export default function TemporaryDrawer(props) {
                 addingToGroup
                     ?
                     <ListItem button key={user.username} onClick={() => {addUserToGroup(user.username)}} >
-                        <AddCircleOutlineIcon style={{ marginRight: "10px"}} /><ListItemText primary={user.firstName+" "+user.lastName} />
+                        <AddCircleOutlineIcon style={{ marginRight: "10px"}} /><ListItemText primary={user.username} />
                     </ListItem>
                     :
                     <ListItem button key={user.username} onClick={() => {addContactToUser(user.jamiId)}} >
-                        <AddCircleOutlineIcon style={{ marginRight: "10px"}} /><ListItemText primary={user.firstName+" "+user.lastName} />
+                        <AddCircleOutlineIcon style={{ marginRight: "10px"}} /><ListItemText primary={user.username} />
                     </ListItem>
 
             ))
diff --git a/jams-react-client/src/views/Groups/EditGroup.js b/jams-react-client/src/views/Groups/EditGroup.js
index b6f41582..9dfa1eec 100644
--- a/jams-react-client/src/views/Groups/EditGroup.js
+++ b/jams-react-client/src/views/Groups/EditGroup.js
@@ -95,82 +95,90 @@ export default function EditGroup(props) {
     const history = useHistory();
     const [groupExists, setGroupExists] = React.useState(false)
 
-    const [name, setName] = React.useState("")
+    const [name, setName] = React.useState(props.groupName)
+    const [blueprint, setBlueprint] = React.useState(null)
     const [groupMembers, setGroupMembers] = React.useState([])
 
     const [openDrawer, setOpenDrawer] = React.useState(false);
 
-    const getAllGroupMembers = () => {
-        /*
-            TODO: Include the username of the user of witch we want to display contacts
-            at the moment the admin sees his contacts in each user profile he visits
-        */
-
+    const getGroup = () => {
         axios(configApiCall(api_path_get_list_group+"?groupName="+props.groupName, 'GET', null, null)).then((response) => {
             let groups=response.data;
             if(groups.length > 1){
                 groups.map((group) => {
                     if(group.name == props.groupName){
+                        props.getBlueprintsOptions().forEach((blueprintOption) => {
+                            if(blueprintOption.label === group["blueprint"]){
+                                setBlueprint(blueprintOption.value)
+                            }
+                        })
                         setGroupMembers(group["groupMembers"]);
                     }
                 })
             }
             else{
                 if(groups.name == props.groupName){
-                    setGroupMembers(groups["groupMembers"]);
+                    props.getBlueprintsOptions().forEach((blueprintOption) => {
+                        if(blueprintOption.label === groups["blueprint"]){
+                            setBlueprint(blueprintOption.value)
+                        }
+                    })
+                    setGroupMembers(groups.groupMembers)
                 }
             }
+
         }).catch((error) => {
             console.log("Error fetching group members of: " + props.groupName + " " + error)
         })
     }
 
     React.useEffect(()=>{
-        setName(props.groupName)
-        axios(configApiCall(api_path_get_list_group+"?groupName="+props.groupName, 'GET', null, null)).then((response) => {
-            let groups=response.data;
-            if(groups.length > 1){
-                groups.map((group) => {
-                    if(group.name == props.groupName){
-                        setGroupMembers(group["groupMembers"]);
-                    }
-                })
-            }
-            else{
-                if(groups.name == props.groupName){
-                    setGroupMembers(groups["groupMembers"])
-                }
-            }
+        getGroup()
+
+    }, [])
+
+    const handleUpdateGroup = (blueprintValue) => {
+
+        let url = '';
+        if(blueprintValue == null){
+            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+name+"&blueprintName=&groupMembers=";
+        }else{
+            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+name+"&blueprintName="+props.getBlueprintsOptions()[blueprintValue].label+"&groupMembers=";
+        }
 
+        axios(configApiCall(url, 'PUT', null, null)).then((response) => {
+            getGroup()
         }).catch((error) => {
-            console.log("Error fetching group members of: " + props.groupName + " " + error)
+            console.log("Error updating group: " + error)
         })
-    }, [openDrawer])
-
-    const handleGroupUpdate = (data) => {
-        setName(data.name)
-        setGroupMembers(data.groupMembers)
     }
 
-    const handleUpdateGroup = () => {
-        const data = {
-            'name': name,
-            'groupMembers': groupMembers
+    const removeUserFromGroup = (user) => {
+
+        let url = '';
+        if(blueprint == null){
+            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+props.groupName+"&blueprintName=&groupMembers="+[user,];
+        }else{
+            url = api_path_put_update_group+"?groupName="+props.groupName+"&newName="+props.groupName+"&blueprintName="+props.getBlueprintsOptions()[blueprint].label+"&groupMembers="+[user,];
         }
-        //TODO: Add blueprint to data to update the blueprint using the blueprintName
 
-        axios(configApiCall(api_path_put_update_group+"?groupName="+name+"&groupMembers="+groupMembers, 'PUT', null, null)).then((response) => {
-            handleGroupUpdate(data);
+        axios(configApiCall(url, 'PUT', null, null)).then((response) => {
+            getGroup()
         }).catch((error) => {
             console.log("Error updating group: " + error)
         })
     }
 
+    const handleBlueprintsChange = (blueprintValue) => {
+        setBlueprint(blueprintValue)
+        handleUpdateGroup(blueprintValue)
+    }
+
     const tableCellClasses = classnames(classes.tableCell);
 
     return(
         <div>
-            <TemporaryDrawer openDrawer={openDrawer} setOpenDrawer={setOpenDrawer} getAllUsers={getAllGroupMembers} direction="right" addingToGroup={true} groupName={name === ''?props.groupName:name} />
+            <TemporaryDrawer openDrawer={openDrawer} setOpenDrawer={setOpenDrawer} direction="right" addingToGroup={true} groupName={name === ''?props.groupName:name} selectedBlueprint={blueprint} getGroup={getGroup} blueprintsOptions={props.getBlueprintsOptions()}/>
             <GridContainer>
                 <GridItem xs={12} sm={12} md={4}>
                     <Card profile>
@@ -198,7 +206,7 @@ export default function EditGroup(props) {
                                                     setName(e.target.value);
                                                     props.initCheckGroupNameExists(e.target.value)
                                                     if(!props.groupNameExits){
-                                                        handleUpdateGroup()
+                                                        handleUpdateGroup(blueprint)
                                                     }
                                                 }}
                                             />
@@ -209,8 +217,8 @@ export default function EditGroup(props) {
                                             <Select
                                                 labelId="demo-simple-select-label"
                                                 fullWidth
-                                                value={props.selectedBlueprint.value}
-                                                onChange={props.handleBlueprintsChange}
+                                                value={blueprint}
+                                                onChange={(e) => handleBlueprintsChange(e.target.value)}
                                                 variant="outlined"
                                                 children={props.blueprintsOptionsItems}
                                             />
@@ -237,7 +245,7 @@ export default function EditGroup(props) {
                                         {user}
                                     </TableCell>
                                     <TableCell align="right" className={classes.tableActions}>
-                                        <Button color="primary">Remove user</Button>
+                                        <Button color="primary" onClick={() => removeUserFromGroup(user)}>Remove user</Button>
                                     </TableCell>
                                 </TableRow>
                             ) }
diff --git a/jams-react-client/src/views/Groups/Groups.js b/jams-react-client/src/views/Groups/Groups.js
index eb7d071f..84df70fb 100644
--- a/jams-react-client/src/views/Groups/Groups.js
+++ b/jams-react-client/src/views/Groups/Groups.js
@@ -137,6 +137,7 @@ export default function Groups() {
         });
         axios(configApiCall(api_path_blueprints+"?name=*", 'GET', null, null)).then((response) => {
             setBlueprints(response.data)
+            setSelectedBlueprint(getBlueprintsOptions()[0])
         }).catch((error) =>{
             console.log(error);
             if(error.response.status === 401){
@@ -160,6 +161,12 @@ export default function Groups() {
         setSelectedGroupName(name)
     }
 
+    const redirectToGroupList = (e) => {
+        e.preventDefault()
+        setSelectedGroup(false);
+        history.push('/admin/groups')
+    }
+
     const handleCheckGroupNameExists = (searchGroupNameValue) => {
         axios(configApiCall(api_path_get_list_group+"?groupName="+searchGroupNameValue, 'GET', null, null)).then((response)=>{
             if(response.data === "[]"){
@@ -178,7 +185,11 @@ export default function Groups() {
 
     const handleCreateGroup = () => {
 
-        axios(configApiCall(api_path_post_create_group+"?name="+groupName, 'POST', null, null)).then(() => {
+        let blueprintName = '';
+        if (selectedBlueprint.label !== "No blueprint")
+            blueprintName = selectedBlueprint.label;
+
+        axios(configApiCall(api_path_post_create_group+"?name="+groupName+"&blueprintName="+blueprintName, 'POST', null, null)).then(() => {
             console.log("Successfully  created "  + groupName)
             setOpenCreate(false);
         }).catch((error) => {
@@ -189,38 +200,39 @@ export default function Groups() {
     }
 
 
-    const getGroupsOptions = () => {
+    const getBlueprintsOptions = () => {
         let blueprintsOptions = []
         let index = 0
         if(blueprints.length === 0)
             blueprintsOptions.push({ value: index, label: "No blueprint found"})
         else {
             blueprints.map((blueprint) => {
-                blueprintsOptions.push({value: index, label: blueprint.groupName})
+                blueprintsOptions.push({value: index, label: blueprint.name})
                 index += 1
             })
         }
         return blueprintsOptions
     }
 
-    const blueprintsOptionsItems = tool.buildSelectMenuItems(getGroupsOptions());
+    const blueprintsOptionsItems = tool.buildSelectMenuItems(getBlueprintsOptions());
 
     const [selectedBlueprint, setSelectedBlueprint] = useState({ value: 0, label: "No blueprint"})
 
     const handleBlueprintsChange = (e) => {
-        setSelectedBlueprint(getGroupsOptions()[e.target.value])
+        setSelectedBlueprint(getBlueprintsOptions()[e.target.value])
     }
 
     if(selectedGroup && auth.hasAdminScope()){
         return(
             <div>
                 <EditGroup
-                    groupName={selectedGroupName} 
-                    initCheckGroupNameExists={initCheckGroupNameExists} 
+                    groupName={selectedGroupName}
+                    initCheckGroupNameExists={initCheckGroupNameExists}
                     groupNameExits={groupNameExits}
                     blueprintsOptionsItems={blueprintsOptionsItems}
                     selectedBlueprint={selectedBlueprint}
                     handleBlueprintsChange={handleBlueprintsChange}
+                    getBlueprintsOptions={getBlueprintsOptions}
                 />
             </div>
         )
@@ -346,7 +358,7 @@ export default function Groups() {
                                             <h3 className={classes.cardTitle}>{group.name}</h3>
                                             <ul>
                                                 <li><PersonIcon fontSize='small'  style={{ marginRight: "10px"}}/><strong style={{ marginRight: "5px"}}>{group.groupMembers.length}</strong>users</li>
-                                                <li><MailOutlineIcon fontSize='small'  style={{ marginRight: "10px"}}/><strong style={{ marginRight: "5px"}}>Blueprint</strong>{selectedBlueprint.label}</li>
+                                                <li><MailOutlineIcon fontSize='small'  style={{ marginRight: "10px"}}/><strong style={{ marginRight: "5px"}}>Blueprint</strong>{group.blueprint}</li>
                                             </ul>
                                         </CardBody>
                                         <CardFooter>
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/GroupServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/GroupServlet.java
index 46bc2e01..ad6b4456 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/GroupServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/group/GroupServlet.java
@@ -14,6 +14,7 @@ import net.jami.jams.common.objects.user.*;
 import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler;
 import net.jami.jams.server.core.workflows.AddUserToGroupFlow;
 import net.jami.jams.server.core.workflows.RegisterDeviceFlow;
+import net.jami.jams.common.annotations.JsonContent;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -29,6 +30,7 @@ public class GroupServlet extends HttpServlet {
 
     @Override
     @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    @JsonContent
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 
         List<Group> groups = new ArrayList<>();
@@ -75,8 +77,11 @@ public class GroupServlet extends HttpServlet {
 
     @Override
     @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    @JsonContent
     protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-        String name = req.getParameter("newName");
+        String name = req.getParameter("groupName");
+        String newName = req.getParameter("newName");
+        String blueprint = req.getParameter("blueprintName");
         StatementList statementList = new StatementList();
         StatementElement st = new StatementElement("name", "=", name, "");
 
@@ -118,20 +123,31 @@ public class GroupServlet extends HttpServlet {
         }
 
         StatementList update = new StatementList();
-        StatementElement st0 = new StatementElement("name", "=", name, "");
+        StatementElement st0;
+
+        if (newName != null)
+            st0 = new StatementElement("name", "=", newName, "");
+        else
+            st0 = new StatementElement("name", "=", name, "");
+
+        StatementElement st1 = new StatementElement("blueprint", "=", blueprint, "");
         update.addStatement(st0);
+        update.addStatement(st1);
         StatementList constraint = new StatementList();
-        StatementElement st1 = new StatementElement("name", "=", name, "");
-        constraint.addStatement(st1);
+        StatementElement st2 = new StatementElement("name", "=", name, "");
+        constraint.addStatement(st2);
+
         if (dataStore.getGroupDao().updateObject(update, constraint)) resp.setStatus(200);
         else resp.sendError(500, "could not update the group's name!");
     }
 
     @Override
     @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    @JsonContent
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         Group group = new Group();
         group.setName(req.getParameter("name"));
+        group.setBlueprint(req.getParameter("blueprintName"));
         group.setGroupMembers(new ArrayList<String>());
         if (dataStore.getGroupDao().storeObject(group))
             resp.setStatus(200);
@@ -141,9 +157,10 @@ public class GroupServlet extends HttpServlet {
 
     @Override
     @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
+    @JsonContent
     protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
 
-        StatementElement statementElement = new StatementElement("groupName", "=", req.getParameter("groupName"), "");
+        StatementElement statementElement = new StatementElement("name", "=", req.getParameter("groupName"), "");
         StatementList constraint = new StatementList();
         constraint.addStatement(statementElement);
         if (dataStore.getGroupDao().deleteObject(constraint)) {
-- 
GitLab