Merge branch 'feature/entities-version-control' of github.com:thingsboard/thingsboard into feature/entities-version-control
This commit is contained in:
commit
bf03475987
@ -119,7 +119,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont
|
|||||||
@SuppressWarnings("UnstableApiUsage")
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<VersionCreationResult> saveEntitiesVersion(SecurityUser user, VersionCreateRequest request) throws Exception {
|
public ListenableFuture<VersionCreationResult> saveEntitiesVersion(SecurityUser user, VersionCreateRequest request) throws Exception {
|
||||||
var pendingCommit = gitServiceQueue.prepareCommit(user.getTenantId(), request);
|
var pendingCommit = gitServiceQueue.prepareCommit(user, request);
|
||||||
|
|
||||||
return transformAsync(pendingCommit, commit -> {
|
return transformAsync(pendingCommit, commit -> {
|
||||||
List<ListenableFuture<Void>> gitFutures = new ArrayList<>();
|
List<ListenableFuture<Void>> gitFutures = new ArrayList<>();
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import org.thingsboard.server.cluster.TbClusterService;
|
|||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.ExportableEntity;
|
import org.thingsboard.server.common.data.ExportableEntity;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
@ -66,10 +67,7 @@ import org.thingsboard.server.service.sync.vc.data.PendingGitRequest;
|
|||||||
import org.thingsboard.server.service.sync.vc.data.VersionsDiffGitRequest;
|
import org.thingsboard.server.service.sync.vc.data.VersionsDiffGitRequest;
|
||||||
import org.thingsboard.server.service.sync.vc.data.VoidGitRequest;
|
import org.thingsboard.server.service.sync.vc.data.VoidGitRequest;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -96,12 +94,12 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<CommitGitRequest> prepareCommit(TenantId tenantId, VersionCreateRequest request) {
|
public ListenableFuture<CommitGitRequest> prepareCommit(User user, VersionCreateRequest request) {
|
||||||
SettableFuture<CommitGitRequest> future = SettableFuture.create();
|
SettableFuture<CommitGitRequest> future = SettableFuture.create();
|
||||||
|
|
||||||
CommitGitRequest commit = new CommitGitRequest(tenantId, request);
|
CommitGitRequest commit = new CommitGitRequest(user.getTenantId(), request);
|
||||||
registerAndSend(commit, builder -> builder.setCommitRequest(
|
registerAndSend(commit, builder -> builder.setCommitRequest(
|
||||||
buildCommitRequest(commit).setPrepareMsg(getCommitPrepareMsg(request)).build()
|
buildCommitRequest(commit).setPrepareMsg(getCommitPrepareMsg(user, request)).build()
|
||||||
).build(), wrap(future, commit));
|
).build(), wrap(future, commit));
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
@ -356,7 +354,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
|
|||||||
} else if (vcResponseMsg.hasCommitResponse()) {
|
} else if (vcResponseMsg.hasCommitResponse()) {
|
||||||
var commitResponse = vcResponseMsg.getCommitResponse();
|
var commitResponse = vcResponseMsg.getCommitResponse();
|
||||||
var commitResult = new VersionCreationResult();
|
var commitResult = new VersionCreationResult();
|
||||||
commitResult.setVersion(new EntityVersion(commitResponse.getTs(), commitResponse.getCommitId(), commitResponse.getName()));
|
commitResult.setVersion(new EntityVersion(commitResponse.getTs(), commitResponse.getCommitId(), commitResponse.getName(), commitResponse.getAuthor()));
|
||||||
commitResult.setAdded(commitResponse.getAdded());
|
commitResult.setAdded(commitResponse.getAdded());
|
||||||
commitResult.setRemoved(commitResponse.getRemoved());
|
commitResult.setRemoved(commitResponse.getRemoved());
|
||||||
commitResult.setModified(commitResponse.getModified());
|
commitResult.setModified(commitResponse.getModified());
|
||||||
@ -405,7 +403,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
|
|||||||
}
|
}
|
||||||
|
|
||||||
private EntityVersion getEntityVersion(TransportProtos.EntityVersionProto proto) {
|
private EntityVersion getEntityVersion(TransportProtos.EntityVersionProto proto) {
|
||||||
return new EntityVersion(proto.getTs(), proto.getId(), proto.getName());
|
return new EntityVersion(proto.getTs(), proto.getId(), proto.getName(), proto.getAuthor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private VersionedEntityInfo getVersionedEntityInfo(TransportProtos.VersionedEntityInfoProto proto) {
|
private VersionedEntityInfo getVersionedEntityInfo(TransportProtos.VersionedEntityInfoProto proto) {
|
||||||
@ -453,8 +451,23 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PrepareMsg getCommitPrepareMsg(VersionCreateRequest request) {
|
private static PrepareMsg getCommitPrepareMsg(User user, VersionCreateRequest request) {
|
||||||
return PrepareMsg.newBuilder().setCommitMsg(request.getVersionName()).setBranchName(request.getBranch()).build();
|
return PrepareMsg.newBuilder().setCommitMsg(request.getVersionName())
|
||||||
|
.setBranchName(request.getBranch()).setAuthorName(getAuthorName(user)).setAuthorEmail(user.getEmail()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getAuthorName(User user) {
|
||||||
|
List<String> parts = new ArrayList<>();
|
||||||
|
if (StringUtils.isNotBlank(user.getFirstName())) {
|
||||||
|
parts.add(user.getFirstName());
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(user.getLastName())) {
|
||||||
|
parts.add(user.getLastName());
|
||||||
|
}
|
||||||
|
if (parts.isEmpty()) {
|
||||||
|
parts.add(user.getName());
|
||||||
|
}
|
||||||
|
return String.join(" ", parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ToVersionControlServiceMsg.Builder newRequestProto(PendingGitRequest<?> request, RepositorySettings settings) {
|
private ToVersionControlServiceMsg.Builder newRequestProto(PendingGitRequest<?> request, RepositorySettings settings) {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ package org.thingsboard.server.service.sync.vc;
|
|||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.ExportableEntity;
|
import org.thingsboard.server.common.data.ExportableEntity;
|
||||||
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.page.PageData;
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
@ -36,7 +37,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface GitVersionControlQueueService {
|
public interface GitVersionControlQueueService {
|
||||||
|
|
||||||
ListenableFuture<CommitGitRequest> prepareCommit(TenantId tenantId, VersionCreateRequest request);
|
ListenableFuture<CommitGitRequest> prepareCommit(User user, VersionCreateRequest request);
|
||||||
|
|
||||||
ListenableFuture<Void> addToCommit(CommitGitRequest commit, EntityExportData<ExportableEntity<EntityId>> entityData);
|
ListenableFuture<Void> addToCommit(CommitGitRequest commit, EntityExportData<ExportableEntity<EntityId>> entityData);
|
||||||
|
|
||||||
|
|||||||
@ -692,14 +692,17 @@ message CommitResponseMsg {
|
|||||||
int64 ts = 1;
|
int64 ts = 1;
|
||||||
string commitId = 2;
|
string commitId = 2;
|
||||||
string name = 3;
|
string name = 3;
|
||||||
int32 added = 4;
|
string author = 4;
|
||||||
int32 modified = 5;
|
int32 added = 5;
|
||||||
int32 removed = 6;
|
int32 modified = 6;
|
||||||
|
int32 removed = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PrepareMsg {
|
message PrepareMsg {
|
||||||
string commitMsg = 1;
|
string commitMsg = 1;
|
||||||
string branchName = 2;
|
string branchName = 2;
|
||||||
|
string authorName = 3;
|
||||||
|
string authorEmail = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddMsg {
|
message AddMsg {
|
||||||
@ -733,6 +736,7 @@ message EntityVersionProto {
|
|||||||
int64 ts = 1;
|
int64 ts = 1;
|
||||||
string id = 2;
|
string id = 2;
|
||||||
string name = 3;
|
string name = 3;
|
||||||
|
string author = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListVersionsResponseMsg {
|
message ListVersionsResponseMsg {
|
||||||
|
|||||||
@ -26,4 +26,5 @@ public class EntityVersion {
|
|||||||
private long timestamp;
|
private long timestamp;
|
||||||
private String id;
|
private String id;
|
||||||
private String name;
|
private String name;
|
||||||
|
private String author;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,7 +312,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe
|
|||||||
.setTotalElements(data.getTotalElements())
|
.setTotalElements(data.getTotalElements())
|
||||||
.setHasNext(data.hasNext())
|
.setHasNext(data.hasNext())
|
||||||
.addAllVersions(data.getData().stream().map(
|
.addAllVersions(data.getData().stream().map(
|
||||||
v -> EntityVersionProto.newBuilder().setTs(v.getTimestamp()).setId(v.getId()).setName(v.getName()).build()
|
v -> EntityVersionProto.newBuilder().setTs(v.getTimestamp()).setId(v.getId()).setName(v.getName()).setAuthor(v.getAuthor()).build()
|
||||||
).collect(Collectors.toList())))
|
).collect(Collectors.toList())))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -397,7 +397,8 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe
|
|||||||
|
|
||||||
private void prepareCommit(VersionControlRequestCtx ctx, UUID txId, PrepareMsg prepareMsg) {
|
private void prepareCommit(VersionControlRequestCtx ctx, UUID txId, PrepareMsg prepareMsg) {
|
||||||
var tenantId = ctx.getTenantId();
|
var tenantId = ctx.getTenantId();
|
||||||
var pendingCommit = new PendingCommit(tenantId, ctx.getNodeId(), txId, prepareMsg.getBranchName(), prepareMsg.getCommitMsg());
|
var pendingCommit = new PendingCommit(tenantId, ctx.getNodeId(), txId, prepareMsg.getBranchName(),
|
||||||
|
prepareMsg.getCommitMsg(), prepareMsg.getAuthorName(), prepareMsg.getAuthorEmail());
|
||||||
PendingCommit old = pendingCommitMap.get(tenantId);
|
PendingCommit old = pendingCommitMap.get(tenantId);
|
||||||
if (old != null) {
|
if (old != null) {
|
||||||
doAbortCurrentCommit(tenantId, old);
|
doAbortCurrentCommit(tenantId, old);
|
||||||
@ -460,6 +461,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe
|
|||||||
.setTs(result.getVersion().getTimestamp())
|
.setTs(result.getVersion().getTimestamp())
|
||||||
.setCommitId(result.getVersion().getId())
|
.setCommitId(result.getVersion().getId())
|
||||||
.setName(result.getVersion().getName())
|
.setName(result.getVersion().getName())
|
||||||
|
.setAuthor(result.getVersion().getAuthor())
|
||||||
.setAdded(result.getAdded())
|
.setAdded(result.getAdded())
|
||||||
.setModified(result.getModified())
|
.setModified(result.getModified())
|
||||||
.setRemoved(result.getRemoved())));
|
.setRemoved(result.getRemoved())));
|
||||||
|
|||||||
@ -117,7 +117,7 @@ public class DefaultGitRepositoryService implements GitRepositoryService {
|
|||||||
result.setModified(status.getModified().size());
|
result.setModified(status.getModified().size());
|
||||||
result.setRemoved(status.getRemoved().size());
|
result.setRemoved(status.getRemoved().size());
|
||||||
|
|
||||||
GitRepository.Commit gitCommit = repository.commit(commit.getVersionName());
|
GitRepository.Commit gitCommit = repository.commit(commit.getVersionName(), commit.getAuthorName(), commit.getAuthorEmail());
|
||||||
repository.push(commit.getWorkingBranch(), commit.getBranch());
|
repository.push(commit.getWorkingBranch(), commit.getBranch());
|
||||||
|
|
||||||
result.setVersion(toVersion(gitCommit));
|
result.setVersion(toVersion(gitCommit));
|
||||||
@ -255,7 +255,15 @@ public class DefaultGitRepositoryService implements GitRepositoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private EntityVersion toVersion(GitRepository.Commit commit) {
|
private EntityVersion toVersion(GitRepository.Commit commit) {
|
||||||
return new EntityVersion(commit.getTimestamp(), commit.getId(), commit.getMessage());
|
return new EntityVersion(commit.getTimestamp(), commit.getId(), commit.getMessage(), this.getAuthor(commit));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAuthor(GitRepository.Commit commit) {
|
||||||
|
String author = String.format("<%s>", commit.getAuthorEmail());
|
||||||
|
if (StringUtils.isNotBlank(commit.getAuthorName())) {
|
||||||
|
author = String.format("%s %s", commit.getAuthorName(), author);
|
||||||
|
}
|
||||||
|
return author;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EntityId fromRelativePath(String path) {
|
public static EntityId fromRelativePath(String path) {
|
||||||
|
|||||||
@ -255,8 +255,9 @@ public class GitRepository {
|
|||||||
return new Status(status.getAdded(), modified, status.getRemoved());
|
return new Status(status.getAdded(), modified, status.getRemoved());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Commit commit(String message) throws GitAPIException {
|
public Commit commit(String message, String authorName, String authorEmail) throws GitAPIException {
|
||||||
RevCommit revCommit = execute(git.commit()
|
RevCommit revCommit = execute(git.commit()
|
||||||
|
.setAuthor(authorName, authorEmail)
|
||||||
.setMessage(message));
|
.setMessage(message));
|
||||||
return toCommit(revCommit);
|
return toCommit(revCommit);
|
||||||
}
|
}
|
||||||
@ -325,7 +326,8 @@ public class GitRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Commit toCommit(RevCommit revCommit) {
|
private Commit toCommit(RevCommit revCommit) {
|
||||||
return new Commit(revCommit.getCommitTime() * 1000l, revCommit.getName(), revCommit.getFullMessage(), revCommit.getAuthorIdent().getName());
|
return new Commit(revCommit.getCommitTime() * 1000l, revCommit.getName(),
|
||||||
|
revCommit.getFullMessage(), revCommit.getAuthorIdent().getName(), revCommit.getAuthorIdent().getEmailAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
private RevCommit resolveCommit(String id) throws IOException {
|
private RevCommit resolveCommit(String id) throws IOException {
|
||||||
@ -470,6 +472,7 @@ public class GitRepository {
|
|||||||
private final String id;
|
private final String id;
|
||||||
private final String message;
|
private final String message;
|
||||||
private final String authorName;
|
private final String authorName;
|
||||||
|
private final String authorEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
|||||||
@ -30,12 +30,18 @@ public class PendingCommit {
|
|||||||
private String branch;
|
private String branch;
|
||||||
private String versionName;
|
private String versionName;
|
||||||
|
|
||||||
public PendingCommit(TenantId tenantId, String nodeId, UUID txId, String branch, String versionName) {
|
private String authorName;
|
||||||
|
|
||||||
|
private String authorEmail;
|
||||||
|
|
||||||
|
public PendingCommit(TenantId tenantId, String nodeId, UUID txId, String branch, String versionName, String authorName, String authorEmail) {
|
||||||
this.tenantId = tenantId;
|
this.tenantId = tenantId;
|
||||||
this.nodeId = nodeId;
|
this.nodeId = nodeId;
|
||||||
this.txId = txId;
|
this.txId = txId;
|
||||||
this.branch = branch;
|
this.branch = branch;
|
||||||
this.versionName = versionName;
|
this.versionName = versionName;
|
||||||
|
this.authorName = authorName;
|
||||||
|
this.authorEmail = authorEmail;
|
||||||
this.workingBranch = txId.toString();
|
this.workingBranch = txId.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,11 +101,17 @@
|
|||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container matColumnDef="name">
|
<ng-container matColumnDef="name">
|
||||||
<mat-header-cell *matHeaderCellDef style="width: 100%"> {{ 'version-control.version-name' | translate }} </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef style="width: 50%"> {{ 'version-control.version-name' | translate }} </mat-header-cell>
|
||||||
<mat-cell *matCellDef="let entityVersion">
|
<mat-cell *matCellDef="let entityVersion">
|
||||||
{{ entityVersion.name }}
|
{{ entityVersion.name }}
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="author">
|
||||||
|
<mat-header-cell *matHeaderCellDef style="width: 50%"> {{ 'version-control.author' | translate }} </mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let entityVersion">
|
||||||
|
{{ entityVersion.author }}
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
<ng-container matColumnDef="actions" stickyEnd>
|
<ng-container matColumnDef="actions" stickyEnd>
|
||||||
<mat-header-cell *matHeaderCellDef [ngStyle]="singleEntityMode ? {minWidth: '80px', maxWidth: '80px', width: '80px'} :
|
<mat-header-cell *matHeaderCellDef [ngStyle]="singleEntityMode ? {minWidth: '80px', maxWidth: '80px', width: '80px'} :
|
||||||
{minWidth: '40px', maxWidth: '40px', width: '40px'}">
|
{minWidth: '40px', maxWidth: '40px', width: '40px'}">
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
|
|||||||
@Input()
|
@Input()
|
||||||
singleEntityMode = false;
|
singleEntityMode = false;
|
||||||
|
|
||||||
displayedColumns = ['timestamp', 'id', 'name', 'actions'];
|
displayedColumns = ['timestamp', 'id', 'name', 'author', 'actions'];
|
||||||
pageLink: PageLink;
|
pageLink: PageLink;
|
||||||
textSearchMode = false;
|
textSearchMode = false;
|
||||||
dataSource: EntityVersionsDatasource;
|
dataSource: EntityVersionsDatasource;
|
||||||
|
|||||||
@ -138,6 +138,7 @@ export interface EntityVersion {
|
|||||||
timestamp: number;
|
timestamp: number;
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
author: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VersionCreationResult {
|
export interface VersionCreationResult {
|
||||||
|
|||||||
@ -3119,6 +3119,7 @@
|
|||||||
"create-entity-version": "Create entity version",
|
"create-entity-version": "Create entity version",
|
||||||
"version-name": "Version name",
|
"version-name": "Version name",
|
||||||
"version-name-required": "Version name is required",
|
"version-name-required": "Version name is required",
|
||||||
|
"author": "Author",
|
||||||
"export-entity-relations": "Export entity relations",
|
"export-entity-relations": "Export entity relations",
|
||||||
"entity-versions": "Entity versions",
|
"entity-versions": "Entity versions",
|
||||||
"versions": "Versions",
|
"versions": "Versions",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user