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