From 56d7c65a3b7420d2453e951fd3c13c5218b7463c Mon Sep 17 00:00:00 2001 From: Wilson Kurniawan Date: Fri, 26 Feb 2021 23:13:18 +0800 Subject: [PATCH] [#10944] Migrate Blobstore API to Google Cloud Storage (#10945) --- .gitignore | 2 + build.gradle | 19 +--- filestorage-dev/.gitkeep | 0 .../scripts/GoogleIdMigrationBaseScript.java | 25 ++--- ...ructorCourseStudentDetailsPageE2ETest.json | 3 +- .../InstructorStudentRecordsPageE2ETest.json | 3 +- .../data/StudentCourseDetailsPageE2ETest.json | 6 +- .../data/StudentProfilePageE2ETest.json | 3 +- .../lnp/cases/StudentProfileLNPTest.java | 1 - .../attributes/StudentProfileAttributes.java | 26 +----- .../java/teammates/common/util/Config.java | 8 +- .../common/util/GoogleCloudStorageHelper.java | 88 ------------------ .../java/teammates/logic/api/FileStorage.java | 51 ++++++++++ src/main/java/teammates/logic/api/Logic.java | 25 ----- .../logic/core/FileStorageService.java | 16 ++++ .../logic/core/GoogleCloudStorageService.java | 47 ++++++++++ .../logic/core/LocalFileStorageService.java | 57 ++++++++++++ .../teammates/logic/core/ProfilesLogic.java | 18 ---- .../teammates/storage/api/ProfilesDb.java | 33 +------ .../storage/entity/StudentProfile.java | 15 +-- .../output/StudentProfilePictureResults.java | 16 ---- src/main/java/teammates/ui/webapi/Action.java | 10 ++ .../ui/webapi/DeleteAccountAction.java | 7 +- .../DeleteStudentProfilePictureAction.java | 7 +- .../GetStudentProfilePictureAction.java | 5 +- .../java/teammates/ui/webapi/ImageResult.java | 18 ++-- .../PostStudentProfilePictureAction.java | 16 +--- .../ui/webapi/ResetAccountAction.java | 3 + .../ui/webapi/UpdateStudentProfileAction.java | 1 - src/main/resources/InstructorSampleData.json | 15 +-- .../architecture/ArchitectureTest.java | 26 +----- .../StudentProfileAttributesTest.java | 33 +------ .../common/util/BuildPropertiesTest.java | 2 +- .../logic/core/ProfilesLogicTest.java | 34 ------- .../teammates/storage/api/ProfilesDbTest.java | 69 -------------- .../teammates/test/BaseComponentTestCase.java | 18 ++-- .../java/teammates/test/BaseTestCase.java | 4 + .../java/teammates/test/EmailChecker.java | 2 +- .../java/teammates/test/EmailCheckerTest.java | 2 +- src/test/java/teammates/test/FileHelper.java | 9 ++ .../java/teammates/test/GaeSimulation.java | 1 + .../java/teammates/test/MockFileStorage.java | 47 ++++++++++ ...DeleteStudentProfilePictureActionTest.java | 14 ++- .../GetStudentProfilePictureActionTest.java | 21 +++-- .../PostStudentProfilePictureActionTest.java | 14 +-- .../FeedbackSessionResultsBundleTest.json | 3 +- .../data/FeedbackSessionsLogicTest.json | 3 +- .../resources/data/typicalDataBundle.json | 6 +- src/test/resources/filestorage/.gitkeep | 0 .../resources/images/profile_pic_default.png | Bin 12494 -> 0 bytes .../student-profile-page.component.spec.ts | 38 +++----- .../student-profile-page.component.ts | 2 +- src/web/services/student-profile.service.ts | 2 +- 53 files changed, 360 insertions(+), 534 deletions(-) create mode 100644 filestorage-dev/.gitkeep delete mode 100644 src/main/java/teammates/common/util/GoogleCloudStorageHelper.java create mode 100644 src/main/java/teammates/logic/api/FileStorage.java create mode 100644 src/main/java/teammates/logic/core/FileStorageService.java create mode 100644 src/main/java/teammates/logic/core/GoogleCloudStorageService.java create mode 100644 src/main/java/teammates/logic/core/LocalFileStorageService.java delete mode 100644 src/main/java/teammates/ui/output/StudentProfilePictureResults.java create mode 100644 src/test/java/teammates/test/MockFileStorage.java create mode 100644 src/test/resources/filestorage/.gitkeep delete mode 100644 src/test/resources/images/profile_pic_default.png diff --git a/.gitignore b/.gitignore index 62777417c82..e32f5ca7c21 100644 --- a/.gitignore +++ b/.gitignore @@ -60,5 +60,7 @@ src/client/java/teammates/client/scripts/statistics/data/ src/client/java/teammates/client/scripts/log/ src/e2e/resources/gmail-api/ src/e2e/resources/downloads/ +filestorage-dev/* +src/test/resources/filestorage/**/* !.gitkeep diff --git a/build.gradle b/build.gradle index 1eec9f64526..6415ffec9f5 100644 --- a/build.gradle +++ b/build.gradle @@ -56,19 +56,7 @@ dependencies { implementation("com.google.appengine:appengine-api-1.0-sdk:${appengineVersion}") implementation("com.google.cloud:google-cloud-tasks:1.30.11") implementation("com.google.cloud:google-cloud-logging:2.1.2") - - // This dependency needs to be resolved individually - // because the main dependency (appengine-gcs-client) does not have its transitive dependencies up-to-date - implementation("com.google.appengine.tools:appengine-gcs-client:0.8.1") { - // Use the newer servlet library instead - exclude group: "javax.servlet", module: "servlet-api" - } - implementation("com.google.api-client:google-api-client-appengine:1.30.9") { - // Use the newer servlet library instead - exclude group: "javax.servlet", module: "servlet-api" - } - implementation("com.google.apis:google-api-services-storage:v1-rev20200430-1.30.9") - + implementation("com.google.cloud:google-cloud-storage:1.113.9") implementation("com.google.code.gson:gson:2.8.6") implementation("com.google.guava:guava:30.1-jre") implementation(objectify) @@ -280,9 +268,8 @@ appengine { port = 8080 jvmFlags = ["-Xss2m", "-Dfile.encoding=UTF-8", // Absolute paths are not supported, the following is relative to the project directory - // These only specify the datastore/blobstore paths, but search indexes are still generated in WEB-INF/appengine-generated - "-Ddatastore.backing_store=../../appengine-generated/local_db.bin", - "-Dblobstore.backing_store=../../appengine-generated"] + // These only specify the datastore paths, but search indexes are still generated in WEB-INF/appengine-generated + "-Ddatastore.backing_store=../../appengine-generated/local_db.bin"] automaticRestart = true } deploy { diff --git a/filestorage-dev/.gitkeep b/filestorage-dev/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/client/java/teammates/client/scripts/GoogleIdMigrationBaseScript.java b/src/client/java/teammates/client/scripts/GoogleIdMigrationBaseScript.java index e1d00b10709..34acec68948 100644 --- a/src/client/java/teammates/client/scripts/GoogleIdMigrationBaseScript.java +++ b/src/client/java/teammates/client/scripts/GoogleIdMigrationBaseScript.java @@ -3,17 +3,15 @@ import java.util.List; import java.util.stream.Collectors; -import com.google.appengine.tools.cloudstorage.GcsFilename; -import com.google.appengine.tools.cloudstorage.GcsService; -import com.google.appengine.tools.cloudstorage.GcsServiceFactory; -import com.google.appengine.tools.cloudstorage.RetryParams; +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; import com.googlecode.objectify.Key; import com.googlecode.objectify.cmd.Query; import teammates.client.util.ClientProperties; import teammates.common.datatransfer.attributes.InstructorAttributes; import teammates.common.util.Config; -import teammates.common.util.GoogleCloudStorageHelper; import teammates.storage.api.InstructorsDb; import teammates.storage.entity.Account; import teammates.storage.entity.CourseStudent; @@ -100,23 +98,14 @@ protected void migrateEntity(Account oldAccount) throws Exception { ofy().delete().type(Account.class).id(oldGoogleId).now(); if (oldStudentProfile != null) { - String pictureKey = oldStudentProfile.getPictureKey(); - if (!ClientProperties.isTargetUrlDevServer()) { - try { - GcsFilename oldGcsFilename = new GcsFilename(Config.PRODUCTION_GCS_BUCKETNAME, oldGoogleId); - GcsFilename newGcsFilename = new GcsFilename(Config.PRODUCTION_GCS_BUCKETNAME, newGoogleId); - GcsService gcsService = GcsServiceFactory.createGcsService(RetryParams.getDefaultInstance()); - gcsService.copy(oldGcsFilename, newGcsFilename); - gcsService.delete(oldGcsFilename); - pictureKey = GoogleCloudStorageHelper.createBlobKey(newGoogleId); - } catch (Exception e) { - log("Profile picture not exist or error during copy: " + e.getMessage()); - } + Storage storage = StorageOptions.newBuilder().setProjectId(Config.APP_ID).build().getService(); + Blob blob = storage.get(Config.PRODUCTION_GCS_BUCKETNAME, oldGoogleId); + blob.copyTo(Config.PRODUCTION_GCS_BUCKETNAME, newGoogleId); + blob.delete(); } oldStudentProfile.setGoogleId(newGoogleId); - oldStudentProfile.setPictureKey(pictureKey); ofy().save().entity(oldStudentProfile).now(); ofy().delete().key(oldStudentProfileKey).now(); } diff --git a/src/e2e/resources/data/InstructorCourseStudentDetailsPageE2ETest.json b/src/e2e/resources/data/InstructorCourseStudentDetailsPageE2ETest.json index ee0fc74230c..8597f267338 100644 --- a/src/e2e/resources/data/InstructorCourseStudentDetailsPageE2ETest.json +++ b/src/e2e/resources/data/InstructorCourseStudentDetailsPageE2ETest.json @@ -72,8 +72,7 @@ "institute": "TEAMMATES Test Institute 7", "nationality": "Laotian", "gender": "MALE", - "moreInfo": "This is a lot of info...", - "pictureKey": "" + "moreInfo": "This is a lot of info..." } } } diff --git a/src/e2e/resources/data/InstructorStudentRecordsPageE2ETest.json b/src/e2e/resources/data/InstructorStudentRecordsPageE2ETest.json index 89e49fbdc5a..3690dddf3e5 100644 --- a/src/e2e/resources/data/InstructorStudentRecordsPageE2ETest.json +++ b/src/e2e/resources/data/InstructorStudentRecordsPageE2ETest.json @@ -70,8 +70,7 @@ "institute": "TEAMMATES Test Institute 7", "nationality": "Singaporean", "gender": "MALE", - "moreInfo": "This is a lot of info!", - "pictureKey": "" + "moreInfo": "This is a lot of info!" } } } diff --git a/src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json b/src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json index 4b920209171..e4d3d6ce97c 100644 --- a/src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json +++ b/src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json @@ -105,8 +105,7 @@ "institute": "inst", "nationality": "American", "gender": "OTHER", - "moreInfo": "I am just a student :P", - "pictureKey": "" + "moreInfo": "I am just a student :P" }, "SCDet.charlie": { "googleId": "tm.e2e.SCDet.charlie", @@ -115,8 +114,7 @@ "institute": "inst", "nationality": "Singaporean", "gender": "OTHER", - "moreInfo": "I am also a student :P", - "pictureKey": "" + "moreInfo": "I am also a student :P" } } } diff --git a/src/e2e/resources/data/StudentProfilePageE2ETest.json b/src/e2e/resources/data/StudentProfilePageE2ETest.json index e8254f5d344..3c99c049407 100644 --- a/src/e2e/resources/data/StudentProfilePageE2ETest.json +++ b/src/e2e/resources/data/StudentProfilePageE2ETest.json @@ -39,8 +39,7 @@ "institute": "TEAMMATES Test Institute 4", "nationality": "Singaporean", "gender": "MALE", - "moreInfo": "I am just another student :P", - "pictureKey": "" + "moreInfo": "I am just another student :P" } } } diff --git a/src/lnp/java/teammates/lnp/cases/StudentProfileLNPTest.java b/src/lnp/java/teammates/lnp/cases/StudentProfileLNPTest.java index a9cd0759e68..1190be0f144 100644 --- a/src/lnp/java/teammates/lnp/cases/StudentProfileLNPTest.java +++ b/src/lnp/java/teammates/lnp/cases/StudentProfileLNPTest.java @@ -120,7 +120,6 @@ protected Map generateProfiles() { .withShortName(String.valueOf(i)) .withInstitute("TEAMMATES Test Institute 222") .withMoreInfo("I am " + i) - .withPictureKey("") .withGender(StudentProfileAttributes.Gender.MALE) .withNationality("American") .build() diff --git a/src/main/java/teammates/common/datatransfer/attributes/StudentProfileAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/StudentProfileAttributes.java index 431c660590c..6e555107b08 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/StudentProfileAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/StudentProfileAttributes.java @@ -25,7 +25,6 @@ public class StudentProfileAttributes extends EntityAttributes { public String nationality; public Gender gender; public String moreInfo; - public String pictureKey; public Instant modifiedDate; private StudentProfileAttributes(String googleId) { @@ -36,7 +35,6 @@ private StudentProfileAttributes(String googleId) { this.nationality = ""; this.gender = Gender.OTHER; this.moreInfo = ""; - this.pictureKey = ""; this.modifiedDate = Instant.now(); } @@ -59,9 +57,6 @@ public static StudentProfileAttributes valueOf(StudentProfile sp) { if (sp.getMoreInfo() != null) { studentProfileAttributes.moreInfo = sp.getMoreInfo(); } - if (sp.getPictureKey() != null) { - studentProfileAttributes.pictureKey = sp.getPictureKey(); - } if (sp.getModifiedDate() != null) { studentProfileAttributes.modifiedDate = sp.getModifiedDate(); } @@ -85,7 +80,6 @@ public StudentProfileAttributes getCopy() { studentProfileAttributes.gender = gender; studentProfileAttributes.nationality = nationality; studentProfileAttributes.moreInfo = moreInfo; - studentProfileAttributes.pictureKey = pictureKey; studentProfileAttributes.modifiedDate = modifiedDate; return studentProfileAttributes; @@ -119,10 +113,6 @@ public String getMoreInfo() { return moreInfo; } - public String getPictureKey() { - return pictureKey; - } - public Instant getModifiedDate() { return modifiedDate; } @@ -153,8 +143,6 @@ public List getInvalidityInfo() { Assumption.assertNotNull(gender); - Assumption.assertNotNull(this.pictureKey); - // No validation for modified date as it is determined by the system. // No validation for More Info. It will properly sanitized. @@ -170,7 +158,7 @@ public String toString() { public int hashCode() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(this.email).append(this.shortName).append(this.institute) - .append(this.googleId).append(this.pictureKey).append(this.gender.toString()); + .append(this.googleId).append(this.gender.toString()); return stringBuilder.toString().hashCode(); } @@ -186,7 +174,6 @@ public boolean equals(Object other) { && Objects.equals(this.shortName, otherProfile.shortName) && Objects.equals(this.institute, otherProfile.institute) && Objects.equals(this.googleId, otherProfile.googleId) - && Objects.equals(this.pictureKey, otherProfile.pictureKey) && Objects.equals(this.gender, otherProfile.gender); } else { return false; @@ -196,7 +183,7 @@ public boolean equals(Object other) { @Override public StudentProfile toEntity() { return new StudentProfile(googleId, shortName, email, institute, nationality, gender.name().toLowerCase(), - moreInfo, this.pictureKey); + moreInfo); } @Override @@ -214,7 +201,6 @@ public void update(UpdateOptions updateOptions) { updateOptions.nationalityOption.ifPresent(s -> nationality = s); updateOptions.genderOption.ifPresent(s -> gender = s); updateOptions.moreInfoOption.ifPresent(s -> moreInfo = s); - updateOptions.pictureKeyOption.ifPresent(s -> pictureKey = s); } /** @@ -280,7 +266,6 @@ public static class UpdateOptions { private UpdateOption nationalityOption = UpdateOption.empty(); private UpdateOption genderOption = UpdateOption.empty(); private UpdateOption moreInfoOption = UpdateOption.empty(); - private UpdateOption pictureKeyOption = UpdateOption.empty(); private UpdateOptions(String googleId) { Assumption.assertNotNull(googleId); @@ -380,13 +365,6 @@ public B withMoreInfo(String moreInfo) { return thisBuilder; } - public B withPictureKey(String pictureKey) { - Assumption.assertNotNull(pictureKey); - - updateOptions.pictureKeyOption = UpdateOption.of(pictureKey); - return thisBuilder; - } - public abstract T build(); } diff --git a/src/main/java/teammates/common/util/Config.java b/src/main/java/teammates/common/util/Config.java index 90d1808fcb1..1b9fa3b2b80 100644 --- a/src/main/java/teammates/common/util/Config.java +++ b/src/main/java/teammates/common/util/Config.java @@ -15,9 +15,6 @@ */ public final class Config { - /** The value of the application URL, or null if no server instance is running. */ - public static final String APP_URL; - /** The value of the "app.id" in build.properties file. */ public static final String APP_ID; @@ -88,7 +85,6 @@ public final class Config { public static final boolean MAINTENANCE; static { - APP_URL = readAppUrl(); Properties properties = new Properties(); try (InputStream buildPropStream = FileHelper.getResourceAsStream("build.properties")) { properties.load(buildPropStream); @@ -124,7 +120,7 @@ private Config() { // access static fields directly } - private static String readAppUrl() { + static String getBaseAppUrl() { ApiProxy.Environment serverEnvironment = ApiProxy.getCurrentEnvironment(); if (serverEnvironment == null) { return null; @@ -179,7 +175,7 @@ public static AppUrl getFrontEndAppUrl(String relativeUrl) { * {@code relativeUrl} must start with a "/". */ private static AppUrl getBackEndAppUrl(String relativeUrl) { - return new AppUrl(APP_URL + relativeUrl); + return new AppUrl(getBaseAppUrl() + relativeUrl); } public static boolean isUsingSendgrid() { diff --git a/src/main/java/teammates/common/util/GoogleCloudStorageHelper.java b/src/main/java/teammates/common/util/GoogleCloudStorageHelper.java deleted file mode 100644 index a5c82f23dc0..00000000000 --- a/src/main/java/teammates/common/util/GoogleCloudStorageHelper.java +++ /dev/null @@ -1,88 +0,0 @@ -package teammates.common.util; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import javax.servlet.http.HttpServletResponse; - -import com.google.appengine.api.blobstore.BlobKey; -import com.google.appengine.api.blobstore.BlobstoreService; -import com.google.appengine.api.blobstore.BlobstoreServiceFactory; -import com.google.appengine.tools.cloudstorage.GcsFileOptions; -import com.google.appengine.tools.cloudstorage.GcsFilename; -import com.google.appengine.tools.cloudstorage.GcsOutputChannel; -import com.google.appengine.tools.cloudstorage.GcsServiceFactory; -import com.google.appengine.tools.cloudstorage.RetryParams; - -/** - * Holds functions for operations related to Google Cloud Storage. - */ -public final class GoogleCloudStorageHelper { - - private static final Logger log = Logger.getLogger(); - - private GoogleCloudStorageHelper() { - // utility class - } - - private static BlobstoreService service() { - return BlobstoreServiceFactory.getBlobstoreService(); - } - - /** - * Returns true if a file with the specified {@code fileKey} exists in the - * Google Cloud Storage. - */ - public static boolean doesFileExistInGcs(String fileKey) { - try { - service().fetchData(new BlobKey(fileKey), 0, 1); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } - - /** - * Deletes the file with the specified {@code fileKey} in the Google Cloud Storage. - */ - public static void deleteFile(String fileKey) { - try { - service().delete(new BlobKey(fileKey)); - } catch (Exception e) { - log.warning("Trying to delete non-existent file with key: " + fileKey); - } - } - - /** - * Writes a byte array {@code imageData} as image to the Google Cloud Storage, - * with the {@code googleId} as the identifier name for the image. - * - * @return the {@link BlobKey} used as the image's identifier in Google Cloud Storage - */ - public static String writeImageDataToGcs(String googleId, byte[] imageData, String contentType) throws IOException { - GcsFilename gcsFilename = new GcsFilename(Config.PRODUCTION_GCS_BUCKETNAME, googleId); - try (GcsOutputChannel outputChannel = - GcsServiceFactory.createGcsService(RetryParams.getDefaultInstance()) - .createOrReplace(gcsFilename, new GcsFileOptions.Builder().mimeType(contentType).build())) { - - outputChannel.write(ByteBuffer.wrap(imageData)); - } - - return createBlobKey(googleId); - } - - /** - * Creates a blob key for the object with the given identifier in the production GCS bucket. - */ - public static String createBlobKey(String identifier) { - return service().createGsBlobKey("/gs/" + Config.PRODUCTION_GCS_BUCKETNAME + "/" + identifier).getKeyString(); - } - - /** - * Serves the content of the file with the specified {@code fileKey} as the body of the given HTTP response. - */ - public static void serve(HttpServletResponse resp, String fileKey) throws IOException { - service().serve(new BlobKey(fileKey), resp); - } - -} diff --git a/src/main/java/teammates/logic/api/FileStorage.java b/src/main/java/teammates/logic/api/FileStorage.java new file mode 100644 index 00000000000..9cbe9a733e0 --- /dev/null +++ b/src/main/java/teammates/logic/api/FileStorage.java @@ -0,0 +1,51 @@ +package teammates.logic.api; + +import teammates.common.util.Config; +import teammates.logic.core.FileStorageService; +import teammates.logic.core.GoogleCloudStorageService; +import teammates.logic.core.LocalFileStorageService; + +/** + * Handles operations related to binary files. + */ +public class FileStorage { + + private final FileStorageService service; + + public FileStorage() { + if (Config.isDevServer()) { + service = new LocalFileStorageService(); + } else { + service = new GoogleCloudStorageService(); + } + } + + /** + * Returns true if a file with the specified {@code fileKey} exists in the storage. + */ + public boolean doesFileExist(String fileKey) { + return service.doesFileExist(fileKey); + } + + /** + * Gets the content of the file with the specified {@code fileKey} as bytes. + */ + public byte[] getContent(String fileKey) { + return service.getContent(fileKey); + } + + /** + * Deletes the file with the specified {@code fileKey}. + */ + public void delete(String fileKey) { + service.delete(fileKey); + } + + /** + * Creates a file with the specified {@code contentBytes} as content and with type {@code contentType}. + */ + public void create(String fileKey, byte[] contentBytes, String contentType) { + service.create(fileKey, contentBytes, contentType); + } + +} diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 3769cde10ee..61e11edc5bf 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -107,31 +107,6 @@ public void deleteAccountCascade(String googleId) { accountsLogic.deleteAccountCascade(googleId); } - /** - * Delete the picture associated with the {@code key} in Cloud Storage. - * - *
Preconditions:
- * All parameters are non-null. - * - *

Fails silently if the {@code key} doesn't exist.

- */ - public void deletePicture(String key) { - Assumption.assertNotNull(key); - - profilesLogic.deletePicture(key); - } - - /** - * Deletes {@code pictureKey} for the student profile associated with {@code googleId}. - * - *

If the associated profile doesn't exist, create a new one.

- */ - public void deletePictureKey(String googleId) { - Assumption.assertNotNull(googleId); - - profilesLogic.deletePictureKey(googleId); - } - /** * Creates an instructor. * diff --git a/src/main/java/teammates/logic/core/FileStorageService.java b/src/main/java/teammates/logic/core/FileStorageService.java new file mode 100644 index 00000000000..58670b33a50 --- /dev/null +++ b/src/main/java/teammates/logic/core/FileStorageService.java @@ -0,0 +1,16 @@ +package teammates.logic.core; + +/** + * A binary file storage interface used for managing binary files such as profile pictures. + */ +public interface FileStorageService { + + boolean doesFileExist(String fileKey); + + byte[] getContent(String fileKey); + + void delete(String fileKey); + + void create(String fileKey, byte[] contentBytes, String contentType); + +} diff --git a/src/main/java/teammates/logic/core/GoogleCloudStorageService.java b/src/main/java/teammates/logic/core/GoogleCloudStorageService.java new file mode 100644 index 00000000000..8b83eae0f0b --- /dev/null +++ b/src/main/java/teammates/logic/core/GoogleCloudStorageService.java @@ -0,0 +1,47 @@ +package teammates.logic.core; + +import com.google.cloud.storage.Blob; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; + +import teammates.common.util.Config; + +/** + * Holds functions for operations related to Google Cloud Storage. + */ +public final class GoogleCloudStorageService implements FileStorageService { + + private static Storage storage = StorageOptions.getDefaultInstance().getService(); + + @Override + public void delete(String fileKey) { + storage.delete(BlobId.of(Config.PRODUCTION_GCS_BUCKETNAME, fileKey)); + } + + @Override + public void create(String fileKey, byte[] contentBytes, String contentType) { + BlobId blobId = BlobId.of(Config.PRODUCTION_GCS_BUCKETNAME, fileKey); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(contentType).build(); + storage.create(blobInfo, contentBytes); + } + + @Override + public boolean doesFileExist(String fileKey) { + BlobId blobId = BlobId.of(Config.PRODUCTION_GCS_BUCKETNAME, fileKey); + Blob blob = storage.get(blobId); + return blob.exists(); + } + + @Override + public byte[] getContent(String fileKey) { + BlobId blobId = BlobId.of(Config.PRODUCTION_GCS_BUCKETNAME, fileKey); + Blob blob = storage.get(blobId); + if (blob == null) { + return new byte[0]; + } + return blob.getContent(); + } + +} diff --git a/src/main/java/teammates/logic/core/LocalFileStorageService.java b/src/main/java/teammates/logic/core/LocalFileStorageService.java new file mode 100644 index 00000000000..a8c8741caf9 --- /dev/null +++ b/src/main/java/teammates/logic/core/LocalFileStorageService.java @@ -0,0 +1,57 @@ +package teammates.logic.core; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +import teammates.common.exception.TeammatesException; +import teammates.common.util.Logger; + +/** + * Holds functions for operations related to binary file storage in local dev environment. + */ +public final class LocalFileStorageService implements FileStorageService { + + private static final String BASE_DIRECTORY = "../../filestorage-dev"; + private static final Logger log = Logger.getLogger(); + + private static String constructFilePath(String fileKey) { + return BASE_DIRECTORY + "/" + fileKey; + } + + @Override + public void delete(String fileKey) { + File file = new File(constructFilePath(fileKey)); + file.delete(); + } + + @Override + public void create(String fileKey, byte[] contentBytes, String contentType) { + try (OutputStream os = Files.newOutputStream(Paths.get(constructFilePath(fileKey)))) { + os.write(contentBytes); + } catch (IOException e) { + log.warning(TeammatesException.toStringWithStackTrace(e)); + } + } + + @Override + public boolean doesFileExist(String fileKey) { + return Files.exists(Paths.get(constructFilePath(fileKey))); + } + + @Override + public byte[] getContent(String fileKey) { + byte[] buffer = new byte[1024 * 300]; + try (InputStream fis = Files.newInputStream(Paths.get(constructFilePath(fileKey)))) { + fis.read(buffer); + } catch (IOException e) { + log.warning(TeammatesException.toStringWithStackTrace(e)); + return new byte[0]; + } + return buffer; + } + +} diff --git a/src/main/java/teammates/logic/core/ProfilesLogic.java b/src/main/java/teammates/logic/core/ProfilesLogic.java index 4d5dd79a3ae..5a1cc315d80 100644 --- a/src/main/java/teammates/logic/core/ProfilesLogic.java +++ b/src/main/java/teammates/logic/core/ProfilesLogic.java @@ -50,22 +50,4 @@ public void deleteStudentProfile(String googleId) { profilesDb.deleteStudentProfile(googleId); } - /** - * Deletes picture associated with the {@code key}. - * - *

Fails silently if the {@code key} doesn't exist.

- */ - public void deletePicture(String key) { - profilesDb.deletePicture(key); - } - - /** - * Deletes {@code pictureKey} for the student profile associated with {@code googleId}. - * - *

If the associated profile doesn't exist, create a new one.

- */ - public void deletePictureKey(String googleId) { - profilesDb.deletePictureKey(googleId); - } - } diff --git a/src/main/java/teammates/storage/api/ProfilesDb.java b/src/main/java/teammates/storage/api/ProfilesDb.java index 7efbc5f9f40..53c94757d91 100644 --- a/src/main/java/teammates/storage/api/ProfilesDb.java +++ b/src/main/java/teammates/storage/api/ProfilesDb.java @@ -10,7 +10,6 @@ import teammates.common.datatransfer.attributes.StudentProfileAttributes; import teammates.common.exception.InvalidParametersException; import teammates.common.util.Assumption; -import teammates.common.util.GoogleCloudStorageHelper; import teammates.storage.entity.Account; import teammates.storage.entity.StudentProfile; @@ -62,8 +61,7 @@ public StudentProfileAttributes updateOrCreateStudentProfile(StudentProfileAttri && this.hasSameValue(studentProfile.getInstitute(), newAttributes.getInstitute()) && this.hasSameValue(studentProfile.getNationality(), newAttributes.getNationality()) && this.hasSameValue(studentProfile.getGender(), newAttributes.getGender().name().toLowerCase()) - && this.hasSameValue(studentProfile.getMoreInfo(), newAttributes.getMoreInfo()) - && this.hasSameValue(studentProfile.getPictureKey(), newAttributes.getPictureKey()); + && this.hasSameValue(studentProfile.getMoreInfo(), newAttributes.getMoreInfo()); if (!shouldCreateEntity && hasSameAttributes) { log.info(String.format(OPTIMIZED_SAVING_POLICY_APPLIED, StudentProfile.class.getSimpleName(), updateOptions)); return newAttributes; @@ -75,7 +73,6 @@ public StudentProfileAttributes updateOrCreateStudentProfile(StudentProfileAttri studentProfile.setNationality(newAttributes.nationality); studentProfile.setGender(newAttributes.gender.name().toLowerCase()); studentProfile.setMoreInfo(newAttributes.moreInfo); - studentProfile.setPictureKey(newAttributes.pictureKey); studentProfile.setModifiedDate(Instant.now()); saveEntity(studentProfile); @@ -93,39 +90,11 @@ public void deleteStudentProfile(String googleId) { if (sp == null) { return; } - if (!sp.getPictureKey().equals("")) { - deletePicture(sp.getPictureKey()); - } Key parentKey = Key.create(Account.class, googleId); Key profileKey = Key.create(parentKey, StudentProfile.class, googleId); deleteEntity(profileKey); } - /** - * Deletes picture associated with the {@code key}. - * - *

Fails silently if the {@code key} doesn't exist.

- */ - public void deletePicture(String key) { - GoogleCloudStorageHelper.deleteFile(key); - } - - /** - * Deletes the {@code pictureKey} of the profile with given {@code googleId} by setting it to an empty string. - * - *

Fails silently if the {@code studentProfile} doesn't exist.

- */ - public void deletePictureKey(String googleId) { - Assumption.assertNotNull(googleId); - StudentProfile studentProfile = getStudentProfileEntityFromDb(googleId); - - if (studentProfile != null) { - studentProfile.setPictureKey(""); - studentProfile.setModifiedDate(Instant.now()); - saveEntity(studentProfile); - } - } - /** * Gets the profile entity associated with the {@code googleId}. * diff --git a/src/main/java/teammates/storage/entity/StudentProfile.java b/src/main/java/teammates/storage/entity/StudentProfile.java index 7492893bac8..b6a9c4428a7 100644 --- a/src/main/java/teammates/storage/entity/StudentProfile.java +++ b/src/main/java/teammates/storage/entity/StudentProfile.java @@ -2,7 +2,6 @@ import java.time.Instant; -import com.google.appengine.api.blobstore.BlobKey; import com.google.appengine.api.datastore.Text; import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Entity; @@ -43,8 +42,6 @@ public class StudentProfile extends BaseEntity { @Unindex private Text moreInfo; - private BlobKey pictureKey; - @Index @Translate(InstantTranslatorFactory.class) private Instant modifiedDate; @@ -75,7 +72,7 @@ private StudentProfile() { * Miscellaneous information, including external profile */ public StudentProfile(String googleId, String shortName, String email, String institute, - String nationality, String gender, String moreInfo, String pictureKey) { + String nationality, String gender, String moreInfo) { this.setGoogleId(googleId); this.setShortName(shortName); this.setEmail(email); @@ -84,7 +81,6 @@ public StudentProfile(String googleId, String shortName, String email, String in this.setGender(gender); this.setMoreInfo(moreInfo); this.setModifiedDate(Instant.now()); - this.setPictureKey(pictureKey); } public StudentProfile(String googleId) { @@ -95,7 +91,6 @@ public StudentProfile(String googleId) { this.setNationality(""); this.setGender("other"); this.setMoreInfo(""); - this.setPictureKey(""); this.setModifiedDate(Instant.now()); } @@ -159,14 +154,6 @@ public void setMoreInfo(String moreInfo) { this.moreInfo = moreInfo == null ? null : new Text(moreInfo); } - public String getPictureKey() { - return this.pictureKey == null ? null : this.pictureKey.getKeyString(); - } - - public void setPictureKey(String pictureKey) { - this.pictureKey = pictureKey == null ? null : new BlobKey(pictureKey); - } - public Instant getModifiedDate() { return this.modifiedDate; } diff --git a/src/main/java/teammates/ui/output/StudentProfilePictureResults.java b/src/main/java/teammates/ui/output/StudentProfilePictureResults.java deleted file mode 100644 index 678b2adae4a..00000000000 --- a/src/main/java/teammates/ui/output/StudentProfilePictureResults.java +++ /dev/null @@ -1,16 +0,0 @@ -package teammates.ui.output; - -/** - * API output for profile picture results. - */ -public class StudentProfilePictureResults extends ApiOutput { - private final String pictureKey; - - public StudentProfilePictureResults(String pictureKey) { - this.pictureKey = pictureKey; - } - - public String getPictureKey() { - return pictureKey; - } -} diff --git a/src/main/java/teammates/ui/webapi/Action.java b/src/main/java/teammates/ui/webapi/Action.java index 36497693ac9..efdff2df0e5 100644 --- a/src/main/java/teammates/ui/webapi/Action.java +++ b/src/main/java/teammates/ui/webapi/Action.java @@ -22,6 +22,7 @@ import teammates.common.util.StringHelper; import teammates.logic.api.EmailGenerator; import teammates.logic.api.EmailSender; +import teammates.logic.api.FileStorage; import teammates.logic.api.GateKeeper; import teammates.logic.api.Logic; import teammates.logic.api.TaskQueuer; @@ -40,6 +41,7 @@ public abstract class Action { EmailGenerator emailGenerator = new EmailGenerator(); TaskQueuer taskQueuer = new TaskQueuer(); EmailSender emailSender = new EmailSender(); + FileStorage fileStorage = new FileStorage(); RecaptchaVerifier recaptchaVerifier = new RecaptchaVerifier(Config.CAPTCHA_SECRET_KEY); HttpServletRequest req; @@ -73,6 +75,14 @@ public void setEmailSender(EmailSender emailSender) { this.emailSender = emailSender; } + public FileStorage getFileStorage() { + return fileStorage; + } + + public void setFileStorage(FileStorage fileStorage) { + this.fileStorage = fileStorage; + } + public void setRecaptchaVerifier(RecaptchaVerifier recaptchaVerifier) { this.recaptchaVerifier = recaptchaVerifier; } diff --git a/src/main/java/teammates/ui/webapi/DeleteAccountAction.java b/src/main/java/teammates/ui/webapi/DeleteAccountAction.java index 0c40d6901b6..d7b957796c4 100644 --- a/src/main/java/teammates/ui/webapi/DeleteAccountAction.java +++ b/src/main/java/teammates/ui/webapi/DeleteAccountAction.java @@ -11,8 +11,11 @@ class DeleteAccountAction extends AdminOnlyAction { @Override JsonResult execute() { - String instructorId = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_ID); - logic.deleteAccountCascade(instructorId); + String googleId = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_ID); + if (fileStorage.doesFileExist(googleId)) { + fileStorage.delete(googleId); + } + logic.deleteAccountCascade(googleId); return new JsonResult("Account is successfully deleted.", HttpStatus.SC_OK); } diff --git a/src/main/java/teammates/ui/webapi/DeleteStudentProfilePictureAction.java b/src/main/java/teammates/ui/webapi/DeleteStudentProfilePictureAction.java index 734ee2b3e2b..f0723448757 100644 --- a/src/main/java/teammates/ui/webapi/DeleteStudentProfilePictureAction.java +++ b/src/main/java/teammates/ui/webapi/DeleteStudentProfilePictureAction.java @@ -7,7 +7,7 @@ import teammates.common.util.Const; /** - * Deletes a student's profile picture and its picture key. + * Deletes a student's profile picture. */ class DeleteStudentProfilePictureAction extends Action { @Override @@ -33,8 +33,9 @@ JsonResult execute() { if (studentProfileAttributes == null) { return new JsonResult("Invalid student profile", HttpStatus.SC_NOT_FOUND); } - logic.deletePicture(studentProfileAttributes.pictureKey); - logic.deletePictureKey(userInfo.id); + if (fileStorage.doesFileExist(studentProfileAttributes.googleId)) { + fileStorage.delete(studentProfileAttributes.googleId); + } return new JsonResult("Your profile picture has been deleted successfully", HttpStatus.SC_OK); } } diff --git a/src/main/java/teammates/ui/webapi/GetStudentProfilePictureAction.java b/src/main/java/teammates/ui/webapi/GetStudentProfilePictureAction.java index c0f76c885bf..bb3804e7fa9 100644 --- a/src/main/java/teammates/ui/webapi/GetStudentProfilePictureAction.java +++ b/src/main/java/teammates/ui/webapi/GetStudentProfilePictureAction.java @@ -63,10 +63,11 @@ ActionResult execute() { } } - if (studentProfile == null || studentProfile.pictureKey.equals("")) { + if (studentProfile == null || !fileStorage.doesFileExist(studentProfile.googleId)) { return new ImageResult(); } - return new ImageResult(studentProfile.pictureKey); + byte[] bytes = fileStorage.getContent(studentProfile.googleId); + return new ImageResult(bytes); } } diff --git a/src/main/java/teammates/ui/webapi/ImageResult.java b/src/main/java/teammates/ui/webapi/ImageResult.java index 0d22dea7f78..c5b96d665c6 100644 --- a/src/main/java/teammates/ui/webapi/ImageResult.java +++ b/src/main/java/teammates/ui/webapi/ImageResult.java @@ -6,35 +6,31 @@ import org.apache.http.HttpStatus; -import teammates.common.util.GoogleCloudStorageHelper; - /** * Action result in form of an image. */ class ImageResult extends ActionResult { - /** The blob key for the image. */ - private String blobKey; + private byte[] bytes; ImageResult() { super(HttpStatus.SC_NO_CONTENT); + this.bytes = new byte[0]; } - ImageResult(String blobKey) { + ImageResult(byte[] bytes) { super(HttpStatus.SC_OK); - this.blobKey = blobKey; + this.bytes = bytes; } - String getBlobKey() { - return blobKey; + byte[] getBytes() { + return this.bytes; } @Override void send(HttpServletResponse resp) throws IOException { resp.setContentType("image/png"); - if (blobKey != null) { - GoogleCloudStorageHelper.serve(resp, blobKey); - } + resp.getOutputStream().write(bytes); } } diff --git a/src/main/java/teammates/ui/webapi/PostStudentProfilePictureAction.java b/src/main/java/teammates/ui/webapi/PostStudentProfilePictureAction.java index 4959f30bd4c..7d12abda0d9 100644 --- a/src/main/java/teammates/ui/webapi/PostStudentProfilePictureAction.java +++ b/src/main/java/teammates/ui/webapi/PostStudentProfilePictureAction.java @@ -8,12 +8,8 @@ import org.apache.http.HttpStatus; -import teammates.common.datatransfer.attributes.StudentProfileAttributes; import teammates.common.exception.InvalidHttpRequestBodyException; -import teammates.common.exception.InvalidParametersException; import teammates.common.exception.UnauthorizedAccessException; -import teammates.common.util.GoogleCloudStorageHelper; -import teammates.ui.output.StudentProfilePictureResults; /** * Action: saves the file information of the profile picture that was just uploaded. @@ -53,16 +49,8 @@ JsonResult execute() { try (InputStream is = image.getInputStream()) { is.read(imageData); } - String pictureKey = GoogleCloudStorageHelper.writeImageDataToGcs(userInfo.id, imageData, image.getContentType()); - logic.updateOrCreateStudentProfile( - StudentProfileAttributes.updateOptionsBuilder(userInfo.id) - .withPictureKey(pictureKey) - .build()); - StudentProfilePictureResults dataFormat = - new StudentProfilePictureResults(pictureKey); - return new JsonResult(dataFormat); - } catch (InvalidParametersException ipe) { - throw new InvalidHttpRequestBodyException(ipe.getMessage(), ipe); + fileStorage.create(userInfo.id, imageData, image.getContentType()); + return new JsonResult("Your profile picture is updated successfully."); } catch (ServletException | IOException e) { return new JsonResult(e.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } diff --git a/src/main/java/teammates/ui/webapi/ResetAccountAction.java b/src/main/java/teammates/ui/webapi/ResetAccountAction.java index fe43b82360f..85d62aacfbd 100644 --- a/src/main/java/teammates/ui/webapi/ResetAccountAction.java +++ b/src/main/java/teammates/ui/webapi/ResetAccountAction.java @@ -61,6 +61,9 @@ JsonResult execute() { if (wrongGoogleId != null && logic.getStudentsForGoogleId(wrongGoogleId).isEmpty() && logic.getInstructorsForGoogleId(wrongGoogleId).isEmpty()) { + if (fileStorage.doesFileExist(wrongGoogleId)) { + fileStorage.delete(wrongGoogleId); + } logic.deleteAccountCascade(wrongGoogleId); } diff --git a/src/main/java/teammates/ui/webapi/UpdateStudentProfileAction.java b/src/main/java/teammates/ui/webapi/UpdateStudentProfileAction.java index 686a45a5346..637ea4a6dc3 100644 --- a/src/main/java/teammates/ui/webapi/UpdateStudentProfileAction.java +++ b/src/main/java/teammates/ui/webapi/UpdateStudentProfileAction.java @@ -67,7 +67,6 @@ private StudentProfileAttributes extractProfileData(String studentId, StudentPro editedProfile.gender = StudentProfileAttributes.Gender.getGenderEnumValue(req.getGender()); editedProfile.moreInfo = req.getMoreInfo(); - editedProfile.pictureKey = ""; sanitizeProfile(editedProfile); return editedProfile; diff --git a/src/main/resources/InstructorSampleData.json b/src/main/resources/InstructorSampleData.json index d4db57a9d1f..58ef2d94ec3 100644 --- a/src/main/resources/InstructorSampleData.json +++ b/src/main/resources/InstructorSampleData.json @@ -3027,8 +3027,7 @@ "institute": "TEAMMATES Test Institute 5", "nationality": "Chinese", "gender": "FEMALE", - "moreInfo": "I am Alice from TEAMMATES Test Institute 1 and am here at Cambridge for exchange. My Major in TEAMMATES Test Institute 5 is Art History with Minor in Business Administration. I really look forward to a great and fruitful semester ahead!", - "pictureKey": "" + "moreInfo": "I am Alice from TEAMMATES Test Institute 1 and am here at Cambridge for exchange. My Major in TEAMMATES Test Institute 5 is Art History with Minor in Business Administration. I really look forward to a great and fruitful semester ahead!" }, "danny.e.tmms": { "googleId": "danny.e.tmms.sampleData", @@ -3037,8 +3036,7 @@ "institute": "TEAMMATES Test Institute 7", "nationality": "Singaporean", "gender": "MALE", - "moreInfo": "", - "pictureKey": "" + "moreInfo": "" }, "emma.f.tmms": { "googleId": "emma.f.tmms.sampleData", @@ -3047,8 +3045,7 @@ "institute": "TEAMMATES Test Institute 8", "nationality": "Indian", "gender": "FEMALE", - "moreInfo": "I am Emma from TEAMMATES Test Institute 8 India and am here at TEAMMATES Test Institute 5 for exchange. ", - "pictureKey": "" + "moreInfo": "I am Emma from TEAMMATES Test Institute 8 India and am here at TEAMMATES Test Institute 5 for exchange. " }, "charlie.d.tmms": { "googleId": "charlie.d.tmms.sampleData", @@ -3057,8 +3054,7 @@ "institute": "TEAMMATES Test Institute 8", "nationality": "American", "gender": "MALE", - "moreInfo": "I am Charlie - an exchange student", - "pictureKey": "" + "moreInfo": "I am Charlie - an exchange student" }, "gene.h.tmms@demo.course": { "googleId": "gene.h.tmms.sampleData", @@ -3067,8 +3063,7 @@ "institute": "TEAMMATES Test Institute 5", "nationality": "Nigerian", "gender": "FEMALE", - "moreInfo": "", - "pictureKey": "" + "moreInfo": "" } } } diff --git a/src/test/java/teammates/architecture/ArchitectureTest.java b/src/test/java/teammates/architecture/ArchitectureTest.java index eeda64c4a83..2f0be405fc8 100644 --- a/src/test/java/teammates/architecture/ArchitectureTest.java +++ b/src/test/java/teammates/architecture/ArchitectureTest.java @@ -455,30 +455,13 @@ public void testArchitecture_externalApi_searchApiCanOnlyBeAccessedBySomePackage } @Test - public void testArchitecture_externalApi_gcsApiCanOnlyBeAccessedByGcsHelper() { - noClasses().that().doNotHaveSimpleName("GoogleCloudStorageHelper") + public void testArchitecture_externalApi_cloudStorageApiCanOnlyBeAccessedByGcsService() { + noClasses().that().doNotHaveSimpleName("GoogleCloudStorageService") .and().resideOutsideOfPackage(includeSubpackages(CLIENT_SCRIPTS_PACKAGE)) - .should().accessClassesThat().resideInAPackage("com.google.appengine.tools.cloudstorage..") + .should().accessClassesThat().resideInAPackage("com.google.cloud.storage..") .check(ALL_CLASSES); } - @Test - public void testArchitecture_externalApi_blobstoreApiCanOnlyBeAccessedByGcsHelper() { - noClasses().that().doNotHaveSimpleName("GoogleCloudStorageHelper") - .and().resideOutsideOfPackage(includeSubpackages(STORAGE_ENTITY_PACKAGE)) - .should().accessClassesThat().resideInAPackage("com.google.appengine.api.blobstore..") - .check(ALL_CLASSES); - - noClasses().that().resideInAPackage(includeSubpackages(STORAGE_ENTITY_PACKAGE)) - .should().accessClassesThat(new DescribedPredicate("") { - @Override - public boolean apply(JavaClass input) { - return !"BlobKey".equals(input.getSimpleName()) - && input.getPackageName().startsWith("com.google.appengine.api.blobstore"); - } - }).check(ALL_CLASSES); - } - @Test public void testArchitecture_externalApi_cloudTasksApiCanOnlyBeAccessedByTaskQueueLogic() { noClasses().that().doNotHaveSimpleName("TaskQueuesLogic") @@ -513,8 +496,7 @@ public void testArchitecture_externalApi_datastoreTypesCanOnlyBeAccessedByEntity @Test public void testArchitecture_externalApi_servletApiCanOnlyBeAccessedBySomePackages() { - noClasses().that().doNotHaveSimpleName("GoogleCloudStorageHelper") - .and().doNotHaveSimpleName("HttpRequestHelper") + noClasses().that().doNotHaveSimpleName("HttpRequestHelper") .and().doNotHaveSimpleName("OfyHelper") .and().doNotHaveSimpleName("GaeSimulation") .and().doNotHaveSimpleName("MockFilterChain") diff --git a/src/test/java/teammates/common/datatransfer/attributes/StudentProfileAttributesTest.java b/src/test/java/teammates/common/datatransfer/attributes/StudentProfileAttributesTest.java index 9dee1464007..3f8b75964bd 100644 --- a/src/test/java/teammates/common/datatransfer/attributes/StudentProfileAttributesTest.java +++ b/src/test/java/teammates/common/datatransfer/attributes/StudentProfileAttributesTest.java @@ -29,7 +29,6 @@ public void classSetup() { .withNationality("Lebanese") .withGender(StudentProfileAttributes.Gender.FEMALE) .withMoreInfo("moreInfo can have a lot more than this...") - .withPictureKey("profile Pic Key") .build(); } @@ -44,7 +43,6 @@ public void testBuilder_withNothingPassed_shouldUseDefaultValues() { assertEquals("", profileAttributes.institute); assertEquals("", profileAttributes.nationality); assertEquals("", profileAttributes.moreInfo); - assertEquals("", profileAttributes.pictureKey); } @Test @@ -87,12 +85,6 @@ public void testBuilder_withNullValuePassed_shouldThrowException() { .withMoreInfo(null) .build(); }); - - assertThrows(AssertionError.class, () -> { - StudentProfileAttributes.builder(VALID_GOOGLE_ID) - .withPictureKey(null) - .build(); - }); } @Test @@ -104,7 +96,6 @@ public void testBuilder_withTypicalData_shouldBuildCorrectAttribute() { .withNationality("Lebanese") .withGender(StudentProfileAttributes.Gender.FEMALE) .withMoreInfo("moreInfo can have a lot more than this...") - .withPictureKey("profile Pic Key") .build(); assertEquals(VALID_GOOGLE_ID, studentProfileAttributes.getGoogleId()); @@ -114,14 +105,13 @@ public void testBuilder_withTypicalData_shouldBuildCorrectAttribute() { assertEquals("Lebanese", studentProfileAttributes.getNationality()); assertEquals(StudentProfileAttributes.Gender.FEMALE, studentProfileAttributes.getGender()); assertEquals("moreInfo can have a lot more than this...", studentProfileAttributes.getMoreInfo()); - assertEquals("profile Pic Key", studentProfileAttributes.getPictureKey()); } @Test public void testValueOf_withAllFieldPopulatedStudentProfile_shouldGenerateAttributesCorrectly() { StudentProfile studentProfile = new StudentProfile("id", "Joe", "joe@gmail.com", "Teammates Institute", "American", StudentProfileAttributes.Gender.MALE.name().toLowerCase(), - "hello", "key"); + "hello"); StudentProfileAttributes profileAttributes = StudentProfileAttributes.valueOf(studentProfile); assertEquals(studentProfile.getGoogleId(), profileAttributes.googleId); @@ -131,14 +121,13 @@ public void testValueOf_withAllFieldPopulatedStudentProfile_shouldGenerateAttrib assertEquals(studentProfile.getNationality(), profileAttributes.nationality); assertEquals(studentProfile.getGender(), profileAttributes.gender.name().toLowerCase()); assertEquals(studentProfile.getMoreInfo(), profileAttributes.moreInfo); - assertEquals(studentProfile.getPictureKey(), profileAttributes.pictureKey); } @Test public void testValueOf_withSomeFieldsPopulatedAsNull_shouldUseDefaultValues() { StudentProfile studentProfile = new StudentProfile("id", null, null, - null, null, null, null, null); + null, null, null, null); StudentProfileAttributes profileAttributes = StudentProfileAttributes.valueOf(studentProfile); assertEquals(studentProfile.getGoogleId(), profileAttributes.googleId); @@ -148,8 +137,6 @@ public void testValueOf_withSomeFieldsPopulatedAsNull_shouldUseDefaultValues() { assertEquals("", profileAttributes.nationality); assertEquals(StudentProfileAttributes.Gender.OTHER, profileAttributes.gender); assertEquals("", profileAttributes.moreInfo); - assertEquals("", profileAttributes.pictureKey); - } @Test @@ -204,7 +191,6 @@ public void testSanitizeForSaving() { assertEquals(profileToSanitizeExpected.nationality, profileToSanitize.nationality); assertEquals(profileToSanitizeExpected.gender, profileToSanitize.gender); assertEquals(profileToSanitizeExpected.moreInfo, profileToSanitize.moreInfo); - assertEquals(profileToSanitizeExpected.pictureKey, profileToSanitize.pictureKey); } @Override @@ -220,7 +206,6 @@ public void testToEntity() { assertEquals(expectedEntity.getNationality(), actualEntity.getNationality()); assertEquals(expectedEntity.getGender(), actualEntity.getGender()); assertEquals(expectedEntity.getMoreInfo(), actualEntity.getMoreInfo()); - assertEquals(expectedEntity.getPictureKey(), actualEntity.getPictureKey()); } @Test @@ -242,7 +227,6 @@ public void testUpdateOptions_withTypicalUpdateOptions_shouldUpdateAttributeCorr .withNationality("Singapore") .withGender(StudentProfileAttributes.Gender.MALE) .withMoreInfo("more info") - .withPictureKey("newPic") .build(); assertEquals("testGoogleId", updateOptions.getGoogleId()); @@ -257,7 +241,6 @@ public void testUpdateOptions_withTypicalUpdateOptions_shouldUpdateAttributeCorr assertEquals("Singapore", profileAttributes.nationality); assertEquals(StudentProfileAttributes.Gender.MALE, profileAttributes.gender); assertEquals("more info", profileAttributes.moreInfo); - assertEquals("newPic", profileAttributes.pictureKey); } @Test @@ -288,10 +271,6 @@ public void testUpdateOptionsBuilder_withNullInput_shouldFailWithAssertionError( assertThrows(AssertionError.class, () -> StudentProfileAttributes.updateOptionsBuilder("validId") .withMoreInfo(null)); - - assertThrows(AssertionError.class, () -> - StudentProfileAttributes.updateOptionsBuilder("validId") - .withPictureKey(null)); } @Test @@ -309,7 +288,6 @@ public void testEquals() { .withNationality("Lebanese") .withGender(StudentProfileAttributes.Gender.FEMALE) .withMoreInfo("moreInfo can have a lot more than this...") - .withPictureKey("profile Pic Key") .build(); assertTrue(profile.equals(studentProfileSimilar)); @@ -339,7 +317,6 @@ public void testHashCode() { .withNationality("Lebanese") .withGender(StudentProfileAttributes.Gender.FEMALE) .withMoreInfo("moreInfo can have a lot more than this...") - .withPictureKey("profile Pic Key") .build(); assertTrue(profile.hashCode() == studentProfileSimilar.hashCode()); @@ -359,7 +336,7 @@ private StudentProfile createStudentProfileFrom( StudentProfileAttributes profile) { return new StudentProfile(profile.googleId, profile.shortName, profile.email, profile.institute, profile.nationality, profile.gender.name().toLowerCase(), - profile.moreInfo, profile.pictureKey); + profile.moreInfo); } private List generatedExpectedErrorMessages(StudentProfileAttributes profile) throws Exception { @@ -395,7 +372,6 @@ private StudentProfileAttributes getInvalidStudentProfileAttributes() { String nationality = "$invalid nationality "; StudentProfileAttributes.Gender gender = StudentProfileAttributes.Gender.MALE; String moreInfo = "Ooops no validation for this one..."; - String pictureKey = ""; return StudentProfileAttributes.builder(googleId) .withShortName(shortName) @@ -404,7 +380,6 @@ private StudentProfileAttributes getInvalidStudentProfileAttributes() { .withNationality(nationality) .withGender(gender) .withMoreInfo(moreInfo) - .withPictureKey(pictureKey) .build(); } @@ -416,7 +391,6 @@ private StudentProfileAttributes getStudentProfileAttributesToSanitize() { String nationality = "&\"invalid nationality &"; StudentProfileAttributes.Gender gender = StudentProfileAttributes.Gender.OTHER; String moreInfo = "<"; - String pictureKey = "testPictureKey"; return StudentProfileAttributes.builder(googleId) .withShortName(shortName) @@ -425,7 +399,6 @@ private StudentProfileAttributes getStudentProfileAttributesToSanitize() { .withNationality(nationality) .withGender(gender) .withMoreInfo(moreInfo) - .withPictureKey(pictureKey) .build(); } diff --git a/src/test/java/teammates/common/util/BuildPropertiesTest.java b/src/test/java/teammates/common/util/BuildPropertiesTest.java index 25dbdc1c58d..c4d5b5fee6f 100644 --- a/src/test/java/teammates/common/util/BuildPropertiesTest.java +++ b/src/test/java/teammates/common/util/BuildPropertiesTest.java @@ -11,7 +11,7 @@ public class BuildPropertiesTest extends BaseTestCaseWithMinimalGaeEnvironment { @Test public void checkPresence() { - assertNotNull(Config.APP_URL); + assertNotNull(Config.getBaseAppUrl()); } } diff --git a/src/test/java/teammates/logic/core/ProfilesLogicTest.java b/src/test/java/teammates/logic/core/ProfilesLogicTest.java index 0b1ceae049d..cc83f58587c 100644 --- a/src/test/java/teammates/logic/core/ProfilesLogicTest.java +++ b/src/test/java/teammates/logic/core/ProfilesLogicTest.java @@ -48,29 +48,6 @@ public void testStudentProfileFunctions() throws Exception { expectedSpa.modifiedDate = actualSpa.modifiedDate; assertEquals(expectedSpa.toString(), actualSpa.toString()); assertEquals(expectedSpa.toString(), updateSpa.toString()); - - ______TS("update SP"); - - expectedSpa.pictureKey = "non-empty"; - profilesLogic.updateOrCreateStudentProfile( - StudentProfileAttributes.updateOptionsBuilder(expectedSpa.googleId) - .withPictureKey(expectedSpa.pictureKey) - .build()); - - actualSpa = profilesLogic.getStudentProfile(expectedSpa.googleId); - expectedSpa.modifiedDate = actualSpa.modifiedDate; - assertEquals(expectedSpa.toString(), actualSpa.toString()); - - ______TS("update picture"); - - expectedSpa.pictureKey = writeFileToGcs(expectedSpa.googleId, "src/test/resources/images/profile_pic.png"); - profilesLogic.updateOrCreateStudentProfile( - StudentProfileAttributes.updateOptionsBuilder(expectedSpa.googleId) - .withPictureKey(expectedSpa.pictureKey) - .build()); - actualSpa = profilesLogic.getStudentProfile(expectedSpa.googleId); - expectedSpa.modifiedDate = actualSpa.modifiedDate; - assertEquals(expectedSpa.toString(), actualSpa.toString()); } @Test @@ -80,24 +57,13 @@ public void testDeleteStudentProfile() throws Exception { profilesLogic.updateOrCreateStudentProfile( StudentProfileAttributes.updateOptionsBuilder("sp.logic.test") .withShortName("Test Name") - .withPictureKey(writeFileToGcs("sp.logic.test", "src/test/resources/images/profile_pic_default.png")) .build()); - // make sure we create an profile with picture key StudentProfileAttributes savedProfile = profilesLogic.getStudentProfile("sp.logic.test"); assertNotNull(savedProfile); - assertFalse(savedProfile.pictureKey.isEmpty()); profilesLogic.deleteStudentProfile("sp.logic.test"); // check that profile get deleted and picture get deleted verifyAbsentInDatastore(savedProfile); - assertFalse(doesFileExistInGcs(savedProfile.pictureKey)); - } - - @Test - public void testDeletePicture() throws Exception { - String keyString = writeFileToGcs("accountsLogicTestid", "src/test/resources/images/profile_pic.png"); - profilesLogic.deletePicture(keyString); - assertFalse(doesFileExistInGcs(keyString)); } } diff --git a/src/test/java/teammates/storage/api/ProfilesDbTest.java b/src/test/java/teammates/storage/api/ProfilesDbTest.java index 10452d28c7b..5e503611ee9 100644 --- a/src/test/java/teammates/storage/api/ProfilesDbTest.java +++ b/src/test/java/teammates/storage/api/ProfilesDbTest.java @@ -1,7 +1,5 @@ package teammates.storage.api; -import java.io.IOException; - import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -20,22 +18,15 @@ public class ProfilesDbTest extends BaseComponentTestCase { private StudentProfileAttributes typicalProfileWithPicture; private StudentProfileAttributes typicalProfileWithoutPicture; - private String typicalPictureKey; @BeforeMethod public void createTypicalData() throws Exception { - // typical picture - typicalPictureKey = uploadDefaultPictureForProfile("valid.googleId"); - assertTrue(doesFileExistInGcs(typicalPictureKey)); - // typical profiles profilesDb.createEntity(StudentProfileAttributes.builder("valid.googleId") .withInstitute("TEAMMATES Test Institute 1") - .withPictureKey(typicalPictureKey) .build()); profilesDb.createEntity(StudentProfileAttributes.builder("valid.googleId2") .withInstitute("TEAMMATES Test Institute 1") - .withPictureKey(typicalPictureKey) .build()); // save entity and picture @@ -50,10 +41,6 @@ public void deleteTypicalData() { profilesDb.deleteStudentProfile(typicalProfileWithoutPicture.googleId); verifyAbsentInDatastore(typicalProfileWithPicture); verifyAbsentInDatastore(typicalProfileWithoutPicture); - - // delete picture - profilesDb.deletePicture(typicalPictureKey); - assertFalse(doesFileExistInGcs(typicalPictureKey)); } @Test @@ -174,17 +161,6 @@ public void testUpdateOrCreateStudentProfile_updateSingleField_shouldUpdateCorre assertEquals("more info", updatedProfile.getMoreInfo()); assertEquals("more info", actualProfile.getMoreInfo()); assertEquals(actualProfile.getModifiedDate(), updatedProfile.getModifiedDate()); - - assertNotEquals("newPic", actualProfile.getPictureKey()); - updatedProfile = - profilesDb.updateOrCreateStudentProfile( - StudentProfileAttributes.updateOptionsBuilder(typicalProfileWithoutPicture.getGoogleId()) - .withPictureKey("newPic") - .build()); - actualProfile = profilesDb.getStudentProfile(typicalProfileWithoutPicture.getGoogleId()); - assertEquals("newPic", updatedProfile.getPictureKey()); - assertEquals("newPic", actualProfile.getPictureKey()); - assertEquals(actualProfile.getModifiedDate(), updatedProfile.getModifiedDate()); } @Test @@ -215,7 +191,6 @@ public void testUpdateOrCreateStudentProfile_noChangesToProfile_shouldNotIssueSa StudentProfileAttributes.updateOptionsBuilder(typicalProfileWithPicture.googleId) .withShortName(typicalProfileWithPicture.shortName) .withGender(typicalProfileWithPicture.gender) - .withPictureKey(typicalProfileWithPicture.pictureKey) .withMoreInfo(typicalProfileWithPicture.moreInfo) .withInstitute(typicalProfileWithPicture.institute) .withEmail(typicalProfileWithPicture.email) @@ -225,8 +200,6 @@ public void testUpdateOrCreateStudentProfile_noChangesToProfile_shouldNotIssueSa StudentProfileAttributes storedProfile = profilesDb.getStudentProfile(typicalProfileWithPicture.googleId); // other fields remain verifyPresentInDatastore(typicalProfileWithPicture); - // picture remains - assertTrue(doesFileExistInGcs(storedProfile.pictureKey)); // modifiedDate remains assertEquals(typicalProfileWithPicture.modifiedDate, storedProfile.modifiedDate); @@ -238,28 +211,10 @@ public void testUpdateOrCreateStudentProfile_noChangesToProfile_shouldNotIssueSa storedProfile = profilesDb.getStudentProfile(typicalProfileWithPicture.getGoogleId()); // other fields remain verifyPresentInDatastore(typicalProfileWithPicture); - // picture remains - assertTrue(doesFileExistInGcs(storedProfile.getPictureKey())); // modifiedDate remains assertEquals(typicalProfileWithPicture.getModifiedDate(), storedProfile.getModifiedDate()); } - @Test - public void testUpdateOrCreateStudentProfile_withNonEmptyPictureKey_shouldUpdateSuccessfully() throws Exception { - typicalProfileWithoutPicture.pictureKey = uploadDefaultPictureForProfile(typicalProfileWithPicture.googleId); - - StudentProfileAttributes updatedSpa = profilesDb.updateOrCreateStudentProfile( - StudentProfileAttributes.updateOptionsBuilder(typicalProfileWithoutPicture.googleId) - .withPictureKey(typicalProfileWithoutPicture.pictureKey) - .build()); - - verifyPresentInDatastore(typicalProfileWithoutPicture); - assertEquals(typicalProfileWithoutPicture.pictureKey, updatedSpa.pictureKey); - - // tear down - profilesDb.deletePicture(typicalProfileWithoutPicture.pictureKey); - } - @Test public void testDeleteStudentProfile_nonExistentEntity_shouldFailSilently() { profilesDb.deleteStudentProfile("test.non-existent"); @@ -280,30 +235,6 @@ public void testDeleteStudentProfile_profileWithPicture_shouldDeleteCorrectly() // check that profile get deleted and picture get deleted verifyAbsentInDatastore(typicalProfileWithPicture); - assertFalse(doesFileExistInGcs(typicalProfileWithPicture.pictureKey)); - } - - @Test - public void testDeletePicture_unknownBlobKey_shouldFailSilently() { - profilesDb.deletePicture("unknown"); - - assertFalse(doesFileExistInGcs("unknown")); } - @Test - public void testDeletePicture_typicalBlobKey_shouldDeleteSuccessfully() { - profilesDb.deletePicture(typicalPictureKey); - - assertFalse(doesFileExistInGcs(typicalPictureKey)); - } - - //------------------------------------------------------------------------------------------------------- - //-------------------------------------- Helper Functions ----------------------------------------------- - //------------------------------------------------------------------------------------------------------- - - private String uploadDefaultPictureForProfile(String googleId) - throws IOException { - // we upload a small text file as the actual file does not matter here - return writeFileToGcs(googleId, "src/test/resources/images/not_a_picture.txt"); - } } diff --git a/src/test/java/teammates/test/BaseComponentTestCase.java b/src/test/java/teammates/test/BaseComponentTestCase.java index 2d76785c7a6..894bcf523c9 100644 --- a/src/test/java/teammates/test/BaseComponentTestCase.java +++ b/src/test/java/teammates/test/BaseComponentTestCase.java @@ -20,7 +20,6 @@ import teammates.common.datatransfer.attributes.StudentAttributes; import teammates.common.datatransfer.attributes.StudentProfileAttributes; import teammates.common.exception.TeammatesException; -import teammates.common.util.GoogleCloudStorageHelper; import teammates.common.util.retry.RetryManager; import teammates.logic.api.LogicExtension; @@ -33,6 +32,7 @@ public class BaseComponentTestCase extends BaseTestCaseWithDatastoreAccess { protected static final GaeSimulation gaeSimulation = GaeSimulation.inst(); protected static final LogicExtension logic = new LogicExtension(); + private static final MockFileStorage MOCK_FILE_STORAGE = new MockFileStorage(); @Override @BeforeClass @@ -51,14 +51,18 @@ protected RetryManager getPersistenceRetryManager() { return new RetryManager(TestProperties.PERSISTENCE_RETRY_PERIOD_IN_S / 2); } - protected static String writeFileToGcs(String googleId, String filename) throws IOException { - byte[] image = FileHelper.readFileAsBytes(filename); - String contentType = URLConnection.guessContentTypeFromName(filename); - return GoogleCloudStorageHelper.writeImageDataToGcs(googleId, image, contentType); + protected static void writeFileToStorage(String targetFileName, String sourceFilePath) throws IOException { + byte[] bytes = FileHelper.readFileAsBytes(sourceFilePath); + String contentType = URLConnection.guessContentTypeFromName(sourceFilePath); + MOCK_FILE_STORAGE.create(targetFileName, bytes, contentType); } - protected static boolean doesFileExistInGcs(String fileKey) { - return GoogleCloudStorageHelper.doesFileExistInGcs(fileKey); + protected static void deleteFile(String fileName) { + MOCK_FILE_STORAGE.delete(fileName); + } + + protected static boolean doesFileExist(String fileName) { + return MOCK_FILE_STORAGE.doesFileExist(fileName); } @Override diff --git a/src/test/java/teammates/test/BaseTestCase.java b/src/test/java/teammates/test/BaseTestCase.java index 4ed4fc039df..1b111c0d695 100644 --- a/src/test/java/teammates/test/BaseTestCase.java +++ b/src/test/java/teammates/test/BaseTestCase.java @@ -145,6 +145,10 @@ protected static void assertEquals(String message, Object expected, Object actua Assert.assertEquals(message, expected, actual); } + protected static void assertArrayEquals(byte[] expected, byte[] actual) { + Assert.assertArrayEquals(expected, actual); + } + protected static void assertNotEquals(Object first, Object second) { Assert.assertNotEquals(first, second); } diff --git a/src/test/java/teammates/test/EmailChecker.java b/src/test/java/teammates/test/EmailChecker.java index 4a2b8064630..1df51f10d09 100644 --- a/src/test/java/teammates/test/EmailChecker.java +++ b/src/test/java/teammates/test/EmailChecker.java @@ -63,7 +63,7 @@ private static String injectTestProperties(String emailContent) { } private static String getAppUrl() { - return Config.isDevServer() ? Config.APP_FRONTENDDEV_URL : Config.APP_URL; + return Config.getFrontEndAppUrl("").toAbsoluteString(); } /** diff --git a/src/test/java/teammates/test/EmailCheckerTest.java b/src/test/java/teammates/test/EmailCheckerTest.java index 2ef9237989a..0a2ddf38f87 100644 --- a/src/test/java/teammates/test/EmailCheckerTest.java +++ b/src/test/java/teammates/test/EmailCheckerTest.java @@ -26,7 +26,7 @@ private String injectContextDependentValuesForTest(String emailContent) { } private static String getAppUrl() { - return Config.isDevServer() ? Config.APP_FRONTENDDEV_URL : Config.APP_URL; + return Config.getFrontEndAppUrl("").toAbsoluteString(); } } diff --git a/src/test/java/teammates/test/FileHelper.java b/src/test/java/teammates/test/FileHelper.java index b3512832b76..8db38d29db6 100644 --- a/src/test/java/teammates/test/FileHelper.java +++ b/src/test/java/teammates/test/FileHelper.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Scanner; @@ -44,7 +45,15 @@ public static void saveFile(String filePath, String content) throws IOException try (BufferedWriter fw = Files.newBufferedWriter(Paths.get(filePath))) { fw.write(content); } + } + /** + * Saves the supplied content to the specified file path. + */ + public static void saveFile(String filePath, byte[] content) throws IOException { + try (OutputStream os = Files.newOutputStream(Paths.get(filePath))) { + os.write(content); + } } /** diff --git a/src/test/java/teammates/test/GaeSimulation.java b/src/test/java/teammates/test/GaeSimulation.java index de18712cb22..d0a4afebf4c 100644 --- a/src/test/java/teammates/test/GaeSimulation.java +++ b/src/test/java/teammates/test/GaeSimulation.java @@ -138,6 +138,7 @@ public Action getActionObject(String uri, String method, String body, Map invalidProfilePicAction.execute()); + + deleteFile(student1.googleId); } @Override diff --git a/src/test/resources/data/FeedbackSessionResultsBundleTest.json b/src/test/resources/data/FeedbackSessionResultsBundleTest.json index d3827e3576b..f0345de2e9d 100644 --- a/src/test/resources/data/FeedbackSessionResultsBundleTest.json +++ b/src/test/resources/data/FeedbackSessionResultsBundleTest.json @@ -282,8 +282,7 @@ "institute": "TEAMMATES Test Institute 1", "nationality": "American", "gender": "MALE", - "moreInfo": "I am just a student :P", - "pictureKey": "asdf34&hfn3!@" + "moreInfo": "I am just a student :P" } } } diff --git a/src/test/resources/data/FeedbackSessionsLogicTest.json b/src/test/resources/data/FeedbackSessionsLogicTest.json index bb916296268..8293cadf0fb 100644 --- a/src/test/resources/data/FeedbackSessionsLogicTest.json +++ b/src/test/resources/data/FeedbackSessionsLogicTest.json @@ -1403,8 +1403,7 @@ "institute": "TEAMMATES Test Institute 3", "nationality": "American", "gender": "MALE", - "moreInfo": "I am just a student :P", - "pictureKey": "asdf34&hfn3!@" + "moreInfo": "I am just a student :P" } } } diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index 60551c48a10..e432c41fc23 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1685,8 +1685,7 @@ "institute": "TEAMMATES Test Institute 3", "nationality": "American", "gender": "MALE", - "moreInfo": "I am just a student :P", - "pictureKey": "asdf34&hfn3!@" + "moreInfo": "I am just a student :P" }, "student1InTestingSanitizationCourse": { "googleId": "student1InTestingSanitizationCourse", @@ -1695,8 +1694,7 @@ "institute": "inst", "nationality": "American", "gender": "OTHER", - "moreInfo": "I am just a student :P", - "pictureKey": "" + "moreInfo": "I am just a student :P" } } } diff --git a/src/test/resources/filestorage/.gitkeep b/src/test/resources/filestorage/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/test/resources/images/profile_pic_default.png b/src/test/resources/images/profile_pic_default.png deleted file mode 100644 index 818b23023c7e88d3ea56b7024a934a1340950639..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12494 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 W#E_KmGQ+;E0Lt { }); it('should snap with a student field without information', () => { - const studentDetails: any = { - studentProfile: { - shortName: '', - email: '', - institute: '', - nationality: '', - gender: Gender, - moreInfo: '', - pictureKey: '', - }, + const studentDetails: StudentProfile = { name: '', - requestId: '', + shortName: '', + email: '', + institute: '', + nationality: '', + gender: Gender.MALE, + moreInfo: '', }; component.student = studentDetails; component.editForm = new FormGroup({ @@ -77,18 +73,14 @@ describe('StudentProfilePageComponent', () => { }); it('should snap with values and a profile photo', () => { - const studentDetails: any = { - studentProfile: { - shortName: 'Ash', - email: 'ayush@nus.com', - institute: 'NUS', - nationality: 'Indian', - gender: Gender.MALE, - moreInfo: 'I like to party', - pictureKey: 'photo.jpg', - }, + const studentDetails: StudentProfile = { name: 'Ayush', - requestId: '16', + shortName: 'Ash', + email: 'ayush@nus.com', + institute: 'NUS', + nationality: 'Indian', + gender: Gender.MALE, + moreInfo: 'I like to party', }; component.student = studentDetails; component.profilePicLink = `${environment.backendUrl}/webapi/students/` + diff --git a/src/web/app/pages-student/student-profile-page/student-profile-page.component.ts b/src/web/app/pages-student/student-profile-page/student-profile-page.component.ts index f88336bc460..26a47ef3fb8 100644 --- a/src/web/app/pages-student/student-profile-page/student-profile-page.component.ts +++ b/src/web/app/pages-student/student-profile-page/student-profile-page.component.ts @@ -202,7 +202,7 @@ export class StudentProfilePageComponent implements OnInit { } /** - * Deletes the profile picture and the profile picture key + * Deletes the profile picture. */ deleteProfilePicture(): void { const paramMap: Record = { diff --git a/src/web/services/student-profile.service.ts b/src/web/services/student-profile.service.ts index d37c19746db..3af8f96c0de 100644 --- a/src/web/services/student-profile.service.ts +++ b/src/web/services/student-profile.service.ts @@ -57,7 +57,7 @@ export class StudentProfileService { } /** - * Deletes the profile picture and the profile picture key + * Deletes the profile picture. */ deleteProfilePicture(paramMap: Record): Observable { return this.httpRequestService.delete(ResourceEndpoints.STUDENT_PROFILE_PICTURE, paramMap);