From ba208ad173e1df85dd0ca05daa0b513e69f2a68b Mon Sep 17 00:00:00 2001 From: Smruti Ranjan Sahoo Date: Thu, 4 Nov 2021 12:19:25 -0700 Subject: [PATCH] added support for dashboard history api --- .../net/opentsdb/horizon/fs/model/File.java | 13 ++ .../horizon/fs/model/FileHistory.java | 67 ++++---- .../horizon/fs/store/FolderStore.java | 138 ++++++++++++++-- .../opentsdb/horizon/fs/view/ContentDto.java | 56 +++++++ .../net/opentsdb/horizon/fs/view/FileDto.java | 10 ++ .../horizon/fs/view/FileHistoryDto.java | 74 +++++++++ .../horizon/fs/view/FileHistoryListDto.java | 66 ++++++++ .../opentsdb/horizon/service/AuthService.java | 14 +- .../service/NamespaceMemberService.java | 28 ++-- .../horizon/service/AuthServiceTest.java | 5 +- .../service/NamespaceMemberServiceTest.java | 2 + .../opentsdb/horizon/ApplicationFactory.java | 10 +- .../horizon/resource/DashboardResource.java | 38 ++++- .../horizon/service/DashboardService.java | 148 ++++++++++++++++-- 14 files changed, 591 insertions(+), 78 deletions(-) create mode 100644 filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/ContentDto.java create mode 100644 filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileHistoryDto.java create mode 100644 filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileHistoryListDto.java diff --git a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/model/File.java b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/model/File.java index ca7c3cf..ac988d8 100644 --- a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/model/File.java +++ b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/model/File.java @@ -18,8 +18,13 @@ package net.opentsdb.horizon.fs.model; +import javax.persistence.Transient; + public class File extends Folder { + @Transient + private long historyId; + private byte[] content; public byte[] getContent() { @@ -29,4 +34,12 @@ public byte[] getContent() { public void setContent(byte[] content) { this.content = content; } + + public long getHistoryId() { + return historyId; + } + + public void setHistoryId(long historyId) { + this.historyId = historyId; + } } diff --git a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/model/FileHistory.java b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/model/FileHistory.java index d8f12b5..66865da 100644 --- a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/model/FileHistory.java +++ b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/model/FileHistory.java @@ -17,44 +17,55 @@ package net.opentsdb.horizon.fs.model; +import javax.persistence.Transient; import java.sql.Timestamp; public class FileHistory { - private long id; - private long fileid; - private byte[] contentid; - private Timestamp createdtime; + private long id; + private long fileid; + private byte[] contentid; + private Timestamp createdtime; + + @Transient private String createdBy; - public long getId() { - return id; - } + public long getId() { + return id; + } - public void setId(long id) { - this.id = id; - } + public void setId(long id) { + this.id = id; + } - public long getFileid() { - return fileid; - } + public Long getFileid() { + return fileid; + } - public void setFileid(long fileid) { - this.fileid = fileid; - } + public void setFileid(Long fileid) { + this.fileid = fileid; + } - public byte[] getContentid() { - return contentid; - } + public byte[] getContentid() { + return contentid; + } - public void setContentid(byte[] contentid) { - this.contentid = contentid; - } + public void setContentid(byte[] contentid) { + this.contentid = contentid; + } - public Timestamp getCreatedtime() { - return createdtime; - } + public Timestamp getCreatedtime() { + return createdtime; + } - public void setCreatedtime(Timestamp createdtime) { - this.createdtime = createdtime; - } + public void setCreatedtime(Timestamp createdtime) { + this.createdtime = createdtime; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } } diff --git a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/store/FolderStore.java b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/store/FolderStore.java index 417811e..6b9030a 100644 --- a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/store/FolderStore.java +++ b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/store/FolderStore.java @@ -262,12 +262,34 @@ public Folder getFolderByPathHash(FolderType folderType, byte[] pathHash, Connec public File getFileAndContentById(FolderType folderType, long id, Connection connection) throws SQLException { String sql = - "SELECT f.id, f.name, f.type, f.path, f.pathhash, f.parentpathhash, f.contentid, f.createdtime, f.createdby, f.updatedtime, f.updatedby, c.data " - + "FROM folder f INNER JOIN content c ON f.contentid = c.sha2 " + "SELECT f.id, f.name, f.type, f.path, f.pathhash, f.parentpathhash, f.contentid, f.createdtime, f.createdby, f.updatedtime, f.updatedby, fh.id as historyid, c.data FROM folder f " + + "INNER JOIN content c ON f.contentid = c.sha2 " + + "INNER JOIN folder_history fh ON c.sha2 = fh.contentid " + "WHERE f.type = ? AND f.id = ?"; return getFileAndContentById(folderType, id, connection, sql); } + public File getFileAndContentByHistoryId( + FolderType folderType, long id, long historyId, Connection connection) throws SQLException { + String sql = + "SELECT f.id, f.name, f.type, f.path, f.pathhash, f.parentpathhash, f.contentid, f.createdtime, f.createdby, f.updatedtime, f.updatedby, fh.id as historyid, c.data FROM folder f " + + "INNER JOIN folder_history fh ON f.id = fh.folderid " + + "INNER JOIN content c ON fh.contentid = c.sha2 " + + "WHERE f.type = ? AND f.id = ? AND fh.id = ?"; + File file = null; + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setByte(1, folderType.value); + statement.setLong(2, id); + statement.setLong(3, historyId); + try (final ResultSet rs = statement.executeQuery()) { + while (rs.next()) { + file = createFile(rs); + } + } + } + return file; + } + private File getFileAndContentById( FolderType folderType, long id, Connection connection, String sql) throws SQLException { File file = null; @@ -276,25 +298,31 @@ private File getFileAndContentById( statement.setLong(2, id); try (final ResultSet rs = statement.executeQuery()) { while (rs.next()) { - file = new File(); - file.setId(rs.getLong("id")); - file.setName(rs.getString("name")); - file.setType(FolderType.values()[rs.getInt("type")]); - file.setPath(rs.getString("path")); - file.setPathHash(rs.getBytes("pathhash")); - file.setParentPathHash(rs.getBytes("parentpathhash")); - file.setContentid(rs.getBytes("contentid")); - file.setCreatedTime(rs.getTimestamp("createdtime")); - file.setCreatedBy(rs.getString("createdby")); - file.setUpdatedTime(rs.getTimestamp("updatedtime")); - file.setUpdatedBy(rs.getString("updatedby")); - file.setContent(rs.getBytes("data")); + file = createFile(rs); } } } return file; } + private File createFile(ResultSet rs) throws SQLException { + File file = new File(); + file.setId(rs.getLong("id")); + file.setName(rs.getString("name")); + file.setType(FolderType.values()[rs.getInt("type")]); + file.setPath(rs.getString("path")); + file.setPathHash(rs.getBytes("pathhash")); + file.setParentPathHash(rs.getBytes("parentpathhash")); + file.setContentid(rs.getBytes("contentid")); + file.setCreatedTime(rs.getTimestamp("createdtime")); + file.setCreatedBy(rs.getString("createdby")); + file.setUpdatedTime(rs.getTimestamp("updatedtime")); + file.setUpdatedBy(rs.getString("updatedby")); + file.setHistoryId(rs.getLong("historyid")); + file.setContent(rs.getBytes("data")); + return file; + } + public File getFileById(FolderType folderType, long id, Connection connection) throws SQLException { String sql = "SELECT * FROM folder WHERE type = ? AND id = ? AND contentid IS NOT NULL"; @@ -379,6 +407,85 @@ public File getFileAndContentByPathHash( return file; } + public List getFileHistory(final long id, final int limit, final Connection connection) throws SQLException { + + String sql = + "SELECT fh.id, fh.createdtime, c.createdby FROM folder_history fh " + + "INNER JOIN content c ON fh.contentid = c.sha2 WHERE fh.folderid = ? ORDER BY fh.createdtime DESC LIMIT ?"; + + List history = new ArrayList(); + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setLong(1, id); + statement.setInt(2, limit); + try (final ResultSet rs = statement.executeQuery()) { + while (rs.next()) { + FileHistory fh = new FileHistory(); + fh.setId(rs.getLong("id")); + fh.setCreatedBy(rs.getString("createdby")); + fh.setCreatedtime(rs.getTimestamp("createdtime")); + history.add(fh); + } + } + } + return history; + } + + public long getDefaultFileHistoryId(long fileId, Connection connection) throws SQLException { + + String sql = + "SELECT fh.id FROM folder f INNER JOIN folder_history fh ON f.id = fh.folderid AND f.contentid = fh.contentid WHERE f.id = ?"; + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setLong(1, fileId); + try (final ResultSet rs = statement.executeQuery()) { + while (rs.next()) { + return rs.getLong("id"); + } + } + } + return -1; + } + + public FileHistory getFileHistory( + final long fileId, final long historyId, final Connection connection) throws SQLException { + + String sql = "SELECT contentid, createdtime FROM folder_history WHERE id = ? AND folderid = ?"; + + FileHistory history = null; + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setLong(1, historyId); + statement.setLong(2, fileId); + try (final ResultSet rs = statement.executeQuery()) { + while (rs.next()) { + history = new FileHistory(); + history.setId(historyId); + history.setFileid(fileId); + history.setContentid(rs.getBytes("contentid")); + history.setCreatedtime(rs.getTimestamp("createdtime")); + } + } + } + return history; + } + + public Content getContentByHistoryId(long historyId, Connection connection) throws SQLException { + String sql = "SELECT c.sha2, c.data, c.createdtime, c.createdby FROM folder_history fh " + + "INNER JOIN content c ON fh.contentid = c.sha2 WHERE fh.id = ?"; + Content content = null; + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setLong(1, historyId); + try (final ResultSet rs = statement.executeQuery()) { + while (rs.next()) { + content = new Content(); + content.setSha2(rs.getBytes("sha2")); + content.setData(rs.getBytes("data")); + content.setCreatedby(rs.getString("createdby")); + content.setCreatedtime(rs.getTimestamp("createdtime")); + } + } + } + return content; + } + public Content getContentById(byte[] sha2, Connection connection) throws SQLException { String sql = "SELECT data FROM content WHERE sha2 = ?"; Content content = null; @@ -513,4 +620,5 @@ public boolean isFavorite(String userId, long folderId, Connection connection) } return false; } + } diff --git a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/ContentDto.java b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/ContentDto.java new file mode 100644 index 0000000..4ca3cb2 --- /dev/null +++ b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/ContentDto.java @@ -0,0 +1,56 @@ +/* + * This file is part of OpenTSDB. + * Copyright (C) 2021 Yahoo. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.opentsdb.horizon.fs.view; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.sql.Timestamp; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ContentDto { + + private Object content; + private Timestamp createdTime; + private String createdBy; + + public Object getContent() { + return content; + } + + public void setContent(Object content) { + this.content = content; + } + + public Timestamp getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(Timestamp createdTime) { + this.createdTime = createdTime; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } +} diff --git a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileDto.java b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileDto.java index d5d3baf..9f6bbdd 100644 --- a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileDto.java +++ b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileDto.java @@ -26,6 +26,8 @@ public class FileDto extends FolderDto { private Object content; + private Long historyId; + public Object getContent() { return content; } @@ -33,4 +35,12 @@ public Object getContent() { public void setContent(Object content) { this.content = content; } + + public Long getHistoryId() { + return historyId; + } + + public void setHistoryId(Long historyId) { + this.historyId = historyId; + } } diff --git a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileHistoryDto.java b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileHistoryDto.java new file mode 100644 index 0000000..350f9a5 --- /dev/null +++ b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileHistoryDto.java @@ -0,0 +1,74 @@ +/* + * This file is part of OpenTSDB. + * Copyright (C) 2021 Yahoo. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.opentsdb.horizon.fs.view; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.sql.Timestamp; + +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +@JsonIgnoreProperties(ignoreUnknown = true) +public class FileHistoryDto { + + private long id; + private long fileId; + private byte[] contentId; + private Timestamp createdTime; + private String creatorId; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getFileId() { + return fileId; + } + + public void setFileId(long fileId) { + this.fileId = fileId; + } + + public byte[] getContentId() { + return contentId; + } + + public void setContentId(byte[] contentId) { + this.contentId = contentId; + } + + public Timestamp getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(Timestamp createdTime) { + this.createdTime = createdTime; + } + + public String getCreatorId() { + return creatorId; + } + + public void setCreatorId(String creatorId) { + this.creatorId = creatorId; + } +} diff --git a/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileHistoryListDto.java b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileHistoryListDto.java new file mode 100644 index 0000000..610122f --- /dev/null +++ b/filesystem-service/src/main/java/net/opentsdb/horizon/fs/view/FileHistoryListDto.java @@ -0,0 +1,66 @@ +/* + * This file is part of OpenTSDB. + * Copyright (C) 2021 Yahoo. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.opentsdb.horizon.fs.view; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class FileHistoryListDto { + + private long fileId; + private long defaultHistoryId; + private List histories; + private Map userNames; + + public long getFileId() { + return fileId; + } + + public void setFileId(long fileId) { + this.fileId = fileId; + } + + public long getDefaultHistoryId() { + return defaultHistoryId; + } + + public void setDefaultHistoryId(long defaultHistoryId) { + this.defaultHistoryId = defaultHistoryId; + } + + public List getHistories() { + return histories; + } + + public void setHistories(List histories) { + this.histories = histories; + } + + public Map getUserNames() { + return userNames; + } + + public void setUserNames(Map userNames) { + this.userNames = userNames; + } +} diff --git a/profile-service/src/main/java/net/opentsdb/horizon/service/AuthService.java b/profile-service/src/main/java/net/opentsdb/horizon/service/AuthService.java index 99d8f68..2d32121 100644 --- a/profile-service/src/main/java/net/opentsdb/horizon/service/AuthService.java +++ b/profile-service/src/main/java/net/opentsdb/horizon/service/AuthService.java @@ -35,21 +35,25 @@ public class AuthService { private static final Logger LOGGER = LoggerFactory.getLogger(AuthService.class); - public static final String PROVIDER_DOMAIN = "tsdb.property"; - public static final String PROVIDER_SERVICE = "monitoring"; public static final String ACCESS_ROLE_FORMAT = "%s.tenant.%s.res_group.namespace_%s.access"; private final NamespaceMemberStore memberStore; private final ZTSClient ztsClient; private final String athensDomain; + private String providerDomain; + private String providerService; public AuthService( final NamespaceMemberStore memberStore, final ZTSClient ztsClient, - final String athensDomain) { + final String athensDomain, + final String providerService, + final String providerDomain) { this.memberStore = memberStore; this.ztsClient = ztsClient; this.athensDomain = athensDomain; + this.providerService = providerService; + this.providerDomain = providerDomain; } public boolean isSuperAdmin(String principal) { @@ -89,7 +93,7 @@ private boolean isMember(int namespaceId, String principal) throws SQLException private boolean checkAccess( final String namespaceAlias, final String tenantDomain, final String principal) { String roleName = - String.format(ACCESS_ROLE_FORMAT, PROVIDER_SERVICE, tenantDomain, namespaceAlias); - return ztsClient.getAccess(PROVIDER_DOMAIN, roleName, principal).granted; + String.format(ACCESS_ROLE_FORMAT, providerService, tenantDomain, namespaceAlias); + return ztsClient.getAccess(providerDomain, roleName, principal).granted; } } diff --git a/profile-service/src/main/java/net/opentsdb/horizon/service/NamespaceMemberService.java b/profile-service/src/main/java/net/opentsdb/horizon/service/NamespaceMemberService.java index ff6eaea..33de461 100644 --- a/profile-service/src/main/java/net/opentsdb/horizon/service/NamespaceMemberService.java +++ b/profile-service/src/main/java/net/opentsdb/horizon/service/NamespaceMemberService.java @@ -17,6 +17,12 @@ package net.opentsdb.horizon.service; +import com.yahoo.athenz.zms.Role; +import com.yahoo.athenz.zms.RoleMember; +import com.yahoo.athenz.zms.ZMSClient; +import com.yahoo.athenz.zms.ZMSClientException; +import com.yahoo.athenz.zts.ZTSClient; +import com.yahoo.rdl.Timestamp; import net.opentsdb.horizon.NamespaceCache; import net.opentsdb.horizon.UserCache; import net.opentsdb.horizon.model.Namespace; @@ -25,12 +31,6 @@ import net.opentsdb.horizon.profile.Utils; import net.opentsdb.horizon.store.NamespaceFollowerStore; import net.opentsdb.horizon.store.NamespaceMemberStore; -import com.yahoo.athenz.zms.Role; -import com.yahoo.athenz.zms.RoleMember; -import com.yahoo.athenz.zms.ZMSClient; -import com.yahoo.athenz.zms.ZMSClientException; -import com.yahoo.athenz.zts.ZTSClient; -import com.yahoo.rdl.Timestamp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,8 +48,6 @@ import static net.opentsdb.horizon.profile.Utils.isAthensManaged; import static net.opentsdb.horizon.profile.Utils.validateNamespace; import static net.opentsdb.horizon.service.AuthService.ACCESS_ROLE_FORMAT; -import static net.opentsdb.horizon.service.AuthService.PROVIDER_DOMAIN; -import static net.opentsdb.horizon.service.AuthService.PROVIDER_SERVICE; import static net.opentsdb.horizon.service.BaseService.internalServerError; public class NamespaceMemberService { @@ -63,6 +61,8 @@ public class NamespaceMemberService { private ZMSClient zmsClient; private NamespaceCache namespaceCache; private UserCache userCache; + private String providerDomain; + private String providerService; public NamespaceMemberService( final NamespaceMemberStore memberStore, @@ -71,7 +71,9 @@ public NamespaceMemberService( final ZTSClient ztsClient, final ZMSClient zmsClient, final NamespaceCache namespaceCache, - final UserCache userCache) { + final UserCache userCache, + final String providerService, + final String providerDomain) { this.memberStore = memberStore; this.followerStore = followerStore; @@ -80,6 +82,8 @@ public NamespaceMemberService( this.zmsClient = zmsClient; this.namespaceCache = namespaceCache; this.userCache = userCache; + this.providerDomain = providerDomain; + this.providerService = providerService; } public List getNamespaceMember(final int namespaceId) { @@ -104,10 +108,10 @@ public List getNamespaceMember(final int namespaceId) { } } else { String roleName = - String.format(ACCESS_ROLE_FORMAT, PROVIDER_SERVICE, tenantDomain, namespace.getAlias()); + String.format(ACCESS_ROLE_FORMAT, providerService, tenantDomain, namespace.getAlias()); Role role; try { - role = zmsClient.getRole(PROVIDER_DOMAIN, roleName, false, true); + role = zmsClient.getRole(providerDomain, roleName, false, true); } catch (ZMSClientException e) { String message = "Error reading members for namespace: " + namespace.getName(); LOGGER.error(message, e); @@ -284,7 +288,7 @@ List getNamespaces(String userId, Connection connection) { LOGGER.debug("userId: {} namespaces: {}", userId, namespaces); if (ztsClient != null) { - List roles = ztsClient.getRoleAccess("tsdb.property", userId).getRoles(); + List roles = ztsClient.getRoleAccess(providerDomain, userId).getRoles(); LOGGER.debug("userId: {} athens roles: {}", userId, roles); Set tenantNamespaces = Utils.parseNamespaces(roles); diff --git a/profile-service/src/test/java/net/opentsdb/horizon/service/AuthServiceTest.java b/profile-service/src/test/java/net/opentsdb/horizon/service/AuthServiceTest.java index f485a1c..46648d3 100644 --- a/profile-service/src/test/java/net/opentsdb/horizon/service/AuthServiceTest.java +++ b/profile-service/src/test/java/net/opentsdb/horizon/service/AuthServiceTest.java @@ -43,12 +43,11 @@ public class AuthServiceTest { @Tested private AuthService authService; @Injectable private NamespaceMemberStore namespaceMemberStore; - @Injectable private ZTSClient ztsClient; - @Mocked private Connection connection; - @Injectable private String athensDomain; + @Injectable private String providerDomain; + @Injectable private String providerService; @ParameterizedTest @MethodSource("testAuthorization") diff --git a/profile-service/src/test/java/net/opentsdb/horizon/service/NamespaceMemberServiceTest.java b/profile-service/src/test/java/net/opentsdb/horizon/service/NamespaceMemberServiceTest.java index 925c3f9..fec58c3 100644 --- a/profile-service/src/test/java/net/opentsdb/horizon/service/NamespaceMemberServiceTest.java +++ b/profile-service/src/test/java/net/opentsdb/horizon/service/NamespaceMemberServiceTest.java @@ -53,6 +53,8 @@ public class NamespaceMemberServiceTest { @Injectable private ZMSClient zmsClient; @Injectable private NamespaceCache namespaceCache; @Injectable private UserCache userCache; + @Injectable private String providerDomain; + @Injectable private String providerService; @Mocked private Connection connection; diff --git a/webapp/src/main/java/net/opentsdb/horizon/ApplicationFactory.java b/webapp/src/main/java/net/opentsdb/horizon/ApplicationFactory.java index d7c10ff..0786791 100644 --- a/webapp/src/main/java/net/opentsdb/horizon/ApplicationFactory.java +++ b/webapp/src/main/java/net/opentsdb/horizon/ApplicationFactory.java @@ -202,7 +202,10 @@ public Application buildApplication() throws Exception { zmsClient = new ZMSClient(zmsUrl, athenzSSlContext); } - AuthService authService = new AuthService(namespaceMemberStore, ztsClient, athensDomain); + String athenzProviderService = (String) appParams.get("athenzService"); + String athenzServiceProviderDomain = (String) appParams.get("athenzServiceProviderDomain"); + + AuthService authService = new AuthService(namespaceMemberStore, ztsClient, athensDomain, athenzProviderService, athenzServiceProviderDomain); NamespaceFollowerService namespaceFollowerService = new NamespaceFollowerService(namespaceFollowerStore, authService, namespaceCache); NamespaceMemberService namespaceMemberService = @@ -213,7 +216,9 @@ public Application buildApplication() throws Exception { ztsClient, zmsClient, namespaceCache, - userCache); + userCache, + athenzProviderService, + athenzServiceProviderDomain); NamespaceService namespaceService = new NamespaceService( namespaceStore, @@ -235,6 +240,7 @@ public Application buildApplication() throws Exception { namespaceMemberService, namespaceFollowerStore, namespaceCache, + userCache, authService, userStore, digest, diff --git a/webapp/src/main/java/net/opentsdb/horizon/resource/DashboardResource.java b/webapp/src/main/java/net/opentsdb/horizon/resource/DashboardResource.java index 2fdc504..18e6ecf 100644 --- a/webapp/src/main/java/net/opentsdb/horizon/resource/DashboardResource.java +++ b/webapp/src/main/java/net/opentsdb/horizon/resource/DashboardResource.java @@ -18,7 +18,10 @@ package net.opentsdb.horizon.resource; +import net.opentsdb.horizon.fs.view.ContentDto; import net.opentsdb.horizon.fs.view.FileDto; +import net.opentsdb.horizon.fs.view.FileHistoryDto; +import net.opentsdb.horizon.fs.view.FileHistoryListDto; import net.opentsdb.horizon.fs.view.FolderDto; import net.opentsdb.horizon.service.DashboardService; import net.opentsdb.horizon.view.MoveRequest; @@ -111,12 +114,43 @@ public Response getFolderById(@PathParam("id") long id, @Context HttpServletRequ @Path("file/{id}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - public Response getFileById(@PathParam("id") long id, @Context HttpServletRequest request) { - FileDto file = dashboardService.getFileById(id, request.getUserPrincipal().getName()); + public Response getFileById(@PathParam("id") long id, @DefaultValue("0") @QueryParam("historyId") long historyId, @Context HttpServletRequest request) { + FileDto file = dashboardService.getFileById(id, historyId, request.getUserPrincipal().getName()); final Response.ResponseBuilder responseBuilder = file == null ? Response.status(Response.Status.NOT_FOUND) : Response.status(Response.Status.OK); return responseBuilder.entity(file).build(); } + @ApiOperation("Get File history") + @GET + @Path("file/{id}/history") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response getFileHistory(@PathParam("id") long id, @Context HttpServletRequest request) { + FileHistoryListDto histories = dashboardService.getFileHistory(id); + return Response.status(Response.Status.OK).entity(histories).build(); + } + + @ApiOperation("Get File content") + @GET + @Path("file/history/{historyId}/content") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response getFileContent(@PathParam("historyId") long historyId, @Context HttpServletRequest request) { + ContentDto content = dashboardService.getFileContent(historyId); + final Response.ResponseBuilder responseBuilder = content == null ? Response.status(Response.Status.NOT_FOUND) : Response.status(Response.Status.OK); + return responseBuilder.entity(content).build(); + } + + @ApiOperation("Set default content") + @PUT + @Path("file/{id}/content") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response setDefaultContent(@PathParam("id") long id, FileDto file, @Context HttpServletRequest request) { + FileDto view = dashboardService.setDefaultContent(id, file.getHistoryId(), request.getUserPrincipal().getName()); + return Response.status(Response.Status.OK).entity(view).build(); + } + @ApiOperation("Get by path") @GET @Path("{path:.*}") diff --git a/webapp/src/main/java/net/opentsdb/horizon/service/DashboardService.java b/webapp/src/main/java/net/opentsdb/horizon/service/DashboardService.java index 45ec3f6..d935108 100644 --- a/webapp/src/main/java/net/opentsdb/horizon/service/DashboardService.java +++ b/webapp/src/main/java/net/opentsdb/horizon/service/DashboardService.java @@ -18,6 +18,7 @@ package net.opentsdb.horizon.service; import net.opentsdb.horizon.NamespaceCache; +import net.opentsdb.horizon.UserCache; import net.opentsdb.horizon.fs.Path; import net.opentsdb.horizon.fs.Path.PathException; import net.opentsdb.horizon.fs.Path.RootType; @@ -26,7 +27,10 @@ import net.opentsdb.horizon.fs.model.FileHistory; import net.opentsdb.horizon.fs.model.Folder; import net.opentsdb.horizon.fs.store.FolderStore; +import net.opentsdb.horizon.fs.view.ContentDto; import net.opentsdb.horizon.fs.view.FileDto; +import net.opentsdb.horizon.fs.view.FileHistoryDto; +import net.opentsdb.horizon.fs.view.FileHistoryListDto; import net.opentsdb.horizon.fs.view.FolderDto; import net.opentsdb.horizon.fs.view.FolderType; import net.opentsdb.horizon.model.Namespace; @@ -49,7 +53,9 @@ import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static net.opentsdb.horizon.service.BaseService.badRequestException; @@ -71,16 +77,18 @@ public class DashboardService { private final NamespaceMemberService namespaceMemberService; private final NamespaceFollowerStore namespaceFollowerStore; private final NamespaceCache namespaceCache; + private final UserCache userCache; private final AuthService authService; - private UserStore userStore; + private final UserStore userStore; private final MessageDigest digest; - private DashboardActivityJobScheduler activityJobScheduler; + private final DashboardActivityJobScheduler activityJobScheduler; public DashboardService( final FolderStore folderStore, final NamespaceMemberService namespaceMemberService, final NamespaceFollowerStore namespaceFollowerStore, - NamespaceCache namespaceCache, + final NamespaceCache namespaceCache, + final UserCache userCache, final AuthService authService, final UserStore userStore, final MessageDigest digest, @@ -90,6 +98,7 @@ public DashboardService( this.namespaceMemberService = namespaceMemberService; this.namespaceFollowerStore = namespaceFollowerStore; this.namespaceCache = namespaceCache; + this.userCache = userCache; this.authService = authService; this.userStore = userStore; this.digest = digest; @@ -417,24 +426,107 @@ public FolderDto getFolderById(final long id, final String userId) { } } - public FileDto getFileById(final long id, final String userId) { + public FileDto getFileById(final long id, final long historyId, final String userId) { try (Connection connection = folderStore.getReadOnlyConnection()) { - File model = folderStore.getFileAndContentById(FolderType.DASHBOARD, id, connection); - if (null == model) { + return doGetFileDto(id, historyId, userId, connection); + } catch (SQLException | IOException e) { + String message = "Error reading dashboard with id: " + id; + LOGGER.error(message, e); + throw internalServerError(message); + } + } + + private FileDto doGetFileDto(long id, long historyId, String userId, Connection connection) + throws SQLException, IOException { + File model; + if (historyId > 0) { + model = + folderStore.getFileAndContentByHistoryId(FolderType.DASHBOARD, id, historyId, connection); + } else { + model = folderStore.getFileAndContentById(FolderType.DASHBOARD, id, connection); + } + + if (model == null) { + StringBuilder sb = new StringBuilder("Dashboard not found with id: ").append(id); + if (historyId > 0) { + sb.append(" and historyId: ").append(historyId); + } + throw notFoundException(sb.toString()); + } + + boolean favorite = folderStore.isFavorite(userId, id, connection); + activityJobScheduler.addActivity(id, userId); + FileDto view = modelToView(model); + view.setFavorite(favorite); + return view; + } + + public FileHistoryListDto getFileHistory(final long id) { + try (Connection connection = folderStore.getReadOnlyConnection()) { + List fileHistory = folderStore.getFileHistory(id, 50, connection); + if (fileHistory.isEmpty()) { throw notFoundException("Dashboard not found with id: " + id); } - boolean favorite = folderStore.isFavorite(userId, id, connection); - activityJobScheduler.addActivity(id, userId); - FileDto view = modelToView(model); - view.setFavorite(favorite); + FileHistoryListDto view = viewToModel(fileHistory); + view.setFileId(id); + long historyId = folderStore.getDefaultFileHistoryId(id, connection); + view.setDefaultHistoryId(historyId); return view; + } catch (SQLException e) { + String message = "Error reading history for dashboard with id: " + id; + LOGGER.error(message, e); + throw internalServerError(message); + } + } + + public ContentDto getFileContent(final long historyId) { + try (Connection connection = folderStore.getReadOnlyConnection()) { + Content content = folderStore.getContentByHistoryId(historyId, connection); + if (content == null) { + throw notFoundException("Content not found with history id: " + historyId); + } + return modelToView(content); } catch (SQLException | IOException e) { - String message = "Error reading dashboard with id: " + id; + String message = "Error reading content for history with id: " + historyId; LOGGER.error(message, e); throw internalServerError(message); } } + public FileDto setDefaultContent(long id, long historyId, String userId) { + String errorMessage = "Error setting dashboard content"; + try (Connection con = folderStore.getReadWriteConnection()) { + try { + File file = folderStore.getFileById(FolderType.DASHBOARD, id, con); + if (file == null) { + String message = "File not found with id: " + id; + throw notFoundException(message); + } + + Path path = Path.get(file.getPath()); + checkAccess(path, userId); + FileHistory fileHistory = folderStore.getFileHistory(id, historyId, con); + if (fileHistory == null) { + throw badRequestException("Invalid historyId"); + } + file.setContent(fileHistory.getContentid()); + file.setUpdatedBy(userId); + file.setUpdatedTime(new Timestamp(System.currentTimeMillis())); + folderStore.updateFile(file, con); + return doGetFileDto(id, historyId, userId, con); + } catch (Exception e) { + folderStore.rollback(con); + throw e; + } + } catch (PathException e) { + LOGGER.error(errorMessage, e); + throw badRequestException(e.getMessage()); + } catch (SQLException | IOException e) { + LOGGER.error(errorMessage, e); + throw internalServerError(errorMessage); + } + } + public FolderDto getByPath(final String pathString, final String userId) { String idString = pathString.split("/")[pathString.startsWith("/") ? 1 : 0]; @@ -738,6 +830,38 @@ private void checkAccess(Path path, String principal) throws SQLException { } } + private FileHistoryListDto viewToModel(List models) { + List viewList = new ArrayList<>(); + Map userNameMap = new HashMap<>(); + for (int i = 0; i < models.size(); i++) { + FileHistory model = models.get(i); + FileHistoryDto view = new FileHistoryDto(); + view.setId(model.getId()); + view.setContentId(model.getContentid()); + view.setCreatedTime(model.getCreatedtime()); + String userId = model.getCreatedBy(); + view.setCreatorId(userId); + + userNameMap.putIfAbsent(userId, userCache.getById(userId).getName()); + viewList.add(view); + } + FileHistoryListDto listDto = new FileHistoryListDto(); + listDto.setHistories(viewList); + listDto.setUserNames(userNameMap); + return listDto; + } + + private ContentDto modelToView(Content model) throws IOException { + byte[] compressed = model.getData(); + byte[] decompressed = decompress(compressed); + Object deSerialized = deSerialize(decompressed, Object.class); + ContentDto view = new ContentDto(); + view.setContent(deSerialized); + view.setCreatedBy(model.getCreatedby()); + view.setCreatedTime(model.getCreatedtime()); + return view; + } + private FolderDto modelToView(Folder model) { FolderDto dto = new FolderDto(); modelToView(model, dto); @@ -752,6 +876,7 @@ private FileDto modelToView(File model) throws IOException { byte[] decompressed = decompress(compressed); Object deSerialized = deSerialize(decompressed, Object.class); dto.setContent(deSerialized); + dto.setHistoryId(model.getHistoryId()); } return dto; } @@ -802,4 +927,5 @@ private Content createContent(Object content) throws IOException { byte[] sha2 = digest.digest(serialized); return new Content(sha2, compress(serialized)); } + }