diff --git a/src/main/java/org/openepics/names/old/model/Device.java b/src/main/java/org/openepics/names/old/model/Device.java new file mode 100644 index 0000000000000000000000000000000000000000..aa2565afb16d7694d9c24c7802d23d01958ee73f --- /dev/null +++ b/src/main/java/org/openepics/names/old/model/Device.java @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 2014 European Spallation Source ERIC. + * Copyright (c) 2014 Cosylab d.d. + * + * This file is part of Naming Service. + * Naming Service is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 2 of the License, or any newer version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt + */ + +package org.openepics.names.old.model; + +import com.google.common.base.Preconditions; + +import javax.persistence.Entity; + +import org.openepics.names.repository.model.Persistable; + +import java.util.Objects; +import java.util.UUID; + +/** + * An entity representing a device. + * + * @author Marko Kolar + */ +@Entity +public class Device extends Persistable { + + private static final long serialVersionUID = 8489649071981076533L; + private String uuid; + + protected Device() {} + + /** + * Constructs a new device entity based on a UUID identifier. + * + * @param uuid the universally unique identifier + */ + public Device(UUID uuid) { + Preconditions.checkNotNull(uuid); + this.uuid = uuid.toString(); + } + + /** + * @return The universally unique identifier. + */ + public UUID getUuid() { return UUID.fromString(uuid); } + + @Override public boolean equals(Object other) { + return other instanceof Device && ((Device) other).getUuid().equals(getUuid()); + } + + @Override + public int hashCode() { + return Objects.hash(getUuid()); + } +} diff --git a/src/main/java/org/openepics/names/old/model/DeviceRevision.java b/src/main/java/org/openepics/names/old/model/DeviceRevision.java new file mode 100644 index 0000000000000000000000000000000000000000..c7efa5a8ab96d2c1755905aebe3abbb3d250ef7f --- /dev/null +++ b/src/main/java/org/openepics/names/old/model/DeviceRevision.java @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 2014 European Spallation Source ERIC. + * Copyright (c) 2014 Cosylab d.d. + * + * This file is part of Naming Service. + * Naming Service is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 2 of the License, or any newer version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt + */ + +package org.openepics.names.old.model; + +import com.google.common.base.Preconditions; + +import javax.annotation.Nullable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.openepics.names.repository.model.Persistable; + +import java.util.Date; + +/** + * A revision of a Device entity representing its state at some point in time. + * + * @author Marko Kolar + */ +@Entity +@Table(name = "devicerevision") +public class DeviceRevision extends Persistable { + + private static final long serialVersionUID = -7697708696131775826L; + + @JoinColumn(name = "device_id") + private @ManyToOne Device device; + + @JoinColumn(name = "requestedby_id") + private @ManyToOne UserAccount requestedBy; + + @Column(name = "requestdate") + private Date requestDate; + + private boolean deleted; + + @JoinColumn(name = "section_id") + private @ManyToOne NamePart section; + + @JoinColumn(name = "devicetype_id") + private @ManyToOne NamePart deviceType; + + @Column(name = "instanceindex") + private @Nullable String instanceIndex; + + @Column(name = "conventionname") + private String conventionName; + + @Column(name = "conventionnameeqclass") + private String conventionNameEqClass; + + @Column(name = "additionalinfo") + private @Nullable String additionalInfo; + + @Column(name = "processorcomment") + private @Nullable String processorComment; + + protected DeviceRevision() {} + + /** + * Constructs a new device revision entity (data object with relations). + * + * @param device the device the revision pertains to + * @param requestDate the time when the revision was proposed + * @param requestedBy the user that proposed the revision. Null if the revision was generated by an automated + * process. + * @param deleted a flag signifying that the revision represents deletion of the device + * @param section the section containing the device + * @param deviceType the type of the device + * @param instanceIndex an additional identifier that, in combination with other attributes, determine the unique + * convention name of the device. Null if omitted. + * @param conventionName the full name of the device in accordance with the naming convention + * @param conventionNameEqClass the representative of the equivalence class the convention name belongs to. This is + * used to ensure uniqueness of convention names when treating similar looking names (for example, containing 0 vs. + * O, 1 vs. l) as equal. + * @param additionalInfo Additional information (description, comment etc) about the device. + */ + public DeviceRevision( + Device device, Date requestDate, @Nullable UserAccount requestedBy, boolean deleted, + NamePart section, NamePart deviceType, @Nullable String instanceIndex, String conventionName, + String conventionNameEqClass, @Nullable String additionalInfo) { + + this(device, requestDate, requestedBy, deleted, section, deviceType, instanceIndex, conventionName, + conventionNameEqClass, additionalInfo, null); + } + + /** + * Constructs a new device revision entity (data object with relations). + * + * @param device the device the revision pertains to + * @param requestDate the time when the revision was proposed + * @param requestedBy the user that proposed the revision. Null if the revision was generated by an automated + * process. + * @param deleted a flag signifying that the revision represents deletion of the device + * @param section the section containing the device + * @param deviceType the type of the device + * @param instanceIndex an additional identifier that, in combination with other attributes, determine the unique + * convention name of the device. Null if omitted. + * @param conventionName the full name of the device in accordance with the naming convention + * @param conventionNameEqClass the representative of the equivalence class the convention name belongs to. This is + * used to ensure uniqueness of convention names when treating similar looking names (for example, containing 0 vs. + * O, 1 vs. l) as equal. + * @param additionalInfo additional information (description, comment etc) about the device + * @param commitMessage commit message + */ + public DeviceRevision( + Device device, Date requestDate, @Nullable UserAccount requestedBy, boolean deleted, + NamePart section, NamePart deviceType, @Nullable String instanceIndex, String conventionName, + String conventionNameEqClass, @Nullable String additionalInfo, @Nullable String commitMessage) { + + Preconditions.checkNotNull(device); + Preconditions.checkNotNull(requestDate); + Preconditions.checkNotNull(section); + // not check deviceType as device may have section but no deviceType + Preconditions.checkArgument(instanceIndex == null || !instanceIndex.isEmpty()); + Preconditions.checkArgument(conventionName != null && !conventionName.isEmpty()); + Preconditions.checkArgument(conventionNameEqClass != null); + Preconditions.checkArgument(additionalInfo == null || !additionalInfo.isEmpty()); + this.device = device; + this.requestedBy = requestedBy; + this.requestDate = new Date(requestDate.getTime()); + this.deleted = deleted; + this.section = section; + this.deviceType = deviceType; + this.instanceIndex = instanceIndex; + this.conventionName = conventionName; + this.conventionNameEqClass = conventionNameEqClass; + this.additionalInfo=additionalInfo; + this.processorComment=commitMessage; + } + + /** + * @return The device the revision pertains to. + */ + public Device getDevice() { return device; } + + /** + * @return The time when the revision was proposed. + */ + public Date getRequestDate() { + return requestDate != null ? new Date(requestDate.getTime()) : null; + } + + /** + * @return The user that proposed the revision. Null if the revision was generated by an automated process. + */ + public @Nullable UserAccount getRequestedBy() { return requestedBy; } + + /** + * @return A flag signifying that the revision represents deletion of the device. + */ + public boolean isDeleted() { return deleted; } + + /** + * @return The section containing the device. + */ + public NamePart getSection() { return section; } + + /** + * @return The type of the device. + */ + public NamePart getDeviceType() { return deviceType; } + + /** + * @return An additional identifier that, in combination with other attributes, + * determine the unique convention name of the device. Null if omitted. + */ + public @Nullable String getInstanceIndex() { return instanceIndex; } + + /** + * @return The full name of the device in accordance with the naming convention. + */ + public String getConventionName() { return conventionName; } + + /** + * @return The representative of the equivalence class the convention name belongs to. + * This is used to ensure uniqueness of convention names when treating similar + * looking names (for example, containing 0 vs. O, 1 vs. l) as equal. + */ + public String getConventionNameEqClass() { return conventionNameEqClass; } + + /** + * Sets the convention equivalent class + * @param conventionNameEqClass the convention equivalent class to be set. + */ + public void setConventionNameEqClass(String conventionNameEqClass){ + this.conventionNameEqClass= conventionNameEqClass; + } + + /** + * @return Additional information (description, comment etc) about the device. + */ + public @Nullable String getAdditionalInfo(){ return additionalInfo;} + + @Nullable + public String getProcessorComment() { + return processorComment; + } + + public void setProcessorComment(@Nullable String processorComment) { + this.processorComment = processorComment; + } + + public boolean supersede(DeviceRevision other) { + // TODO: fix this...(when new revision are be created instead of updating old ones) + // return other == null + // || getId() > other.getId() + // || getId() == other.getId() && other.getStatus().isPending() && !getStatus().isPending(); + return other == null + || getId() > other.getId(); + } + +} diff --git a/src/main/java/org/openepics/names/old/model/NamePart.java b/src/main/java/org/openepics/names/old/model/NamePart.java new file mode 100644 index 0000000000000000000000000000000000000000..b80c650b451a86ba393e8f65ffff26033111511f --- /dev/null +++ b/src/main/java/org/openepics/names/old/model/NamePart.java @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 2014 European Spallation Source ERIC. + * Copyright (c) 2014 Cosylab d.d. + * + * This file is part of Naming Service. + * Naming Service is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 2 of the License, or any newer version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt + */ + +package org.openepics.names.old.model; + +import com.google.common.base.Preconditions; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; + +import org.openepics.names.repository.model.Persistable; + +import java.util.Objects; +import java.util.UUID; + +/** + * An entity representing either a named section of the System Structure or a named device type of the Device Structure, + * depending on the specified namePartType. + * + * @author Marko Kolar + */ +@Entity +@Table(name = "namepart") +public class NamePart extends Persistable { + + /** + * + */ + private static final long serialVersionUID = 1674808840579429369L; + + private String uuid; + + @Column(name = "nameparttype") + @Enumerated(EnumType.STRING) + private NamePartType namePartType; + + protected NamePart() {} + + /** + * Constructs a new name part entity based on a UUID identifier and name part type. + * + * @param uuid the universally unique identifier + * @param namePartType the type of the NamePart + */ + public NamePart(UUID uuid, NamePartType namePartType) { + Preconditions.checkNotNull(uuid); + Preconditions.checkNotNull(namePartType); + this.uuid = uuid.toString(); + this.namePartType = namePartType; + } + + /** + * @return The universally unique identifier. + */ + public UUID getUuid() { return UUID.fromString(uuid); } + + /** + * @return The type of the NamePart. + */ + public NamePartType getNamePartType() { return namePartType; } + + @Override public boolean equals(Object other) { + return other instanceof NamePart && ((NamePart) other).getUuid().equals(getUuid()); + } + + @Override + public int hashCode() { + return Objects.hash(getUuid()); + } +} diff --git a/src/main/java/org/openepics/names/old/model/NamePartRevision.java b/src/main/java/org/openepics/names/old/model/NamePartRevision.java new file mode 100644 index 0000000000000000000000000000000000000000..eea142c9ef11e19e859d99c1108556ce8876c800 --- /dev/null +++ b/src/main/java/org/openepics/names/old/model/NamePartRevision.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2014 European Spallation Source ERIC. + * Copyright (c) 2014 Cosylab d.d. + * + * This software is Copyright by the Board of Trustees of Michigan + * State University (c) Copyright 2012. + * + * You may use this software under the terms of the GNU public license + * (GPL). The terms of this license are described at: + * http://www.gnu.org/licenses/gpl.txt + * + * Contact Information: + * Facilitty for Rare Isotope Beam + * Michigan State University + * East Lansing, MI 48824-1321 + * http://frib.msu.edu + */ + +package org.openepics.names.old.model; + +import com.google.common.base.Preconditions; + +import javax.annotation.Nullable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.openepics.names.repository.model.Persistable; + +import java.util.Date; + +/** + * A revision of a NamePart entity representing its state at some point in time. + * + * @author Vasu V + * @author Marko Kolar + * @author Karin Rathsman + */ +@Entity +@Table(name = "namepartrevision") +public class NamePartRevision extends Persistable { + + /** + * + */ + private static final long serialVersionUID = -8783968098755208875L; + + @JoinColumn(name = "namepart_id") + private @ManyToOne NamePart namePart; + + @Column(name = "requestdate") + private Date requestDate; + + @JoinColumn(name = "requestedby_id") + private @ManyToOne @Nullable UserAccount requestedBy; + + @Column(name = "requestercomment") + private @Nullable String requesterComment; + + private boolean deleted; + + @JoinColumn(name = "parent_id") + private @ManyToOne @Nullable NamePart parent; + + private String name; + + private @Nullable String mnemonic; + + private @Nullable String description=null; + + @Column(name = "mnemoniceqclass") + private @Nullable String mnemonicEqClass; + + @Enumerated(EnumType.STRING) + private NamePartRevisionStatus status; + + @JoinColumn(name = "processedby_id") + private @ManyToOne @Nullable UserAccount processedBy = null; + + @Column(name = "processdate") + private @Nullable Date processDate = null; + + @Column(name = "processorcomment") + private @Nullable String processorComment = null; + + protected NamePartRevision() {} + + /** + * Constructs a new name part revision entity (data object with relations). + * + * @param namePart the name part the revision pertains to + * @param requestDate the time when the revision was proposed + * @param requestedBy the user that proposed the revision. + * Null if the revision was generated by an automated process. + * @param requesterComment the comment the user gave when proposing the revision. Null if no comment was given. + * @param deleted a flag signifying that the revision represents deletion of the name part + * @param parent the parent of this name part in the hierarchy. Null if at the top of the hierarchy. + * @param name is the full name of the part. Does not need to follow a convention. + * @param mnemonic is the short, mnemonic name of the part in accordance with the naming convention + * @param description is the (lengthy) description or comment of the part. + * @param mnemonicEqClass the representative of the equivalence class the mnemonic belongs to. + * This is used to ensure uniqueness of mnemonics on certain levels when treating + * similar looking names (for example, containing 0 vs. O, 1 vs. l) as equal. + */ + public NamePartRevision( + NamePart namePart, Date requestDate, @Nullable UserAccount requestedBy, @Nullable String requesterComment, + boolean deleted, @Nullable NamePart parent, String name, String mnemonic, + @Nullable String description, String mnemonicEqClass) { + + Preconditions.checkNotNull(namePart); + Preconditions.checkNotNull(requestDate); + Preconditions.checkArgument(requesterComment == null || !requesterComment.isEmpty()); + Preconditions.checkArgument(name != null && !name.isEmpty()); + this.namePart = namePart; + this.requestDate = new Date(requestDate.getTime()); + this.requestedBy = requestedBy; + this.requesterComment = requesterComment; + this.deleted = deleted; + this.parent = parent; + this.name = name; + this.mnemonic = mnemonic; + this.description = description; + this.mnemonicEqClass = mnemonicEqClass; + this.status = NamePartRevisionStatus.PENDING; + } + + /** + * @return The name part the revision pertains to. + */ + public NamePart getNamePart() { return namePart; } + + /** + * Sets the status + * @param status the status to be set + */ + public void setStatus(NamePartRevisionStatus status){ + this.status = status; + } + + /** + * @return The time when the revision was proposed. + */ + public Date getRequestDate() { + return requestDate != null ? new Date(requestDate.getTime()) : null; + } + + /** + * @return The user that proposed the revision. Null if the revision was generated by an automated process. + */ + public @Nullable UserAccount getRequestedBy() { return requestedBy; } + + /** + * @return The comment the user gave when proposing the revision. Null if no comment was given. + */ + public @Nullable String getRequesterComment() { return requesterComment; } + + /** + * @return A flag signifying that the revision represents deletion of the name part. + */ + public boolean isDeleted() { return deleted; } + + /** + * @return The parent of this name part in the hierarchy. Null if at the top of the hierarchy. + */ + public @Nullable NamePart getParent() { return parent; } + + /** + * @return Full name of the part. Does not need to follow a convention. + */ + public String getName() { return name; } + + /** + * @return The short, mnemonic name of the part in accordance with the naming convention. + */ + public @Nullable String getMnemonic() { return mnemonic; } + + /** + * @return The description or other relevant information of the namepart. Optional. + */ + public @Nullable String getDescription(){ return description; } + + /** + * @return The representative of the equivalence class the mnemonic belongs to. + * This is used to ensure uniqueness of mnemonics on certain level when treating + * similar looking names (for example, containing 0 vs. O, 1 vs. l) as equal. + */ + public @Nullable String getMnemonicEqClass() { return mnemonicEqClass; } + + //TODO Remove after first deploy!!! + public void setMnemonicEqClass(String mnemonicEqClass) { this.mnemonicEqClass = mnemonicEqClass; } + + /** + * @return The status of the name part in the request / approve workflow. + */ + public NamePartRevisionStatus getStatus() { return status; } + + /** + * @return The time the revision was processed in the request / approve workflow. Null if still pending. + */ + public @Nullable Date getProcessDate() { + return processDate != null ? new Date(processDate.getTime()) : null; + } + + /** + * Sets the process date + * @param date the date to be set + */ + public void setProcessDate(Date date) { + this.processDate = date != null ? new Date(date.getTime()) : null; + } + + /** + * @return The user who processed the revision in the request / approve workflow. + * Null if still pending or if the revision @return was processed by an automated process. + */ + public @Nullable UserAccount getProcessedBy() { return processedBy; } + + /** + * Sets the process user + * @param user the process user + */ + public void setProcessedBy(UserAccount user){ + this.processedBy = user; + } + + /** + * @return The comment the user gave when processing the revision in the request / approve workflow. + * Null if still pending @return or if no comment was given. + */ + public @Nullable String getProcessorComment() { return processorComment; } + + /** + * Sets the processorComment + * @param message the message + */ + public void setProcessorComment(String message){ + this.processorComment = message; + } + + /** + * Updates the revision's status in the request / approve workflow as either approved, rejected or canceled. + * This completes the workflow and no further status changes are possible after that. + * + * @param status the new status of the name part + * @param date the time the revision was processed + * @param by the user who processed the revision. Null if the revision was processed by an automated process. + * @param comment the comment the administrator user gave when processing the revision. + * Null if no comment was given. + * + * @deprecated since version 3.3.2 + */ + @Deprecated + public void updateAsProcessed( + NamePartRevisionStatus status, Date date, @Nullable UserAccount by, @Nullable String comment) { + + Preconditions.checkState(this.status == NamePartRevisionStatus.PENDING); + Preconditions.checkArgument(status != NamePartRevisionStatus.PENDING); + Preconditions.checkNotNull(date); + Preconditions.checkArgument(comment == null || !comment.isEmpty()); + this.status = status; + this.processDate = new Date(date.getTime()); + this.processedBy = by; + this.processorComment = comment; + } + + /** + * rollback processed status to pending. + * + * @deprecated since version 3.3.2 + */ + @Deprecated + public void rollBackProcessed() { + Preconditions.checkState(!this.status.equals(NamePartRevisionStatus.PENDING),"Status cannot be Pending"); + this.status=NamePartRevisionStatus.PENDING; + this.processDate = null; + this.processedBy = null; + this.processorComment = null; + } + +} diff --git a/src/main/java/org/openepics/names/old/model/NamePartType.java b/src/main/java/org/openepics/names/old/model/NamePartType.java new file mode 100644 index 0000000000000000000000000000000000000000..78a585f08be446abcb8b35732a90770c26d6817a --- /dev/null +++ b/src/main/java/org/openepics/names/old/model/NamePartType.java @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2014 European Spallation Source ERIC. + * Copyright (c) 2014 Cosylab d.d. + * + * This file is part of Naming Service. + * Naming Service is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 2 of the License, or any newer version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt + */ + +package org.openepics.names.old.model; + +/** + * Type of a NamePart specifying whether it belongs to the System Structure or the Device Structure. + * + * @author Marko Kolar + */ +public enum NamePartType { + /** A (sub)section of the Logical System Structure. */ + SECTION, + + /** A device (sub)type of the Device Category Structure. */ + DEVICE_TYPE +} diff --git a/src/main/java/org/openepics/names/old/model/Role.java b/src/main/java/org/openepics/names/old/model/Role.java new file mode 100644 index 0000000000000000000000000000000000000000..36c1281f34b7ee6c45210afe7a2a55a7ae423aaa --- /dev/null +++ b/src/main/java/org/openepics/names/old/model/Role.java @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2014 European Spallation Source ERIC. + * Copyright (c) 2014 Cosylab d.d. + * + * This file is part of Naming Service. + * Naming Service is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 2 of the License, or any newer version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt + */ + +package org.openepics.names.old.model; + +/** + * UserAccount's role that determines the user's access control permissions. Used by the SessionServiceTest (not RBAC) + * + * @author Marko Kolar + */ +public enum Role { + /** + * Editor role giving permissions to propose changes to the Logical System and Device Category structures + * and to add, modify or delete Devices. + */ + EDITOR, + + /** + * Super user role giving permission to approve or reject proposed changes to System and Device structures, + * in addition to normal Editor permissions. + */ + SUPERUSER +} diff --git a/src/main/java/org/openepics/names/old/model/UserAccount.java b/src/main/java/org/openepics/names/old/model/UserAccount.java new file mode 100644 index 0000000000000000000000000000000000000000..739f460b760396cabbfdf44ebe160923b45210f5 --- /dev/null +++ b/src/main/java/org/openepics/names/old/model/UserAccount.java @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2014 European Spallation Source ERIC. + * Copyright (c) 2014 Cosylab d.d. + * + * This file is part of Naming Service. + * Naming Service is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 2 of the License, or any newer version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see https://www.gnu.org/licenses/gpl-2.0.txt + */ + +package org.openepics.names.old.model; + +import com.google.common.base.Preconditions; + +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; + +import org.openepics.names.repository.model.Persistable; + +import java.util.Objects; + +/** + * An entity representing a user account used to sing in to the application. + * + * @author Vasu V + * @author Marko Kolar + */ +@Entity +@Table(name = "useraccount") +public class UserAccount extends Persistable { + + /** + * + */ + private static final long serialVersionUID = -6026728360254460697L; + + private String username; + + @Enumerated(EnumType.STRING) private Role role; + + protected UserAccount() {} + + /** + * Constructs a new user account object given username and role. + * + * @param username the name identifying the user + * @param role the role that determines the user's access control permissions + */ + public UserAccount(String username, Role role) { + Preconditions.checkArgument(username != null && !username.isEmpty()); + Preconditions.checkNotNull(role); + this.username = username; + this.role = role; + } + + /** + * @return The name identifying the user + */ + public String getUsername() { return username; } + + /** + * @return The role that determines the user's access control permissions + */ + public Role getRole() { return role; } + + @Override public int hashCode() { + return Objects.hashCode(username); + } + + @Override public boolean equals(Object other) { + return other instanceof UserAccount && ((UserAccount) other).getUsername().equals(getUsername()); + } + + @Override public String toString(){ + return getUsername(); + } +} diff --git a/src/main/java/org/openepics/names/old/nameviews/NameViewProvider.java b/src/main/java/org/openepics/names/old/nameviews/NameViewProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..da8a0b170a51e20ee8178c9497d9a56a5d40af49 --- /dev/null +++ b/src/main/java/org/openepics/names/old/nameviews/NameViewProvider.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2016 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.openepics.names.old.nameviews; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.openepics.names.old.model.DeviceRevision; + +/** + * Implementation of part of NameViewProvider. + * Been that generates the nameView tree and updates name views. + * + * @author karinrathsman + * @author Lars Johansson + * + */ +public class NameViewProvider implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 6916728601880286405L; + + private NameRevisions nameRevisions; + + public NameViewProvider() { + nameRevisions = new NameRevisions(); + } + + public void update(List<DeviceRevision> newRevisions) { + for (DeviceRevision revision : newRevisions) { + nameRevisions.update(revision); + } + } + + public String equivalenceClassRepresentative(String name) { + return name != null + ? name.toUpperCase() + .replaceAll("(?<=[A-Za-z])0+", "") + .replace('I', '1').replace('L', '1').replace('O', '0') + .replaceAll("(?<!\\d)0+(?=\\d)", "") + : null; + } + + /** + * @return the restfulNameRevisionMap that maps a given device name with the + * latest revision with that name. + */ + public NameRevisions getNameRevisions() { + return nameRevisions; + } + + /** + * This class handles map of key-value with equivalence class representative of convention name as key + * and name revision as value. It is used to build structure of name data. + * + * <p> + * Equivalence class representative of convention name is used as key as it ensure uniqueness of names + * when treating similar looking names. Name revision represents database entry. + */ + public class NameRevisions { + + private Map<String, DeviceRevision> deviceRevisionMap; + + /** + * Constructs new map for equivalence class representative of convention name and name revision objects. + */ + public NameRevisions() { + deviceRevisionMap = new HashMap<>(); + } + + private String key(String name) { + return equivalenceClassRepresentative(name); + } + + /** + * Retrieves content from nameRevisionMap for given parameter as <tt>uuid</tt> or <tt>name</tt>. + * Among usage is REST API and its methods. + * + * @param string uuid or name + * @return name revision + */ + public DeviceRevision get(String string) { + // note + // equivalenceClassRepresentative of name as key in deviceRevisionMap + // + // method not to call key method with input string directly since that would mean + // calling equivalenceClassRepresentative twice, which would potentially strip + // information from input string, e.g. CRY0 would become CRY + // + // method works with + // - name + // - equivalenceClassRepresentative of name + // - uuid + // + // 1. consider input equivalenceClassRepresentative + // 2. consider input name (make equivalenceClassRepresentative) + // 3. consider input uuid + + String key = string; + if (deviceRevisionMap.containsKey(key)) { + return deviceRevisionMap.get(key); + } else if (deviceRevisionMap.containsKey(key(key))) { + return deviceRevisionMap.get(key(key)); + } else { + return null; + } + } + + /** + * Updates the deviceRevisionMap if the specified revision supersedes the + * existing revision with the same name + * + * @param revision + */ + protected void update(DeviceRevision revision) { + String key = key(revision.getConventionName()); + DeviceRevision otherRevision = deviceRevisionMap.get(key); + if (revision.supersede(otherRevision)) { + deviceRevisionMap.put(key, revision); + } + } + + /** + * Return key set for device revision map. + * + * @return key set for device revision map + * + * @see NameRevisions + */ + public Set<String> keySet() { + return deviceRevisionMap.keySet(); + } + + /** + * Return entry set for device revision map. + * + * @return entry set for device revision map + * + * @see NameRevisions + */ + public Set<Map.Entry<String, DeviceRevision>> entrySet() { + return deviceRevisionMap.entrySet(); + } + + /** + * Return if device revision map contains given key. + * + * @param name key for device revision map + * @return if device revision map contains given key + * + * @see NameRevisions + */ + public boolean contains(String name) { + return deviceRevisionMap.containsKey(key(name)); + } + + } + +} diff --git a/src/main/java/org/openepics/names/repository/old/IDeviceRevisionRepository.java b/src/main/java/org/openepics/names/repository/old/IDeviceRevisionRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..c31c35e78b42b83ffa11a7e3bc3be41450f05a67 --- /dev/null +++ b/src/main/java/org/openepics/names/repository/old/IDeviceRevisionRepository.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.openepics.names.repository.old; + +import org.openepics.names.old.model.DeviceRevision; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Find device revision information from JPA. + * + * @author Lars Johansson + */ +@Repository +public interface IDeviceRevisionRepository extends JpaRepository<DeviceRevision, Long> { +} diff --git a/src/main/java/org/openepics/names/repository/old/INamePartRevisionRepository.java b/src/main/java/org/openepics/names/repository/old/INamePartRevisionRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..c26b7d1798bca9549e8daff42acbe4d3718f5b1f --- /dev/null +++ b/src/main/java/org/openepics/names/repository/old/INamePartRevisionRepository.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.openepics.names.repository.old; + +import org.openepics.names.old.model.NamePartRevision; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Find name part revision information from JPA. + * + * @author Lars Johansson + */ +@Repository +public interface INamePartRevisionRepository extends JpaRepository<NamePartRevision, Long> { +} diff --git a/src/main/java/org/openepics/names/rest/controller/VerificationController.java b/src/main/java/org/openepics/names/rest/controller/VerificationController.java new file mode 100644 index 0000000000000000000000000000000000000000..1e0f6913564ca446fcea4930888736b5c5cd8aaf --- /dev/null +++ b/src/main/java/org/openepics/names/rest/controller/VerificationController.java @@ -0,0 +1,1045 @@ +/* + * Copyright (C) 2021 European Spallation Source ERIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.openepics.names.rest.controller; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang3.StringUtils; +import org.openepics.names.old.model.DeviceRevision; +import org.openepics.names.old.model.NamePartRevision; +import org.openepics.names.old.model.NamePartRevisionStatus; +import org.openepics.names.old.nameviews.NameViewProvider; +import org.openepics.names.repository.IDeviceGroupRepository; +import org.openepics.names.repository.IDeviceTypeRepository; +import org.openepics.names.repository.IDisciplineRepository; +import org.openepics.names.repository.INameRepository; +import org.openepics.names.repository.ISubsystemRepository; +import org.openepics.names.repository.ISystemGroupRepository; +import org.openepics.names.repository.ISystemRepository; +import org.openepics.names.repository.model.DeviceGroup; +import org.openepics.names.repository.model.DeviceType; +import org.openepics.names.repository.model.Discipline; +import org.openepics.names.repository.model.Name; +import org.openepics.names.repository.model.Subsystem; +import org.openepics.names.repository.model.System; +import org.openepics.names.repository.model.SystemGroup; +import org.openepics.names.repository.old.IDeviceRevisionRepository; +import org.openepics.names.repository.old.INamePartRevisionRepository; +import org.openepics.names.util.HolderIRepositories; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * This part of REST API provides verification of data migration for Naming application. + * + * <p>Prerequisite(s) + * <ul> + * <li>both old and migrated database available</li> + * </ul> + * + * <p> + * Ideally, some knowledge of database tables and object structures acquired to dig into this class. + * It can be done in any case and there is documentation available. + * It is recommended to have database tables and object structures available. + * + * @author Lars Johansson + */ +@RestController +@RequestMapping("/verification") +@EnableAutoConfiguration +public class VerificationController { + + // note + // global exception handler available + + /* + Methods + read GET /verification/migration_devicerevision - readMigrationDeviceRevision() + read GET /verification/migration_namepartrevision - readMigrationNamePartRevision + read GET /verification/data_reachable - readDataReachable() + read GET /verification/restapi_oldvsnew - readRestApiOldVsNew() + */ + + private static final Logger LOGGER = Logger.getLogger(VerificationController.class.getName()); + + private static final String NEW_LINE_BR = "<br/>"; + private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + private HolderIRepositories holderIRepositories; + + @Autowired + IDeviceRevisionRepository deviceRevisionRepository; + @Autowired + INamePartRevisionRepository namePartRevisionRepository; + + @Autowired + public VerificationController( + INameRepository nameRepository, + ISystemGroupRepository systemGroupRepository, + ISystemRepository systemRepository, + ISubsystemRepository subsystemRepository, + IDisciplineRepository disciplineRepository, + IDeviceGroupRepository deviceGroupRepository, + IDeviceTypeRepository deviceTypeRepository) { + + holderIRepositories = new HolderIRepositories( + nameRepository, + systemGroupRepository, + systemRepository, + subsystemRepository, + disciplineRepository, + deviceGroupRepository, + deviceTypeRepository); + } + + /** + * Perform verification of data migration with focus on device revision. + * Ok if all entries ok and no entry nok. + * + * @return report of data migration + */ + @GetMapping ("/migration_devicerevision") + public String readMigrationDeviceRevision() { + // verification of + // name vs. devicerevision, device + // with help of namepartrevision, namepart + + // note + // check entry by entry + // ---------- + // to check 1st, 2nd, 3rd parent to determine if systemgroup, system or subsystem + // otherwise not clear how to make sure if it is supposed to be systemgroup and not system, subsystem and vice versa + // ---------- + // date may be in different format for different objects, to be formatted before being compared + // ---------- + // name.id = devicerevision.id + // name.version = devicerevision.version + // name.uuid = devicerevision.device_id (device.id --> device.uuid) + // name.namepartrevision_systemgroup_uuid = devicerevision.section_id (namepart.id --> namepart.uuid + // name.namepartrevision_system_uuid = devicerevision.section_id (namepart.id --> namepart.uuid + // name.namepartrevision_subsystem_uuid = devicerevision.section_id (namepart.id --> namepart.uuid + // name.namepartrevision_devicetype_uuid = devicerevision.devicetype_id (namepart.id --> namepart.uuid + // name.instance_index = devicerevision.instanceindex + // name.convention_name = devicerevision.conventionname + // name.convention_name_equivalence = devicerevision.conventionnameeqclass + // name.description = devicerevision.additionalinfo + // name.status = null + // name.latest = true if id = get max id for uuid (not consider status) + // name.deleted = devicerevision.deleted + // name.requested = devicerevision.requestdate + // name.requested_by = devicerevision.requestedby_id (useraccount.id --> useraccount.username) + // name.requested_comment = null + // name.processed = null + // name.processed_by = null + // name.processed_comment = devicerevision.processorcomment + + StringBuilder reportHtml = new StringBuilder(); + + // find data + // for verification + // names + // to support + // device names + // name part revisions + + List<Name> names = holderIRepositories.getNameRepository().findAll(); + List<DeviceRevision> deviceNames = deviceRevisionRepository.findAll(); + List<NamePartRevision> namePartRevisions = namePartRevisionRepository.findAll(); + + prepareLogReport("readMigrationDeviceRevision, find data, names.size: " + names.size(), reportHtml); + prepareLogReport("readMigrationDeviceRevision, find data, deviceNames.size: " + deviceNames.size(), reportHtml); + prepareLogReport("readMigrationDeviceRevision, find data, namePartRevisions.size: " + namePartRevisions.size(), reportHtml); + + // utility + // interpret lists into hashmaps for faster retrieval in for loop below + // used to help check name entries + // ---------- + // mapIdDeviceRevision - find corresponding (old) device revision for given id + // mapUuidMaxIdName - find out latest name id for given uuid + // mapUuidNamePartRevision - find out name part revision for given uuid + + HashMap<Long, DeviceRevision> mapIdDeviceRevision = new HashMap<>((int)(deviceNames.size()/0.75 + 2)); + for (DeviceRevision deviceRevision : deviceNames) { + mapIdDeviceRevision.put(deviceRevision.getId(), deviceRevision); + } + HashMap<UUID, Long> mapUuidMaxIdName = new HashMap<>(); + for (Name name : names) { + if (mapUuidMaxIdName.get(name.getUuid()) == null + || name.getId() > mapUuidMaxIdName.get(name.getUuid())) { + mapUuidMaxIdName.put(name.getUuid(), name.getId()); + } + } + HashMap<UUID, NamePartRevision> mapUuidNamePartRevision = new HashMap<>((int)(namePartRevisions.size()/0.75 + 2)); + for (NamePartRevision namePartRevision : namePartRevisions) { + mapUuidNamePartRevision.put(namePartRevision.getNamePart().getUuid(), namePartRevision); + } + + prepareLogReport("readMigrationDeviceRevision, utility, mapIdDeviceRevision.size: " + mapIdDeviceRevision.size(), reportHtml); + prepareLogReport("readMigrationDeviceRevision, utility, mapUuidMaxIdName.size: " + mapUuidMaxIdName.size(), reportHtml); + prepareLogReport("readMigrationDeviceRevision, utility, mapUuidNamePartRevision.size: " + mapUuidNamePartRevision.size(), reportHtml); + + // keep track of id + // ok + // nok + SortedSet<Long> id_ok_deviceRevisionDevice = new TreeSet<>(); + SortedSet<Long> id_nok_deviceRevisionDevice = new TreeSet<>(); + + prepareLogReport("readMigrationDeviceRevision, check, before, id_ok_deviceRevisionDevice.size: " + id_ok_deviceRevisionDevice.size(), reportHtml); + prepareLogReport("readMigrationDeviceRevision, check, before, id_nok_deviceRevisionDevice.size: " + id_nok_deviceRevisionDevice.size(), reportHtml); + + // name + // check entry by entry + // each attribute as expected + boolean check = false; + DeviceRevision deviceRevision = null; + for (Name name : names) { + check = true; + deviceRevision = mapIdDeviceRevision.get(name.getId()); + + check = deviceRevision != null; + + check = check && name.getId().equals(deviceRevision.getId()); + check = check && name.getVersion().equals(deviceRevision.getVersion()); + check = check && name.getUuid().equals(deviceRevision.getDevice().getUuid()); + + // to check 1st, 2nd, 3rd parent to determine if systemgroup, system or subsystem + // otherwise not clear how to make sure if it is supposed to be systemgroup and not system, subsystem and vice versa + NamePartRevision parent1 = deviceRevision.getSection() != null + ? mapUuidNamePartRevision.get(deviceRevision.getSection().getUuid()) + : null; + NamePartRevision parent2 = parent1 != null && parent1.getParent() != null + ? mapUuidNamePartRevision.get(parent1.getParent().getUuid()) + : null; + NamePartRevision parent3 = parent2 != null && parent2.getParent() != null + ? mapUuidNamePartRevision.get(parent2.getParent().getUuid()) + : null; + // parent 1 but not parent 2, 3 - system group + // parent 1, 2 but not parent 3 - system + // parent 1, 2, 3 - subsystem + // else nok + UUID systemGroupUuid = name.getSystemgroupUuid(); + UUID systemUuid = name.getSystemUuid(); + UUID subsystemUuid = name.getSubsystemUuid(); + UUID sectionUuid = deviceRevision.getSection().getUuid(); + if (parent1 != null && parent2 == null && parent3 == null) { + check = check && sectionUuid.equals(systemGroupUuid) && systemUuid == null && subsystemUuid == null; + } else if (parent1 != null && parent2 != null && parent3 == null) { + check = check && sectionUuid.equals(systemUuid) && systemGroupUuid == null && subsystemUuid == null; + } else if (parent1 != null && parent2 != null && parent3 != null) { + check = check && sectionUuid.equals(subsystemUuid) && systemGroupUuid == null && systemUuid == null; + } else { + check = false; + } + + check = check && ((name.getDevicetypeUuid() == null && deviceRevision.getDeviceType() == null) + || (name.getDevicetypeUuid().equals(deviceRevision.getDeviceType().getUuid()))); + check = check && StringUtils.equals(name.getInstanceIndex(), deviceRevision.getInstanceIndex()); + check = check && StringUtils.equals(name.getConventionName(), deviceRevision.getConventionName()); + check = check && StringUtils.equals(name.getConventionNameEquivalence(), deviceRevision.getConventionNameEqClass()); + check = check && StringUtils.equals(name.getDescription(), deviceRevision.getAdditionalInfo()); + check = check && name.getStatus() == null; + + // latest + // true if id = get max id for uuid + check = check && name.isLatest() == name.getId().equals(mapUuidMaxIdName.get(name.getUuid())); + check = check && name.isDeleted() == deviceRevision.isDeleted(); + + // date may be in different format for different objects, to be formatted before being compared + check = check && ((name.getRequested() == null && deviceRevision.getRequestDate() == null) + || StringUtils.equals(SDF.format(name.getRequested()), SDF.format(deviceRevision.getRequestDate()))); + check = check && StringUtils.equals(name.getRequestedBy(), deviceRevision.getRequestedBy().getUsername()); + check = check && name.getRequestedComment() == null; + check = check && name.getProcessed() == null; + check = check && name.getProcessedBy() == null; + check = check && StringUtils.equals(name.getProcessedComment(), deviceRevision.getProcessorComment()); + + // add to count + if (check) { + id_ok_deviceRevisionDevice.add(name.getId()); + } else { + id_nok_deviceRevisionDevice.add(name.getId()); + } + } + + // ok if + // all entries ok + // no entry nok + boolean ok = id_ok_deviceRevisionDevice.size() == names.size() + && id_nok_deviceRevisionDevice.size() == 0; + + prepareLogReport("readMigrationDeviceRevision, check, after, id_ok_deviceRevisionDevice.size: " + id_ok_deviceRevisionDevice.size(), reportHtml); + prepareLogReport("readMigrationDeviceRevision, check, after, id_nok_deviceRevisionDevice.size: " + id_nok_deviceRevisionDevice.size(), reportHtml); + prepareLogReport("readMigrationDeviceRevision, ok: " + ok, reportHtml); + + return reportHtml.toString(); + } + + /** + * Perform verification of data migration with focus on name part revision. + * Ok if all entries ok and no entry nok. + * + * @return report of data migration + */ + @GetMapping ("/migration_namepartrevision") + public String readMigrationNamePartRevision() { + // verification of + // systemgroup + // system + // subsystem + // discipline + // devicegroup + // devicetype + // vs. + // namepartrevision + // with help of namepartrevision, namepart + + // note + // check entry by entry + // ---------- + // to check parent uuid + // ---------- + // date may be in different format for different objects, to be formatted before being compared + // ---------- + // e.g. + // system.id = namepartrevision.id + // system.version = namepartrevision.version + // system.uuid = namepartrevision.namepart_id (namepart.id --> namepart.uuid) + // ( system.parent_uuid = namepartrevision.parent_id (namepart.id --> namepart.uuid) ) + // system.name = namepartrevision.name + // system.mnemonic = namepartrevision.mnemonic + // system.mnemonicequivalence = namepartrevision.mnemoniceqclass + // system.description = namepartrevision.description + // system.status = namepartrevision.status + // system.latest = true if id = get max id for uuid (consider status, but not PENDING) + // system.deleted = namepartrevision.deleted + // system.requested = namepartrevision.requestdate + // system.requested_by = namepartrevision.requestedby_id (useraccount.id --> useraccount.username) + // system.requested_comment = namepartrevision.requestercomment + // system.processed = namepartrevision.processdate + // system.processed_by = namepartrevision.processedby_id (useraccount.id --> useraccount.username) + // system.processed_comment = namepartrevision.processorcomment + + StringBuilder reportHtml = new StringBuilder(); + + // find data + // for verification + // system groups + // systems + // subsystems + // disciplines + // device groups + // device types + // to support + // name part revisions + + List<SystemGroup> systemGroups = holderIRepositories.getSystemGroupRepository().findAll(); + List<System> systems = holderIRepositories.getSystemRepository().findAll(); + List<Subsystem> subsystems = holderIRepositories.getSubsystemRepository().findAll(); + List<Discipline> disciplines = holderIRepositories.getDisciplineRepository().findAll(); + List<DeviceGroup> deviceGroups = holderIRepositories.getDeviceGroupRepository().findAll(); + List<DeviceType> deviceTypes = holderIRepositories.getDeviceTypeRepository().findAll(); + List<NamePartRevision> namePartRevisions = namePartRevisionRepository.findAll(); + + prepareLogReport("readMigrationNamePartRevision, find data, systemGroups.size: " + systemGroups.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, find data, systems.size: " + systems.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, find data, subsystems.size: " + subsystems.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, find data, disciplines.size: " + disciplines.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, find data, deviceGroups.size: " + deviceGroups.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, find data, deviceTypes.size: " + deviceTypes.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, find data, namePartRevisions.size: " + namePartRevisions.size(), reportHtml); + + // utility + // interpret lists into hashmaps for faster retrieval in for loop below + // used to help check name entries + // ---------- + // mapIdNamePartRevision - find corresponding (old) name part revision for given id + // mapUuidMaxIdSystemGroup - find out latest system group id for given uuid + // mapUuidMaxIdSystem - find out latest system id for given uuid + // mapUuidMaxIdSubsystem - find out latest subsystem id for given uuid + // mapUuidMaxIdDiscipline - find out latest discipline id for given uuid + // mapUuidMaxIdDeviceGroup - find out latest device group id for given uuid + // mapUuidMaxIdDeviceType - find out latest device type id for given uuid + + HashMap<Long, NamePartRevision> mapIdNamePartRevision = new HashMap<>((int)(namePartRevisions.size()/0.75 + 2)); + for (NamePartRevision namePartRevision : namePartRevisions) { + mapIdNamePartRevision.put(namePartRevision.getId(), namePartRevision); + } + HashMap<UUID, Long> mapUuidMaxIdSystemGroup = new HashMap<>(); + for (SystemGroup systemGroup : systemGroups) { + if ( (mapUuidMaxIdSystemGroup.get(systemGroup.getUuid()) == null + || systemGroup.getId() > mapUuidMaxIdSystemGroup.get(systemGroup.getUuid())) + && NamePartRevisionStatus.APPROVED.name().equals(systemGroup.getStatus().name())) { + mapUuidMaxIdSystemGroup.put(systemGroup.getUuid(), systemGroup.getId()); + } + } + HashMap<UUID, Long> mapUuidMaxIdSystem = new HashMap<>(); + for (System system : systems) { + if (( mapUuidMaxIdSystem.get(system.getUuid()) == null + || system.getId() > mapUuidMaxIdSystem.get(system.getUuid())) + && NamePartRevisionStatus.APPROVED.name().equals(system.getStatus().name())) { + mapUuidMaxIdSystem.put(system.getUuid(), system.getId()); + } + } + HashMap<UUID, Long> mapUuidMaxIdSubsystem = new HashMap<>(); + for (Subsystem subsystem : subsystems) { + if ( (mapUuidMaxIdSubsystem.get(subsystem.getUuid()) == null + || subsystem.getId() > mapUuidMaxIdSubsystem.get(subsystem.getUuid())) + && NamePartRevisionStatus.APPROVED.name().equals(subsystem.getStatus().name())) { + mapUuidMaxIdSubsystem.put(subsystem.getUuid(), subsystem.getId()); + } + } + HashMap<UUID, Long> mapUuidMaxIdDiscipline = new HashMap<>(); + for (Discipline discipline : disciplines) { + if ((mapUuidMaxIdDiscipline.get(discipline.getUuid()) == null + || discipline.getId() > mapUuidMaxIdDiscipline.get(discipline.getUuid())) + && NamePartRevisionStatus.APPROVED.name().equals(discipline.getStatus().name())) { + mapUuidMaxIdDiscipline.put(discipline.getUuid(), discipline.getId()); + } + } + HashMap<UUID, Long> mapUuidMaxIdDeviceGroup = new HashMap<>(); + for (DeviceGroup deviceGroup : deviceGroups) { + if ((mapUuidMaxIdDeviceGroup.get(deviceGroup.getUuid()) == null + || deviceGroup.getId() > mapUuidMaxIdDeviceGroup.get(deviceGroup.getUuid())) + && NamePartRevisionStatus.APPROVED.name().equals(deviceGroup.getStatus().name())) { + mapUuidMaxIdDeviceGroup.put(deviceGroup.getUuid(), deviceGroup.getId()); + } + } + HashMap<UUID, Long> mapUuidMaxIdDeviceType = new HashMap<>(); + for (DeviceType deviceType : deviceTypes) { + if ((mapUuidMaxIdDeviceType.get(deviceType.getUuid()) == null + || deviceType.getId() > mapUuidMaxIdDeviceType.get(deviceType.getUuid())) + && NamePartRevisionStatus.APPROVED.name().equals(deviceType.getStatus().name())) { + mapUuidMaxIdDeviceType.put(deviceType.getUuid(), deviceType.getId()); + } + } + + prepareLogReport("readMigrationNamePartRevision, utility, mapIdNamePartRevision.size: " + mapIdNamePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, utility, mapUuidMaxIdSystemGroup.size: " + mapUuidMaxIdSystemGroup.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, utility, mapUuidMaxIdSystem.size: " + mapUuidMaxIdSystem.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, utility, mapUuidMaxIdSubsystem.size: " + mapUuidMaxIdSubsystem.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, utility, mapUuidMaxIdDiscipline.size: " + mapUuidMaxIdDiscipline.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, utility, mapUuidMaxIdDeviceGroup.size: " + mapUuidMaxIdDeviceGroup.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, utility, mapUuidMaxIdDeviceType.size: " + mapUuidMaxIdDeviceType.size(), reportHtml); + + // keep track of id + // ok + // nok + SortedSet<Long> id_ok_namePartRevision = new TreeSet<>(); + SortedSet<Long> id_nok_namePartRevision = new TreeSet<>(); + + prepareLogReport("readMigrationNamePartRevision, check, before, id_ok_namePartRevision.size: " + id_ok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, check, before, id_nok_namePartRevision.size: " + id_nok_namePartRevision.size(), reportHtml); + + // system group + // check entry by entry + // each attribute as expected + boolean check = false; + NamePartRevision namePartRevision = null; + for (SystemGroup systemGroup : systemGroups) { + check = true; + namePartRevision = mapIdNamePartRevision.get(systemGroup.getId()); + + check = namePartRevision != null; + + check = check && systemGroup.getId().equals(namePartRevision.getId()); + check = check && systemGroup.getVersion().equals(namePartRevision.getVersion()); + check = check && systemGroup.getUuid().equals(namePartRevision.getNamePart().getUuid()); + + // no parent uuid for system group + check = check && StringUtils.equals(systemGroup.getName(), namePartRevision.getName()); + check = check && StringUtils.equals(systemGroup.getMnemonic(), namePartRevision.getMnemonic()); + check = check && StringUtils.equals(systemGroup.getMnemonicEquivalence(), namePartRevision.getMnemonicEqClass()); + check = check && StringUtils.equals(systemGroup.getDescription(), namePartRevision.getDescription()); + check = check && ((systemGroup.getStatus() == null && namePartRevision.getStatus() == null) + || (systemGroup.getStatus().name().equals(namePartRevision.getStatus().name()))); + + // latest + // true if id = get max id for uuid + // special rules for pending, not consider pending + check = check && systemGroup.isLatest() == systemGroup.getId().equals(mapUuidMaxIdSystemGroup.get(systemGroup.getUuid())); + check = check && systemGroup.isDeleted() == namePartRevision.isDeleted(); + + // date may be in different format for different objects, to be formatted before being compared + check = check && ((systemGroup.getRequested() == null && namePartRevision.getRequestDate() == null) + || StringUtils.equals(SDF.format(systemGroup.getRequested()), SDF.format(namePartRevision.getRequestDate()))); + check = check && (systemGroup.getRequestedBy() == null && namePartRevision.getRequestedBy() == null + || StringUtils.equals(systemGroup.getRequestedBy(), namePartRevision.getRequestedBy().getUsername())); + check = check && StringUtils.equals(systemGroup.getRequestedComment(), namePartRevision.getRequesterComment()); + check = check && ((systemGroup.getProcessed() == null && namePartRevision.getProcessDate() == null) + || StringUtils.equals(SDF.format(systemGroup.getProcessed()), SDF.format(namePartRevision.getProcessDate()))); + check = check && (systemGroup.getProcessedBy() == null && namePartRevision.getProcessedBy() == null + || StringUtils.equals(systemGroup.getProcessedBy(), namePartRevision.getProcessedBy().getUsername())); + check = check && StringUtils.equals(systemGroup.getProcessedComment(), namePartRevision.getProcessorComment()); + + // add to count + if (check) { + id_ok_namePartRevision.add(systemGroup.getId()); + } else { + id_nok_namePartRevision.add(systemGroup.getId()); + } + } + prepareLogReport("readMigrationNamePartRevision, check, after systemgroup, id_ok_namePartRevision.size: " + id_ok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, check, after systemgroup, id_nok_namePartRevision.size: " + id_nok_namePartRevision.size(), reportHtml); + + // system + // check entry by entry + // each attribute as expected + for (System system : systems) { + check = true; + namePartRevision = mapIdNamePartRevision.get(system.getId()); + + check = namePartRevision != null; + + check = check && system.getId().equals(namePartRevision.getId()); + check = check && system.getVersion().equals(namePartRevision.getVersion()); + check = check && system.getUuid().equals(namePartRevision.getNamePart().getUuid()); + + // parent uuid + check = check && system.getParentUuid().equals(namePartRevision.getParent().getUuid()); + check = check && StringUtils.equals(system.getName(), namePartRevision.getName()); + check = check && StringUtils.equals(system.getMnemonic(), namePartRevision.getMnemonic()); + check = check && StringUtils.equals(system.getMnemonicEquivalence(), namePartRevision.getMnemonicEqClass()); + check = check && StringUtils.equals(system.getDescription(), namePartRevision.getDescription()); + check = check && ((system.getStatus() == null && namePartRevision.getStatus() == null) + || (system.getStatus().name().equals(namePartRevision.getStatus().name()))); + + // latest + // true if id = get max id for uuid + // special rules for pending, not consider pending + check = check && system.isLatest() == system.getId().equals(mapUuidMaxIdSystem.get(system.getUuid())); + check = check && system.isDeleted() == namePartRevision.isDeleted(); + + // date may be in different format for different objects, to be formatted before being compared + check = check && ((system.getRequested() == null && namePartRevision.getRequestDate() == null) + || StringUtils.equals(SDF.format(system.getRequested()), SDF.format(namePartRevision.getRequestDate()))); + check = check && (system.getRequestedBy() == null && namePartRevision.getRequestedBy() == null + || StringUtils.equals(system.getRequestedBy(), namePartRevision.getRequestedBy().getUsername())); + check = check && StringUtils.equals(system.getRequestedComment(), namePartRevision.getRequesterComment()); + check = check && ((system.getProcessed() == null && namePartRevision.getProcessDate() == null) + || StringUtils.equals(SDF.format(system.getProcessed()), SDF.format(namePartRevision.getProcessDate()))); + check = check && (system.getProcessedBy() == null && namePartRevision.getProcessedBy() == null + || StringUtils.equals(system.getProcessedBy(), namePartRevision.getProcessedBy().getUsername())); + check = check && StringUtils.equals(system.getProcessedComment(), namePartRevision.getProcessorComment()); + + // add to count + if (check) { + id_ok_namePartRevision.add(system.getId()); + } else { + id_nok_namePartRevision.add(system.getId()); + } + } + prepareLogReport("readMigrationNamePartRevision, check, after system, id_ok_namePartRevision.size: " + id_ok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, check, after system, id_nok_namePartRevision.size: " + id_nok_namePartRevision.size(), reportHtml); + + // subsystem + // check entry by entry + // each attribute as expected + for (Subsystem subsystem : subsystems) { + check = true; + namePartRevision = mapIdNamePartRevision.get(subsystem.getId()); + + check = namePartRevision != null; + + check = check && subsystem.getId().equals(namePartRevision.getId()); + check = check && subsystem.getVersion().equals(namePartRevision.getVersion()); + check = check && subsystem.getUuid().equals(namePartRevision.getNamePart().getUuid()); + + // parent uuid + check = check && subsystem.getParentUuid().equals(namePartRevision.getParent().getUuid()); + check = check && StringUtils.equals(subsystem.getName(), namePartRevision.getName()); + check = check && StringUtils.equals(subsystem.getMnemonic(), namePartRevision.getMnemonic()); + check = check && StringUtils.equals(subsystem.getMnemonicEquivalence(), namePartRevision.getMnemonicEqClass()); + check = check && StringUtils.equals(subsystem.getDescription(), namePartRevision.getDescription()); + check = check && ((subsystem.getStatus() == null && namePartRevision.getStatus() == null) + || (subsystem.getStatus().name().equals(namePartRevision.getStatus().name()))); + + // latest + // true if id = get max id for uuid + // special rules for pending, not consider pending + check = check && subsystem.isLatest() == subsystem.getId().equals(mapUuidMaxIdSubsystem.get(subsystem.getUuid())); + check = check && subsystem.isDeleted() == namePartRevision.isDeleted(); + + // date may be in different format for different objects, to be formatted before being compared + check = check && ((subsystem.getRequested() == null && namePartRevision.getRequestDate() == null) + || StringUtils.equals(SDF.format(subsystem.getRequested()), SDF.format(namePartRevision.getRequestDate()))); + check = check && (subsystem.getRequestedBy() == null && namePartRevision.getRequestedBy() == null + || StringUtils.equals(subsystem.getRequestedBy(), namePartRevision.getRequestedBy().getUsername())); + check = check && StringUtils.equals(subsystem.getRequestedComment(), namePartRevision.getRequesterComment()); + check = check && ((subsystem.getProcessed() == null && namePartRevision.getProcessDate() == null) + || StringUtils.equals(SDF.format(subsystem.getProcessed()), SDF.format(namePartRevision.getProcessDate()))); + check = check && (subsystem.getProcessedBy() == null && namePartRevision.getProcessedBy() == null + || StringUtils.equals(subsystem.getProcessedBy(), namePartRevision.getProcessedBy().getUsername())); + check = check && StringUtils.equals(subsystem.getProcessedComment(), namePartRevision.getProcessorComment()); + + // add to count + if (check) { + id_ok_namePartRevision.add(subsystem.getId()); + } else { + id_nok_namePartRevision.add(subsystem.getId()); + } + } + prepareLogReport("readMigrationNamePartRevision, check, after subsystem, id_ok_namePartRevision.size: " + id_ok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, check, after subsystem, id_nok_namePartRevision.size: " + id_nok_namePartRevision.size(), reportHtml); + + // discipline + // check entry by entry + // each attribute as expected + for (Discipline discipline : disciplines) { + check = true; + namePartRevision = mapIdNamePartRevision.get(discipline.getId()); + + check = namePartRevision != null; + + check = check && discipline.getId().equals(namePartRevision.getId()); + check = check && discipline.getVersion().equals(namePartRevision.getVersion()); + check = check && discipline.getUuid().equals(namePartRevision.getNamePart().getUuid()); + + // no parent uuid for discipline + check = check && StringUtils.equals(discipline.getName(), namePartRevision.getName()); + check = check && StringUtils.equals(discipline.getMnemonic(), namePartRevision.getMnemonic()); + check = check && StringUtils.equals(discipline.getMnemonicEquivalence(), namePartRevision.getMnemonicEqClass()); + check = check && StringUtils.equals(discipline.getDescription(), namePartRevision.getDescription()); + check = check && ((discipline.getStatus() == null && namePartRevision.getStatus() == null) + || (discipline.getStatus().name().equals(namePartRevision.getStatus().name()))); + + // latest + // true if id = get max id for uuid + // special rules for pending, not consider pending + check = check && discipline.isLatest() == discipline.getId().equals(mapUuidMaxIdDiscipline.get(discipline.getUuid())); + check = check && discipline.isDeleted() == namePartRevision.isDeleted(); + + // date may be in different format for different objects, to be formatted before being compared + check = check && ((discipline.getRequested() == null && namePartRevision.getRequestDate() == null) + || StringUtils.equals(SDF.format(discipline.getRequested()), SDF.format(namePartRevision.getRequestDate()))); + check = check && (discipline.getRequestedBy() == null && namePartRevision.getRequestedBy() == null + || StringUtils.equals(discipline.getRequestedBy(), namePartRevision.getRequestedBy().getUsername())); + check = check && StringUtils.equals(discipline.getRequestedComment(), namePartRevision.getRequesterComment()); + check = check && ((discipline.getProcessed() == null && namePartRevision.getProcessDate() == null) + || StringUtils.equals(SDF.format(discipline.getProcessed()), SDF.format(namePartRevision.getProcessDate()))); + check = check && (discipline.getProcessedBy() == null && namePartRevision.getProcessedBy() == null + || StringUtils.equals(discipline.getProcessedBy(), namePartRevision.getProcessedBy().getUsername())); + check = check && StringUtils.equals(discipline.getProcessedComment(), namePartRevision.getProcessorComment()); + + // add to count + if (check) { + id_ok_namePartRevision.add(discipline.getId()); + } else { + id_nok_namePartRevision.add(discipline.getId()); + } + } + prepareLogReport("readMigrationNamePartRevision, check, after discipline, id_ok_namePartRevision.size: " + id_ok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, check, after discipline, id_nok_namePartRevision.size: " + id_nok_namePartRevision.size(), reportHtml); + + // device group + // check entry by entry + // each attribute as expected + for (DeviceGroup deviceGroup : deviceGroups) { + check = true; + namePartRevision = mapIdNamePartRevision.get(deviceGroup.getId()); + + check = namePartRevision != null; + + check = check && deviceGroup.getId().equals(namePartRevision.getId()); + check = check && deviceGroup.getVersion().equals(namePartRevision.getVersion()); + check = check && deviceGroup.getUuid().equals(namePartRevision.getNamePart().getUuid()); + + // parent uuid + check = check && deviceGroup.getParentUuid().equals(namePartRevision.getParent().getUuid()); + check = check && StringUtils.equals(deviceGroup.getName(), namePartRevision.getName()); + check = check && StringUtils.equals(deviceGroup.getMnemonic(), namePartRevision.getMnemonic()); + check = check && StringUtils.equals(deviceGroup.getMnemonicEquivalence(), namePartRevision.getMnemonicEqClass()); + check = check && StringUtils.equals(deviceGroup.getDescription(), namePartRevision.getDescription()); + check = check && ((deviceGroup.getStatus() == null && namePartRevision.getStatus() == null) + || (deviceGroup.getStatus().name().equals(namePartRevision.getStatus().name()))); + + // latest + // true if id = get max id for uuid + // special rules for pending, not consider pending + check = check && deviceGroup.isLatest() == deviceGroup.getId().equals(mapUuidMaxIdDeviceGroup.get(deviceGroup.getUuid())); + check = check && deviceGroup.isDeleted() == namePartRevision.isDeleted(); + + // date may be in different format for different objects, to be formatted before being compared + check = check && ((deviceGroup.getRequested() == null && namePartRevision.getRequestDate() == null) + || StringUtils.equals(SDF.format(deviceGroup.getRequested()), SDF.format(namePartRevision.getRequestDate()))); + check = check && (deviceGroup.getRequestedBy() == null && namePartRevision.getRequestedBy() == null + || StringUtils.equals(deviceGroup.getRequestedBy(), namePartRevision.getRequestedBy().getUsername())); + check = check && StringUtils.equals(deviceGroup.getRequestedComment(), namePartRevision.getRequesterComment()); + check = check && ((deviceGroup.getProcessed() == null && namePartRevision.getProcessDate() == null) + || StringUtils.equals(SDF.format(deviceGroup.getProcessed()), SDF.format(namePartRevision.getProcessDate()))); + check = check && (deviceGroup.getProcessedBy() == null && namePartRevision.getProcessedBy() == null + || StringUtils.equals(deviceGroup.getProcessedBy(), namePartRevision.getProcessedBy().getUsername())); + check = check && StringUtils.equals(deviceGroup.getProcessedComment(), namePartRevision.getProcessorComment()); + + // add to count + if (check) { + id_ok_namePartRevision.add(deviceGroup.getId()); + } else { + id_nok_namePartRevision.add(deviceGroup.getId()); + } + } + prepareLogReport("readMigrationNamePartRevision, check, after devicegroup, id_ok_namePartRevision.size: " + id_ok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, check, after devicegroup, id_nok_namePartRevision.size: " + id_nok_namePartRevision.size(), reportHtml); + + // device type + // check entry by entry + // each attribute as expected + for (DeviceType deviceType : deviceTypes) { + check = true; + namePartRevision = mapIdNamePartRevision.get(deviceType.getId()); + + check = namePartRevision != null; + + check = check && deviceType.getId().equals(namePartRevision.getId()); + check = check && deviceType.getVersion().equals(namePartRevision.getVersion()); + check = check && deviceType.getUuid().equals(namePartRevision.getNamePart().getUuid()); + + // parent uuid + check = check && deviceType.getParentUuid().equals(namePartRevision.getParent().getUuid()); + check = check && StringUtils.equals(deviceType.getName(), namePartRevision.getName()); + check = check && StringUtils.equals(deviceType.getMnemonic(), namePartRevision.getMnemonic()); + check = check && StringUtils.equals(deviceType.getMnemonicEquivalence(), namePartRevision.getMnemonicEqClass()); + check = check && StringUtils.equals(deviceType.getDescription(), namePartRevision.getDescription()); + check = check && ((deviceType.getStatus() == null && namePartRevision.getStatus() == null) + || (deviceType.getStatus().name().equals(namePartRevision.getStatus().name()))); + + // latest + // true if id = get max id for uuid + // special rules for pending, not consider pending + check = check && deviceType.isLatest() == deviceType.getId().equals(mapUuidMaxIdDeviceType.get(deviceType.getUuid())); + check = check && deviceType.isDeleted() == namePartRevision.isDeleted(); + + // date may be in different format for different objects, to be formatted before being compared + check = check && ((deviceType.getRequested() == null && namePartRevision.getRequestDate() == null) + || StringUtils.equals(SDF.format(deviceType.getRequested()), SDF.format(namePartRevision.getRequestDate()))); + check = check && (deviceType.getRequestedBy() == null && namePartRevision.getRequestedBy() == null + || StringUtils.equals(deviceType.getRequestedBy(), namePartRevision.getRequestedBy().getUsername())); + check = check && StringUtils.equals(deviceType.getRequestedComment(), namePartRevision.getRequesterComment()); + check = check && ((deviceType.getProcessed() == null && namePartRevision.getProcessDate() == null) + || StringUtils.equals(SDF.format(deviceType.getProcessed()), SDF.format(namePartRevision.getProcessDate()))); + check = check && (deviceType.getProcessedBy() == null && namePartRevision.getProcessedBy() == null + || StringUtils.equals(deviceType.getProcessedBy(), namePartRevision.getProcessedBy().getUsername())); + check = check && StringUtils.equals(deviceType.getProcessedComment(), namePartRevision.getProcessorComment()); + + // add to count + if (check) { + id_ok_namePartRevision.add(deviceType.getId()); + } else { + id_nok_namePartRevision.add(deviceType.getId()); + } + } + prepareLogReport("readMigrationNamePartRevision, check, after devicetype, id_ok_namePartRevision.size: " + id_ok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, check, after devicetype, id_nok_namePartRevision.size: " + id_nok_namePartRevision.size(), reportHtml); + + // ok if + // all entries ok + // no entry nok + boolean ok = id_ok_namePartRevision.size() == namePartRevisions.size() + && id_nok_namePartRevision.size() == 0; + + prepareLogReport("readMigrationNamePartRevision, check, after, id_ok_namePartRevision.size: " + id_ok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, check, after, id_nok_namePartRevision.size: " + id_nok_namePartRevision.size(), reportHtml); + prepareLogReport("readMigrationNamePartRevision, ok: " + ok, reportHtml); + + return reportHtml.toString(); + } + + // ---------------------------------------------------------------------------------------------------- + + /** + * Perform verification of data in sense that all data can be reached. Current data is one thing but suggested data (pending) and old data (obsolete) is another thing. + * All data reached verification can be done like selecting all entries from tables (names, system group, system, subsystem, discipline, device group, device type) + * and then take uuid and retrieve history for uuid. By that, all entries should be reached. In a sense, select distinct uuid, then retrieve history by uuid, + * all ids should be encompassed by looking at all returned rows. Requires a couple of for loops and maps and sets to keep track of things. + * + * This verification concerns itself with new database, not old database. + * + * @return report of reachability of data + */ + @GetMapping ("/data_reachable") + public String readDataReachable() { + StringBuilder reportHtml = new StringBuilder(); + + List<Name> names = holderIRepositories.getNameRepository().findAll(); + List<SystemGroup> systemGroups = holderIRepositories.getSystemGroupRepository().findAll(); + List<System> systems = holderIRepositories.getSystemRepository().findAll(); + List<Subsystem> subsystems = holderIRepositories.getSubsystemRepository().findAll(); + List<Discipline> disciplines = holderIRepositories.getDisciplineRepository().findAll(); + List<DeviceGroup> deviceGroups = holderIRepositories.getDeviceGroupRepository().findAll(); + List<DeviceType> deviceTypes = holderIRepositories.getDeviceTypeRepository().findAll(); + + prepareLogReport("readDataReachable, find data, names.size: " + names.size(), reportHtml); + prepareLogReport("readDataReachable, find data, systemGroups.size: " + systemGroups.size(), reportHtml); + prepareLogReport("readDataReachable, find data, systems.size: " + systems.size(), reportHtml); + prepareLogReport("readDataReachable, find data, subsystems.size: " + subsystems.size(), reportHtml); + prepareLogReport("readDataReachable, find data, disciplines.size: " + disciplines.size(), reportHtml); + prepareLogReport("readDataReachable, find data, deviceGroups.size: " + deviceGroups.size(), reportHtml); + prepareLogReport("readDataReachable, find data, deviceTypes.size: " + deviceTypes.size(), reportHtml); + + HashSet<Long> setIdName = new HashSet<>(); + HashSet<Long> setIdSystemGroup = new HashSet<>(); + HashSet<Long> setIdSystem = new HashSet<>(); + HashSet<Long> setIdSubsystem = new HashSet<>(); + HashSet<Long> setIdDiscipline = new HashSet<>(); + HashSet<Long> setIdDeviceGroup = new HashSet<>(); + HashSet<Long> setIdDeviceType = new HashSet<>(); + + List<Name> namesByUuid = null; + List<SystemGroup> systemGroupsByUuid = null; + List<System> systemsByUuid = null; + List<Subsystem> subsystemsByUuid = null; + List<Discipline> disciplinesByUuid = null; + List<DeviceGroup> deviceGroupsByUuid = null; + List<DeviceType> deviceTypesByUuid = null; + + prepareLogReport("readDataReachable, check, before", reportHtml); + + // get started + // go through list(s) + // get entries per uuid - to mimic find history through REST API by uuid + // keep track of ids in set(s) + // ok if set(s) size same as list(s) size + // any entry not in set or once in set + + for (Name name : names) { + // to mimic - Find history for name by uuid + namesByUuid = holderIRepositories.getNameRepository().findByUuid(name.getUuid().toString()); + for (Name nameByUuid : namesByUuid) { + setIdName.add(nameByUuid.getId()); + } + } + + prepareLogReport("readDataReachable, check, after name, setIdName.size: " + setIdName.size(), reportHtml); + + for (SystemGroup systemGroup : systemGroups) { + // to mimic - Find history for system group by uuid + systemGroupsByUuid = holderIRepositories.getSystemGroupRepository().findByUuid(systemGroup.getUuid().toString()); + for (SystemGroup systemGroupByUuid : systemGroupsByUuid) { + setIdSystemGroup.add(systemGroupByUuid.getId()); + } + } + + prepareLogReport("readDataReachable, check, after systemgroup, setIdSystemGroup.size: " + setIdSystemGroup.size(), reportHtml); + + for (System system : systems) { + // to mimic - Find history for system by uuid + systemsByUuid = holderIRepositories.getSystemRepository().findByUuid(system.getUuid().toString()); + for (System systemByUuid : systemsByUuid) { + setIdSystem.add(systemByUuid.getId()); + } + } + + prepareLogReport("readDataReachable, check, after system, setIdSystem.size: " + setIdSystem.size(), reportHtml); + + for (Subsystem subsystem : subsystems) { + // to mimic - Find history for subsystem by uuid + subsystemsByUuid = holderIRepositories.getSubsystemRepository().findByUuid(subsystem.getUuid().toString()); + for (Subsystem subsystemByUuid : subsystemsByUuid) { + setIdSubsystem.add(subsystemByUuid.getId()); + } + } + + prepareLogReport("readDataReachable, check, after subsystem, setIdSubsystem.size: " + setIdSubsystem.size(), reportHtml); + + for (Discipline discipline : disciplines) { + // to mimic - Find history for discipline by uuid + disciplinesByUuid = holderIRepositories.getDisciplineRepository().findByUuid(discipline.getUuid().toString()); + for (Discipline disciplineByUuid : disciplinesByUuid) { + setIdDiscipline.add(disciplineByUuid.getId()); + } + } + + prepareLogReport("readDataReachable, check, after discipline, setIdDiscipline.size: " + setIdDiscipline.size(), reportHtml); + + for (DeviceGroup deviceGroup : deviceGroups) { + // to mimic - Find history for device group by uuid + deviceGroupsByUuid = holderIRepositories.getDeviceGroupRepository().findByUuid(deviceGroup.getUuid().toString()); + for (DeviceGroup deviceGroupByUuid : deviceGroupsByUuid) { + setIdDeviceGroup.add(deviceGroupByUuid.getId()); + } + } + + prepareLogReport("readDataReachable, check, after devicegroup, setIdDeviceGroup.size: " + setIdDeviceGroup.size(), reportHtml); + + for (DeviceType deviceType : deviceTypes) { + // to mimic - Find history for device type by uuid + deviceTypesByUuid = holderIRepositories.getDeviceTypeRepository().findByUuid(deviceType.getUuid().toString()); + for (DeviceType deviceTypeByUuid : deviceTypesByUuid) { + setIdDeviceType.add(deviceTypeByUuid.getId()); + } + } + + prepareLogReport("readDataReachable, check, after devicetype, setIdDeviceType.size: " + setIdDeviceType.size(), reportHtml); + + boolean ok = names.size() == setIdName.size() + && systemGroups.size() == setIdSystemGroup.size() + && systems.size() == setIdSystem.size() + && subsystems.size() == setIdSubsystem.size() + && disciplines.size() == setIdDiscipline.size() + && deviceGroups.size() == setIdDeviceGroup.size() + && deviceTypes.size() == setIdDeviceType.size(); + + prepareLogReport("readDataReachable, check, after", reportHtml); + prepareLogReport("readDataReachable, ok: " + ok, reportHtml); + + return reportHtml.toString(); + } + + /** + * Verify difference between old REST API and new REST API. + * + * Amount is less in new than in old. Verify that all content in new REST API is in old REST API and that difference is old/obsolete data. + * + * @return + */ + @GetMapping ("/restapi_oldvsnew") + public String readRestApiOldVsNew() { + StringBuilder reportHtml = new StringBuilder(); + + // prepare old REST API + + List<DeviceRevision> deviceRevisions = deviceRevisionRepository.findAll(); + + NameViewProvider nameViewProvider = new NameViewProvider(); + nameViewProvider.update(deviceRevisions); + + // prepare new REST API + + List<Name> names = holderIRepositories.getNameRepository().findLatestNotDeleted(); + List<Name> namesLatest = holderIRepositories.getNameRepository().findLatest(); + + prepareLogReport("readRestApiOldVsNew, find data, deviceRevisions.size: " + deviceRevisions.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, find data, nameViewProvider.getNameRevisions.entrySet.size: " + nameViewProvider.getNameRevisions().entrySet().size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, find data, names.size: " + names.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, find data, namesLatest.size: " + namesLatest.size(), reportHtml); + + // prepare comparison + + HashMap<Long, DeviceRevision> mapIdDeviceRevision = new HashMap<>((int)(nameViewProvider.getNameRevisions().entrySet().size()/0.75 + 2)); + HashMap<Long, DeviceRevision> mapIdDeviceRevisionDifference = new HashMap<>((int)(nameViewProvider.getNameRevisions().entrySet().size()/0.75 + 2)); + HashMap<Long, UUID> mapIdUuidDeviceRevision = new HashMap<>((int)(nameViewProvider.getNameRevisions().entrySet().size()/0.75 + 2)); + for (Map.Entry<String, DeviceRevision> entry : nameViewProvider.getNameRevisions().entrySet()) { + mapIdDeviceRevision.put(entry.getValue().getId(), entry.getValue()); + mapIdDeviceRevisionDifference.put(entry.getValue().getId(), entry.getValue()); + mapIdUuidDeviceRevision.put(entry.getValue().getId(), entry.getValue().getDevice().getUuid()); + } + + HashMap<Long, Name> mapIdName = new HashMap<>((int)(names.size()/0.75 + 2)); + for (Name name : names) { + mapIdName.put(name.getId(), name); + } + HashMap<UUID, Name> mapUuidNameLatest = new HashMap<>((int)(namesLatest.size()/0.75 + 2)); + for (Name name : namesLatest) { + mapUuidNameLatest.put(name.getUuid(), name); + } + + prepareLogReport("readRestApiOldVsNew, utility, mapIdDeviceRevision.size: " + mapIdDeviceRevision.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, utility, mapIdDeviceRevisionDifference.size: " + mapIdDeviceRevisionDifference.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, utility, mapIdUuidDeviceRevision.size: " + mapIdUuidDeviceRevision.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, utility, mapIdName.size: " + mapIdName.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, utility, mapUuidNameLatest.size: " + mapUuidNameLatest.size(), reportHtml); + + prepareLogReport("readRestApiOldVsNew, check, before", reportHtml); + + // compare old REST API with new REST API + // 1. difference + + boolean idInOld = true; + int countIdInOld = 0; + int countNotIdInOld = 0; + for (Name name : names) { + idInOld = mapIdDeviceRevisionDifference.containsKey(name.getId()); + if (idInOld) { + countIdInOld++; + } else if (!idInOld) { + countNotIdInOld++; + // break; + } + mapIdDeviceRevisionDifference.remove(name.getId()); + } + + prepareLogReport("readRestApiOldVsNew, check, after difference 1, mapIdDeviceRevision.size: " + mapIdDeviceRevision.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, check, after difference 1, mapIdDeviceRevisionDifference.size: " + mapIdDeviceRevisionDifference.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, check, after difference 1, mapIdUuidDeviceRevision.size: " + mapIdUuidDeviceRevision.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, check, after difference 1, mapIdName.size: " + mapIdName.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, check, after difference 1, mapUuidNameLatest.size: " + mapUuidNameLatest.size(), reportHtml); + prepareLogReport("readRestApiOldVsNew, check, after difference 1, countIdInOld: " + countIdInOld, reportHtml); + prepareLogReport("readRestApiOldVsNew, check, after difference 1, countNotIdInOld: " + countNotIdInOld, reportHtml); + + // 2. difference is old/obsolete data + // id corresponding to uuid should not be last in line + // <--> last in line should be deleted + + ArrayList<DeviceRevision> listDeviceRevisionWrong = new ArrayList<>(); + for (Entry<Long, DeviceRevision> entry : mapIdDeviceRevisionDifference.entrySet()) { + UUID uuid = mapIdUuidDeviceRevision.get(entry.getKey()); + Name name = uuid != null ? mapUuidNameLatest.get(uuid) : null; + + // something may be wrong + // if latest for devicerevision is in names then ok + // else if latest for devicerevision is deleted then ok + // else something is wrong + // if (!latestInLine && !name.isDeleted()) { + // <--> + // name != null && (name.isLatest() || name.isDeleted()) --> ok, else wrong + if (!(name != null && (name.isLatest() || name.isDeleted()))) { + // something is wrong + listDeviceRevisionWrong.add(entry.getValue()); + } + } + + prepareLogReport("readRestApiOldVsNew, check, after difference 2, listDeviceRevisionWrong.size: " + listDeviceRevisionWrong.size(), reportHtml); + + boolean ok = (mapIdDeviceRevisionDifference.size() == (nameViewProvider.getNameRevisions().entrySet().size() - names.size())) + && (listDeviceRevisionWrong.size() == 0); + + prepareLogReport("readRestApiOldVsNew, check, after", reportHtml); + prepareLogReport("readRestApiOldVsNew, ok: " + ok, reportHtml); + + return reportHtml.toString(); + } + + /** + * Utility method for preparing reports for logging purposes. + * + * @param message + * @param reportHtml + * @param reportLog + */ + private void prepareLogReport(String message, StringBuilder reportHtml) { + if (!StringUtils.isEmpty(message)) { + LOGGER.log(Level.INFO, message); + reportHtml.append(message + NEW_LINE_BR); + } + } + +}