From d2d3a98b8196a80099c4ff5d8949e6f96e4af91c Mon Sep 17 00:00:00 2001 From: Sonia Zorba <sonia.zorba@inaf.it> Date: Tue, 6 Aug 2019 16:51:09 +0200 Subject: [PATCH] Created SQL and DAO in order to use Postgres ltree extension --- database/Dockerfile | 2 + gms/pom.xml | 6 ++ .../inaf/ia2/gms/persistence/GroupsDAO.java | 101 ++++++++++++++++++ .../ia2/gms/persistence/PermissionsDAO.java | 72 +++++++++++++ .../gms/persistence/model/UserPermission.java | 34 ++++++ gms/src/main/resources/sql/init.sql | 29 +++++ .../ia2/gms/persistence/GroupsDAOTest.java | 92 ++++++++++++++++ .../gms/persistence/PermissionsDAOTest.java | 64 +++++++++++ 8 files changed, 400 insertions(+) create mode 100644 database/Dockerfile create mode 100644 gms/src/main/java/it/inaf/ia2/gms/persistence/GroupsDAO.java create mode 100644 gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java create mode 100644 gms/src/main/java/it/inaf/ia2/gms/persistence/model/UserPermission.java create mode 100644 gms/src/main/resources/sql/init.sql create mode 100644 gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java create mode 100644 gms/src/test/java/it/inaf/ia2/gms/persistence/PermissionsDAOTest.java diff --git a/database/Dockerfile b/database/Dockerfile new file mode 100644 index 0000000..d174c9d --- /dev/null +++ b/database/Dockerfile @@ -0,0 +1,2 @@ +FROM library/postgres:11 +COPY ../gms/src/main/resources/sql/init.sql /docker-entrypoint-initdb.d/ diff --git a/gms/pom.xml b/gms/pom.xml index a24ffb0..905ed51 100644 --- a/gms/pom.xml +++ b/gms/pom.xml @@ -63,6 +63,12 @@ <artifactId>h2</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>io.zonky.test</groupId> + <artifactId>embedded-database-spring-test</artifactId> + <version>1.5.0</version> + <scope>test</scope> + </dependency> </dependencies> <build> diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/GroupsDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/GroupsDAO.java new file mode 100644 index 0000000..f1109fa --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/GroupsDAO.java @@ -0,0 +1,101 @@ +package it.inaf.ia2.gms.persistence; + +import it.inaf.ia2.gms.persistence.model.NewGroup; +import java.sql.PreparedStatement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.sql.DataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +@Component +public class GroupsDAO { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public GroupsDAO(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + public NewGroup createGroup(NewGroup group) { + + String sql = "INSERT INTO gms_group (id, name, path) VALUES (?, ?, ?)"; + + jdbcTemplate.update(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + ps.setString(1, group.getId()); + ps.setString(2, group.getName()); + ps.setObject(3, group.getPath(), Types.OTHER); + return ps; + }); + + return group; + } + + public void deleteGroupById(String groupId) { + String sql = "DELETE FROM gms_group WHERE id = ?"; + jdbcTemplate.update(sql, groupId); + } + + public List<NewGroup> listSubGroups(String path) { + + String sql = "SELECT id, name, path from gms_group WHERE path ~ ? ORDER BY name"; + + return jdbcTemplate.query(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + ps.setObject(1, getSubGroupsPath(path), Types.OTHER); + return ps; + }, resultSet -> { + List<NewGroup> groups = new ArrayList<>(); + while (resultSet.next()) { + NewGroup group = new NewGroup(); + group.setId(resultSet.getString("id")); + group.setName(resultSet.getString("name")); + group.setPath(resultSet.getString("path")); + groups.add(group); + } + return groups; + }); + } + + private String getSubGroupsPath(String path) { + if (!path.isEmpty()) { + path += "."; + } + path += "*{1}"; + return path; + } + + public Map<String, Boolean> getHasChildrenMap(Set<String> groupIds) { + + String sql = "SELECT g.id, COUNT(s.*) > 0 AS has_children \n" + + "FROM gms_group g\n" + + "LEFT JOIN gms_group s ON s.path <@ g.path AND s.path <> g.path\n" + + "WHERE g.id IN(" + + String.join(",", groupIds.stream().map(g -> "?").collect(Collectors.toList())) + + ") GROUP BY g.id"; + + return jdbcTemplate.query(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + int i = 0; + for (String groupId : groupIds) { + ps.setString(++i, groupId); + } + + return ps; + }, resultSet -> { + Map<String, Boolean> map = new HashMap<>(); + while (resultSet.next()) { + map.put(resultSet.getString("id"), resultSet.getBoolean("has_children")); + } + return map; + }); + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java new file mode 100644 index 0000000..0c48022 --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java @@ -0,0 +1,72 @@ +package it.inaf.ia2.gms.persistence; + +import it.inaf.ia2.gms.model.Permission; +import it.inaf.ia2.gms.persistence.model.UserPermission; +import java.sql.PreparedStatement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import javax.sql.DataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +@Component +public class PermissionsDAO { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public PermissionsDAO(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + public UserPermission createPermission(UserPermission userPermission) { + + String sql = "INSERT INTO gms_permission(group_id, user_id, permission) VALUES(?, ?, ?)"; + + jdbcTemplate.update(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + ps.setString(1, userPermission.getGroupId()); + ps.setString(2, userPermission.getUserId()); + ps.setObject(3, userPermission.getPermission().toString(), Types.OTHER); + return ps; + }); + + return userPermission; + } + + public List<UserPermission> findUserPermissions(String userId) { + + String sql = "SELECT group_id, permission FROM gms_permission WHERE user_id = ?"; + + return jdbcTemplate.query(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + ps.setString(1, userId); + return ps; + }, resultSet -> { + List<UserPermission> permissions = new ArrayList<>(); + while (resultSet.next()) { + UserPermission permission = new UserPermission(); + permission.setGroupId(resultSet.getString("group_id")); + permission.setUserId(userId); + permission.setPermission(Permission.valueOf(resultSet.getString("permission"))); + permissions.add(permission); + } + return permissions; + }); + } + + public void deletePermission(UserPermission userPermission) { + + String sql = "DELETE FROM gms_permission WHERE group_id = ? AND user_id = ? AND permission = ?"; + + jdbcTemplate.update(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + ps.setString(1, userPermission.getGroupId()); + ps.setString(2, userPermission.getUserId()); + ps.setObject(3, userPermission.getPermission().toString(), Types.OTHER); + return ps; + }); + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/model/UserPermission.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/UserPermission.java new file mode 100644 index 0000000..6d1e839 --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/UserPermission.java @@ -0,0 +1,34 @@ +package it.inaf.ia2.gms.persistence.model; + +import it.inaf.ia2.gms.model.Permission; + +public class UserPermission { + + private String userId; + private String groupId; + private Permission permission; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public Permission getPermission() { + return permission; + } + + public void setPermission(Permission permission) { + this.permission = permission; + } +} diff --git a/gms/src/main/resources/sql/init.sql b/gms/src/main/resources/sql/init.sql new file mode 100644 index 0000000..780ce0e --- /dev/null +++ b/gms/src/main/resources/sql/init.sql @@ -0,0 +1,29 @@ +CREATE EXTENSION IF NOT EXISTS ltree; + +CREATE TABLE gms_group ( + id varchar NOT NULL, + name text NOT NULL, + path ltree NOT NULL, + primary key(id) +); + +CREATE INDEX group_path_gist_idx ON gms_group USING GIST(path); +CREATE INDEX group_path_idx ON gms_group USING btree(path); +CREATE INDEX group_name_idx ON gms_group USING btree(name); + +CREATE TABLE gms_membership ( + group_id varchar NOT NULL, + user_id varchar NOT NULL, + primary key (group_id, user_id), + foreign key (group_id) references gms_group(id) +); + +CREATE TYPE permission_type AS ENUM ('VIEW_MEMBERS', 'MANAGE_MEMBERS', 'ADMIN'); + +CREATE TABLE gms_permission ( + group_id varchar NOT NULL, + user_id varchar NOT NULL, + permission permission_type NOT NULL, + primary key (group_id, user_id, permission), + foreign key (group_id) references gms_group(id) +); diff --git a/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java b/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java new file mode 100644 index 0000000..5309478 --- /dev/null +++ b/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java @@ -0,0 +1,92 @@ +package it.inaf.ia2.gms.persistence; + +import com.google.common.collect.ImmutableSet; +import io.zonky.test.db.AutoConfigureEmbeddedDatabase; +import it.inaf.ia2.gms.persistence.model.NewGroup; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.sql.DataSource; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@AutoConfigureEmbeddedDatabase(beanName = "dataSource") +public class GroupsDAOTest { + + @Autowired + private DataSource dataSource; + + private GroupsDAO dao; + + @Before + public void setUp() { + dao = new GroupsDAO(dataSource); + } + + @Test + @Sql("/sql/init.sql") + public void testAll() { + + // Create groups + NewGroup root = new NewGroup(); + root.setId("ROOT"); + root.setName("ROOT"); + root.setPath(""); + dao.createGroup(root); + + NewGroup lbt = new NewGroup(); + lbt.setId(getNewGroupId()); + lbt.setName("LBT"); + lbt.setPath(lbt.getId()); + dao.createGroup(lbt); + + NewGroup tng = new NewGroup(); + tng.setId(getNewGroupId()); + tng.setName("TNG"); + tng.setPath(tng.getId()); + dao.createGroup(tng); + + NewGroup lbtInaf = new NewGroup(); + lbtInaf.setId(getNewGroupId()); + lbtInaf.setName("INAF"); + lbtInaf.setPath(lbt.getId() + "." + lbtInaf.getId()); + dao.createGroup(lbtInaf); + + // Sub list + List<NewGroup> groups = dao.listSubGroups(""); + assertEquals(2, groups.size()); + assertEquals("LBT", groups.get(0).getName()); + assertEquals("TNG", groups.get(1).getName()); + + groups = dao.listSubGroups(lbt.getId()); + assertEquals(1, groups.size()); + assertEquals("INAF", groups.get(0).getName()); + + // Children map + Map<String, Boolean> childrenMap = dao.getHasChildrenMap(ImmutableSet.of(root.getId())); + assertEquals(1, childrenMap.size()); + assertTrue(childrenMap.get(root.getId())); + + childrenMap = dao.getHasChildrenMap(ImmutableSet.of(lbt.getId(), tng.getId())); + assertEquals(2, childrenMap.size()); + assertTrue(childrenMap.get(lbt.getId())); + assertFalse(childrenMap.get(tng.getId())); + + // Delete + dao.deleteGroupById(lbtInaf.getId()); + groups = dao.listSubGroups(lbt.getId()); + assertTrue(groups.isEmpty()); + } + + private String getNewGroupId() { + return UUID.randomUUID().toString().replaceAll("-", ""); + } +} diff --git a/gms/src/test/java/it/inaf/ia2/gms/persistence/PermissionsDAOTest.java b/gms/src/test/java/it/inaf/ia2/gms/persistence/PermissionsDAOTest.java new file mode 100644 index 0000000..b5c9a8e --- /dev/null +++ b/gms/src/test/java/it/inaf/ia2/gms/persistence/PermissionsDAOTest.java @@ -0,0 +1,64 @@ +package it.inaf.ia2.gms.persistence; + +import io.zonky.test.db.AutoConfigureEmbeddedDatabase; +import it.inaf.ia2.gms.model.Permission; +import it.inaf.ia2.gms.persistence.model.UserPermission; +import it.inaf.ia2.gms.persistence.model.NewGroup; +import java.util.List; +import javax.sql.DataSource; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@AutoConfigureEmbeddedDatabase(beanName = "dataSource") +public class PermissionsDAOTest { + + @Autowired + private DataSource dataSource; + + private GroupsDAO groupsDAO; + private PermissionsDAO permissionsDAO; + + @Before + public void setUp() { + groupsDAO = new GroupsDAO(dataSource); + permissionsDAO = new PermissionsDAO(dataSource); + } + + @Test + @Sql("/sql/init.sql") + public void testAll() { + + NewGroup root = new NewGroup(); + root.setId("ROOT"); + root.setName("ROOT"); + root.setPath(""); + groupsDAO.createGroup(root); + + String userId = "user_id"; + + UserPermission permission = new UserPermission(); + permission.setGroupId(root.getId()); + permission.setUserId(userId); + permission.setPermission(Permission.ADMIN); + + permissionsDAO.createPermission(permission); + + List<UserPermission> permissions = permissionsDAO.findUserPermissions(userId); + + assertEquals(1, permissions.size()); + assertEquals(Permission.ADMIN, permissions.get(0).getPermission()); + assertEquals(userId, permissions.get(0).getUserId()); + + permissionsDAO.deletePermission(permission); + + permissions = permissionsDAO.findUserPermissions(userId); + assertTrue(permissions.isEmpty()); + } +} -- GitLab