Skip to content

Commit 56d7c65

Browse files
authored
[#10944] Migrate Blobstore API to Google Cloud Storage (#10945)
1 parent 3c3a3b6 commit 56d7c65

File tree

53 files changed

+360
-534
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+360
-534
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,7 @@ src/client/java/teammates/client/scripts/statistics/data/
6060
src/client/java/teammates/client/scripts/log/
6161
src/e2e/resources/gmail-api/
6262
src/e2e/resources/downloads/
63+
filestorage-dev/*
64+
src/test/resources/filestorage/**/*
6365

6466
!.gitkeep

build.gradle

+3-16
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,7 @@ dependencies {
5656
implementation("com.google.appengine:appengine-api-1.0-sdk:${appengineVersion}")
5757
implementation("com.google.cloud:google-cloud-tasks:1.30.11")
5858
implementation("com.google.cloud:google-cloud-logging:2.1.2")
59-
60-
// This dependency needs to be resolved individually
61-
// because the main dependency (appengine-gcs-client) does not have its transitive dependencies up-to-date
62-
implementation("com.google.appengine.tools:appengine-gcs-client:0.8.1") {
63-
// Use the newer servlet library instead
64-
exclude group: "javax.servlet", module: "servlet-api"
65-
}
66-
implementation("com.google.api-client:google-api-client-appengine:1.30.9") {
67-
// Use the newer servlet library instead
68-
exclude group: "javax.servlet", module: "servlet-api"
69-
}
70-
implementation("com.google.apis:google-api-services-storage:v1-rev20200430-1.30.9")
71-
59+
implementation("com.google.cloud:google-cloud-storage:1.113.9")
7260
implementation("com.google.code.gson:gson:2.8.6")
7361
implementation("com.google.guava:guava:30.1-jre")
7462
implementation(objectify)
@@ -280,9 +268,8 @@ appengine {
280268
port = 8080
281269
jvmFlags = ["-Xss2m", "-Dfile.encoding=UTF-8",
282270
// Absolute paths are not supported, the following is relative to the project directory
283-
// These only specify the datastore/blobstore paths, but search indexes are still generated in WEB-INF/appengine-generated
284-
"-Ddatastore.backing_store=../../appengine-generated/local_db.bin",
285-
"-Dblobstore.backing_store=../../appengine-generated"]
271+
// These only specify the datastore paths, but search indexes are still generated in WEB-INF/appengine-generated
272+
"-Ddatastore.backing_store=../../appengine-generated/local_db.bin"]
286273
automaticRestart = true
287274
}
288275
deploy {

filestorage-dev/.gitkeep

Whitespace-only changes.

src/client/java/teammates/client/scripts/GoogleIdMigrationBaseScript.java

+7-18
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,15 @@
33
import java.util.List;
44
import java.util.stream.Collectors;
55

6-
import com.google.appengine.tools.cloudstorage.GcsFilename;
7-
import com.google.appengine.tools.cloudstorage.GcsService;
8-
import com.google.appengine.tools.cloudstorage.GcsServiceFactory;
9-
import com.google.appengine.tools.cloudstorage.RetryParams;
6+
import com.google.cloud.storage.Blob;
7+
import com.google.cloud.storage.Storage;
8+
import com.google.cloud.storage.StorageOptions;
109
import com.googlecode.objectify.Key;
1110
import com.googlecode.objectify.cmd.Query;
1211

1312
import teammates.client.util.ClientProperties;
1413
import teammates.common.datatransfer.attributes.InstructorAttributes;
1514
import teammates.common.util.Config;
16-
import teammates.common.util.GoogleCloudStorageHelper;
1715
import teammates.storage.api.InstructorsDb;
1816
import teammates.storage.entity.Account;
1917
import teammates.storage.entity.CourseStudent;
@@ -100,23 +98,14 @@ protected void migrateEntity(Account oldAccount) throws Exception {
10098
ofy().delete().type(Account.class).id(oldGoogleId).now();
10199

102100
if (oldStudentProfile != null) {
103-
String pictureKey = oldStudentProfile.getPictureKey();
104-
105101
if (!ClientProperties.isTargetUrlDevServer()) {
106-
try {
107-
GcsFilename oldGcsFilename = new GcsFilename(Config.PRODUCTION_GCS_BUCKETNAME, oldGoogleId);
108-
GcsFilename newGcsFilename = new GcsFilename(Config.PRODUCTION_GCS_BUCKETNAME, newGoogleId);
109-
GcsService gcsService = GcsServiceFactory.createGcsService(RetryParams.getDefaultInstance());
110-
gcsService.copy(oldGcsFilename, newGcsFilename);
111-
gcsService.delete(oldGcsFilename);
112-
pictureKey = GoogleCloudStorageHelper.createBlobKey(newGoogleId);
113-
} catch (Exception e) {
114-
log("Profile picture not exist or error during copy: " + e.getMessage());
115-
}
102+
Storage storage = StorageOptions.newBuilder().setProjectId(Config.APP_ID).build().getService();
103+
Blob blob = storage.get(Config.PRODUCTION_GCS_BUCKETNAME, oldGoogleId);
104+
blob.copyTo(Config.PRODUCTION_GCS_BUCKETNAME, newGoogleId);
105+
blob.delete();
116106
}
117107

118108
oldStudentProfile.setGoogleId(newGoogleId);
119-
oldStudentProfile.setPictureKey(pictureKey);
120109
ofy().save().entity(oldStudentProfile).now();
121110
ofy().delete().key(oldStudentProfileKey).now();
122111
}

src/e2e/resources/data/InstructorCourseStudentDetailsPageE2ETest.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@
7272
"institute": "TEAMMATES Test Institute 7",
7373
"nationality": "Laotian",
7474
"gender": "MALE",
75-
"moreInfo": "This is a lot of info...",
76-
"pictureKey": ""
75+
"moreInfo": "This is a lot of info..."
7776
}
7877
}
7978
}

