Skip to content

Commit

Permalink
Add base script for verifying migrated attributes (#12841)
Browse files Browse the repository at this point in the history
* Add datastore entity comparison function (except readNotification)

* Add verify attribute entity base script

Co-authored-by: Kevin Foong <[email protected]>
  • Loading branch information
NicolasCwy and kevin9foong authored Feb 26, 2024
1 parent 59fbae7 commit ff9f3c8
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package teammates.client.scripts.sql;

import java.util.AbstractMap;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import teammates.client.connector.DatastoreClient;
import teammates.client.util.ClientProperties;
import teammates.common.util.HibernateUtil;

/**
* Protected methods may be overriden
*/
public abstract class VerifyNonCourseEntityAttributesBaseScript
<E, T> extends DatastoreClient {

protected Class<E> datastoreEntityClass;
protected Class<T> sqlEntityClass;

public VerifyNonCourseEntityAttributesBaseScript(
Class<E> datastoreEntityClass, Class<T> sqlEntityClass) {
this.datastoreEntityClass = datastoreEntityClass;
this.sqlEntityClass = sqlEntityClass;

String connectionUrl = ClientProperties.SCRIPT_API_URL;
String username = ClientProperties.SCRIPT_API_NAME;
String password = ClientProperties.SCRIPT_API_PASSWORD;

HibernateUtil.buildSessionFactory(connectionUrl, username, password);
}

/**
* Generate the Datstore id of entity to compare with on Datastore side.
*/
protected abstract String generateID(T sqlEntity);

protected E lookupDataStoreEntity(String datastoreEntityId) {
return ofy().load().type(datastoreEntityClass).id(datastoreEntityId).now();
}

protected List<T> lookupSqlEntities() {
HibernateUtil.beginTransaction();
CriteriaBuilder cb = HibernateUtil.getCriteriaBuilder();
CriteriaQuery<T> cr = cb.createQuery(sqlEntityClass);
Root<T> root = cr.from(sqlEntityClass);

cr.select(root);

List<T> sqlEntities = HibernateUtil.createQuery(cr).getResultList();
HibernateUtil.commitTransaction();

return sqlEntities;
}

/**
* Idea: lookup sql side, have all the sql entities for
* each sql entity, lookup datastore entity
* if does not match, return failure
*/
protected List<Map.Entry<T, E>> checkAllEntitiesForFailures() {
List<T> sqlEntities = lookupSqlEntities();

List<Map.Entry<T, E>> failures = new LinkedList<>();

for (T sqlEntity : sqlEntities) {
String entityId = generateID(sqlEntity);
E datastoreEntity = lookupDataStoreEntity(entityId);

if (datastoreEntity == null) {
failures.add(new AbstractMap.SimpleEntry<T, E>(sqlEntity, null));
continue;
}
boolean isEqual = sqlEntity.equals(datastoreEntity);
if (!isEqual) {
failures.add(new AbstractMap.SimpleEntry<T,E>(sqlEntity, datastoreEntity));
continue;
}
}
return failures;
}

/**
* Main function to run to verify isEqual between sql and datastore DBs.
*/
protected void runCheckAllEntities(Class<T> sqlEntityClass,
Class<E> datastoreEntityClass) {
List<Map.Entry<T, E>> failedEntities = checkAllEntitiesForFailures();
System.out.println("========================================");
if (!failedEntities.isEmpty()) {
System.err.println("Errors detected for entity: " + sqlEntityClass.getName());
for (Map.Entry<T, E> failure : failedEntities) {
System.err.println("Sql entity: " + failure.getKey() + " datastore entity: " + failure.getValue());
}
} else {
System.out.println("No errors detected for entity: " + sqlEntityClass.getName());
}
}

protected void doOperation() {
runCheckAllEntities(this.sqlEntityClass, this.datastoreEntityClass);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package teammates.client.scripts.sql;

import teammates.storage.entity.Notification;

public class VerifyNotificationAttributes extends VerifyNonCourseEntityAttributesBaseScript<Notification,
teammates.storage.sqlentity.Notification> {

static String dataStoreIdFieldName = "notificationId";

public VerifyNotificationAttributes() {
super(Notification.class,
teammates.storage.sqlentity.Notification.class);
}

@Override
protected String generateID(teammates.storage.sqlentity.Notification sqlEntity) {
return sqlEntity.getId().toString();
}

public static void main(String[] args) {
VerifyNotificationAttributes script = new VerifyNotificationAttributes();
script.doOperationRemotely();
}
}
12 changes: 12 additions & 0 deletions src/main/java/teammates/storage/sqlentity/Account.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ public List<String> getInvalidityInfo() {
return errors;
}

// Used for sql data migration
public boolean equals(teammates.storage.entity.Account acc) {
try {
// UUID for account is not checked, as datastore ID is google ID
return this.getName() == acc.getName()
&& this.getGoogleId() == acc.getGoogleId()
&& this.getEmail() == acc.getEmail();
} catch (IllegalArgumentException iae) {
return false;
}
}

@Override
public boolean equals(Object other) {
if (other == null) {
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/teammates/storage/sqlentity/AccountRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,20 @@ public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}

// Used for sql data migration
public boolean equals(teammates.storage.entity.AccountRequest accReq) {
try {
// UUID for account is not checked, as datastore ID is email%institute
return this.getRegistrationKey() == accReq.getRegistrationKey()
&& this.getName() == accReq.getName()
&& this.getEmail() == accReq.getEmail()
&& this.getInstitute() == accReq.getInstitute()
&& this.getRegisteredAt() == accReq.getRegisteredAt();
} catch (IllegalArgumentException iae) {
return false;
}
}

@Override
public boolean equals(Object other) {
if (other == null) {
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/teammates/storage/sqlentity/Notification.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,23 @@ public int hashCode() {
return this.getId().hashCode();
}

// Used for sql data migration
public boolean equals(teammates.storage.entity.Notification notif) {
try {
UUID otherUuid = UUID.fromString(notif.getNotificationId());
return this.getId() == otherUuid
&& this.getStartTime() == notif.getStartTime()
&& this.getEndTime() == notif.getEndTime()
&& this.getStyle() == notif.getStyle()
&& this.getTargetUser() == notif.getTargetUser()
&& this.getTitle() == notif.getTitle()
&& this.getMessage() == notif.getMessage()
&& this.isShown() == notif.isShown();
} catch (IllegalArgumentException iae) {
return false;
}
}

@Override
public boolean equals(Object other) {
if (other == null) {
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/teammates/storage/sqlentity/UsageStatistics.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,20 @@ public int getNumSubmissions() {
return numSubmissions;
}

// Used for sql data migration
public boolean equals(teammates.storage.entity.UsageStatistics stat) {
// UUID for account is not checked, as datastore ID is startTime%timePeriod
return this.getStartTime() == stat.getStartTime()
&& this.getTimePeriod() == stat.getTimePeriod()
&& this.getNumResponses() == stat.getNumResponses()
&& this.getNumCourses() == stat.getNumCourses()
&& this.getNumStudents() == stat.getNumStudents()
&& this.getNumInstructors() == stat.getNumInstructors()
&& this.getNumAccountRequests() == stat.getNumAccountRequests()
&& this.getNumEmails() == stat.getNumEmails()
&& this.getNumSubmissions() == stat.getNumSubmissions();
}

@Override
public boolean equals(Object other) {
if (other == null) {
Expand Down

0 comments on commit ff9f3c8

Please sign in to comment.