src/e2e/resources/data/InstructorStudentRecordsPageE2ETest.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@
7070
"institute": "TEAMMATES Test Institute 7",
7171
"nationality": "Singaporean",
7272
"gender": "MALE",
73-
"moreInfo": "This is a lot of info!",
74-
"pictureKey": ""
73+
"moreInfo": "This is a lot of info!"
7574
}
7675
}
7776
}

src/e2e/resources/data/StudentCourseDetailsPageE2ETest.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,7 @@
105105
"institute": "inst",
106106
"nationality": "American",
107107
"gender": "OTHER",
108-
"moreInfo": "I am just a student :P",
109-
"pictureKey": ""
108+
"moreInfo": "I am just a student :P"
110109
},
111110
"SCDet.charlie": {
112111
"googleId": "tm.e2e.SCDet.charlie",
@@ -115,8 +114,7 @@
115114
"institute": "inst",
116115
"nationality": "Singaporean",
117116
"gender": "OTHER",
118-
"moreInfo": "I am also a student :P",
119-
"pictureKey": ""
117+
"moreInfo": "I am also a student :P"
120118
}
121119
}
122120
}

src/e2e/resources/data/StudentProfilePageE2ETest.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@
3939
"institute": "TEAMMATES Test Institute 4",
4040
"nationality": "Singaporean",
4141
"gender": "MALE",
42-
"moreInfo": "I am just another student :P",
43-
"pictureKey": ""
42+
"moreInfo": "I am just another student :P"
4443
}
4544
}
4645
}

src/lnp/java/teammates/lnp/cases/StudentProfileLNPTest.java

-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@ protected Map<String, StudentProfileAttributes> generateProfiles() {
120120
.withShortName(String.valueOf(i))
121121
.withInstitute("TEAMMATES Test Institute 222")
122122
.withMoreInfo("I am " + i)
123-
.withPictureKey("")
124123
.withGender(StudentProfileAttributes.Gender.MALE)
125124
.withNationality("American")
126125
.build()

src/main/java/teammates/common/datatransfer/attributes/StudentProfileAttributes.java

+2-24
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ public class StudentProfileAttributes extends EntityAttributes<StudentProfile> {
2525
public String nationality;
2626
public Gender gender;
2727
public String moreInfo;
28-
public String pictureKey;
2928
public Instant modifiedDate;
3029

3130
private StudentProfileAttributes(String googleId) {
@@ -36,7 +35,6 @@ private StudentProfileAttributes(String googleId) {
3635
this.nationality = "";
3736
this.gender = Gender.OTHER;
3837
this.moreInfo = "";
39-
this.pictureKey = "";
4038
this.modifiedDate = Instant.now();
4139
}
4240

@@ -59,9 +57,6 @@ public static StudentProfileAttributes valueOf(StudentProfile sp) {
5957
if (sp.getMoreInfo() != null) {
6058
studentProfileAttributes.moreInfo = sp.getMoreInfo();
6159
}
62-
if (sp.getPictureKey() != null) {
63-
studentProfileAttributes.pictureKey = sp.getPictureKey();
64-
}
6560
if (sp.getModifiedDate() != null) {
6661
studentProfileAttributes.modifiedDate = sp.getModifiedDate();
6762
}
@@ -85,7 +80,6 @@ public StudentProfileAttributes getCopy() {
8580
studentProfileAttributes.gender = gender;
8681
studentProfileAttributes.nationality = nationality;
8782
studentProfileAttributes.moreInfo = moreInfo;
88-
studentProfileAttributes.pictureKey = pictureKey;
8983
studentProfileAttributes.modifiedDate = modifiedDate;
9084

9185
return studentProfileAttributes;
@@ -119,10 +113,6 @@ public String getMoreInfo() {
119113
return moreInfo;
120114
}
121115

122-
public String getPictureKey() {
123-
return pictureKey;
124-
}
125-
126116
public Instant getModifiedDate() {
127117
return modifiedDate;
128118
}
@@ -153,8 +143,6 @@ public List<String> getInvalidityInfo() {
153143

154144
Assumption.assertNotNull(gender);
155145

156-
Assumption.assertNotNull(this.pictureKey);
157-
158146
// No validation for modified date as it is determined by the system.
159147
// No validation for More Info. It will properly sanitized.
160148

@@ -170,7 +158,7 @@ public String toString() {
170158
public int hashCode() {
171159
StringBuilder stringBuilder = new StringBuilder();
172160
stringBuilder.append(this.email).append(this.shortName).append(this.institute)
173-
.append(this.googleId).append(this.pictureKey).append(this.gender.toString());
161+
.append(this.googleId).append(this.gender.toString());
174162
return stringBuilder.toString().hashCode();
175163
}
176164

@@ -186,7 +174,6 @@ public boolean equals(Object other) {
186174
&& Objects.equals(this.shortName, otherProfile.shortName)
187175
&& Objects.equals(this.institute, otherProfile.institute)
188176
&& Objects.equals(this.googleId, otherProfile.googleId)
189-
&& Objects.equals(this.pictureKey, otherProfile.pictureKey)
190177
&& Objects.equals(this.gender, otherProfile.gender);
191178
} else {
192179
return false;
@@ -196,7 +183,7 @@ public boolean equals(Object other) {
196183
@Override
197184
public StudentProfile toEntity() {
198185
return new StudentProfile(googleId, shortName, email, institute, nationality, gender.name().toLowerCase(),
199-
moreInfo, this.pictureKey);
186+
moreInfo);
200187
}
201188

202189
@Override
@@ -214,7 +201,6 @@ public void update(UpdateOptions updateOptions) {
214201
updateOptions.nationalityOption.ifPresent(s -> nationality = s);
215202
updateOptions.genderOption.ifPresent(s -> gender = s);
216203
updateOptions.moreInfoOption.ifPresent(s -> moreInfo = s);
217-
updateOptions.pictureKeyOption.ifPresent(s -> pictureKey = s);
218204
}
219205

220206
/**
@@ -280,7 +266,6 @@ public static class UpdateOptions {
280266
private UpdateOption<String> nationalityOption = UpdateOption.empty();
281267
private UpdateOption<Gender> genderOption = UpdateOption.empty();
282268
private UpdateOption<String> moreInfoOption = UpdateOption.empty();
283-
private UpdateOption<String> pictureKeyOption = UpdateOption.empty();
284269

285270
private UpdateOptions(String googleId) {
286271
Assumption.assertNotNull(googleId);
@@ -380,13 +365,6 @@ public B withMoreInfo(String moreInfo) {
380365
return thisBuilder;
381366
}
382367

383-
public B withPictureKey(String pictureKey) {
384-
Assumption.assertNotNull(pictureKey);
385-
386-
updateOptions.pictureKeyOption = UpdateOption.of(pictureKey);
387-
return thisBuilder;
388-
}
389-
390368
public abstract T build();
391369

392370
}

src/main/java/teammates/common/util/Config.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
*/
1616
public final class Config {
1717

18-
/** The value of the application URL, or null if no server instance is running. */
19-
public static final String APP_URL;
20-
2118
/** The value of the "app.id" in build.properties file. */
2219
public static final String APP_ID;
2320

@@ -88,7 +85,6 @@ public final class Config {
8885
public static final boolean MAINTENANCE;
8986

9087
static {
91-
APP_URL = readAppUrl();
9288
Properties properties = new Properties();
9389
try (InputStream buildPropStream = FileHelper.getResourceAsStream("build.properties")) {
9490
properties.load(buildPropStream);
@@ -124,7 +120,7 @@ private Config() {
124120
// access static fields directly
125121
}
126122

127-
private static String readAppUrl() {
123+
static String getBaseAppUrl() {
128124
ApiProxy.Environment serverEnvironment = ApiProxy.getCurrentEnvironment();
129125
if (serverEnvironment == null) {
130126
return null;
@@ -179,7 +175,7 @@ public static AppUrl getFrontEndAppUrl(String relativeUrl) {
179175
* {@code relativeUrl} must start with a "/".
180176
*/
181177
private static AppUrl getBackEndAppUrl(String relativeUrl) {
182-
return new AppUrl(APP_URL + relativeUrl);
178+
return new AppUrl(getBaseAppUrl() + relativeUrl);
183179
}
184180

185181
public static boolean isUsingSendgrid() {

src/main/java/teammates/common/util/GoogleCloudStorageHelper.java

-88
This file was deleted.

0 commit comments

Comments
 (0)