From d550da41a17f9b65a76eed04f75d89c92895b763 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Fri, 27 Aug 2021 21:05:41 +0800 Subject: [PATCH 01/79] Defer creation of sample data until after instructor joins --- .../attributes/AccountRequestAttributes.java | 279 ++++++++++++++++++ .../java/teammates/common/util/Const.java | 2 + src/main/java/teammates/logic/api/Logic.java | 45 +++ .../logic/core/AccountRequestsLogic.java | 61 ++++ .../storage/api/AccountRequestsDb.java | 119 ++++++++ .../java/teammates/storage/api/OfyHelper.java | 2 + .../storage/entity/AccountRequest.java | 100 +++++++ .../ui/constants/ResourceEndpoints.java | 1 + .../teammates/ui/webapi/ActionFactory.java | 1 + .../ui/webapi/CreateAccountAction.java | 70 +++-- .../ui/webapi/CreateAccountRequestAction.java | 65 ++++ src/web/app/app.module.ts | 5 + .../admin-home-page.component.ts | 2 +- .../user-create-account-page.component.html | 34 +++ .../user-create-account-page.component.scss | 0 ...user-create-account-page.component.spec.ts | 0 .../app/user-create-account-page.component.ts | 69 +++++ .../app/user-create-account-page.module.ts | 28 ++ src/web/services/account.service.ts | 14 +- 19 files changed, 873 insertions(+), 24 deletions(-) create mode 100644 src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java create mode 100644 src/main/java/teammates/logic/core/AccountRequestsLogic.java create mode 100644 src/main/java/teammates/storage/api/AccountRequestsDb.java create mode 100644 src/main/java/teammates/storage/entity/AccountRequest.java create mode 100644 src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java create mode 100644 src/web/app/user-create-account-page.component.html create mode 100644 src/web/app/user-create-account-page.component.scss create mode 100644 src/web/app/user-create-account-page.component.spec.ts create mode 100644 src/web/app/user-create-account-page.component.ts create mode 100644 src/web/app/user-create-account-page.module.ts diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java new file mode 100644 index 00000000000..4a6a742d22e --- /dev/null +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -0,0 +1,279 @@ +package teammates.common.datatransfer.attributes; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import teammates.common.util.FieldValidator; +import teammates.common.util.SanitizationHelper; +import teammates.storage.entity.AccountRequest; + +/** + * The data transfer object for {@link AccountRequest} entities. + */ +public class AccountRequestAttributes extends EntityAttributes { + + private String email; + private String name; + private String institute; + private transient String registrationKey; + private transient Instant createdAt; + private transient Instant deletedAt; + + private AccountRequestAttributes(String email) { + this.email = email; + this.registrationKey = null; + this.institute = null; + this.name = null; + this.createdAt = Instant.now(); + this.deletedAt = null; + } + + /** + * Gets the {@link AccountRequestAttributes} instance of the given {@link AccountRequest}. + */ + public static AccountRequestAttributes valueOf(AccountRequest accountRequest) { + AccountRequestAttributes accountRequestAttributes = new AccountRequestAttributes(accountRequest.getEmail()); + + accountRequestAttributes.registrationKey = accountRequest.getRegistrationKey(); + accountRequestAttributes.name = accountRequest.getName(); + accountRequestAttributes.institute = accountRequest.getInstitute(); + + if (accountRequest.getCreatedAt() != null) { + accountRequestAttributes.createdAt = accountRequest.getCreatedAt(); + } + accountRequestAttributes.deletedAt = accountRequest.getDeletedAt(); + + return accountRequestAttributes; + } + + /** + * Returns a builder for {@link AccountRequestAttributes}. + */ + public static Builder builder(String email) { + return new Builder(email); + } + + public String getRegistrationKey() { + return registrationKey; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getInstitute() { + return institute; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getDeletedAt() { + return deletedAt; + } + + public void setDeletedAt(Instant deletedAt) { + this.deletedAt = deletedAt; + } + + public boolean isAccountRequestDeleted() { + return this.deletedAt != null; + } + + @Override + public List getInvalidityInfo() { + + List errors = new ArrayList<>(); + + addNonEmptyError(FieldValidator.getInvalidityInfoForEmail(getEmail()), errors); + addNonEmptyError(FieldValidator.getInvalidityInfoForPersonName(getName()), errors); + addNonEmptyError(FieldValidator.getInvalidityInfoForInstituteName(getInstitute()), errors); + + return errors; + } + + @Override + public AccountRequest toEntity() { + return new AccountRequest(getEmail(), getRegistrationKey(), getName(), getInstitute(), getCreatedAt(), getDeletedAt()); + } + + @Override + public String toString() { + return "[" + AccountRequestAttributes.class.getSimpleName() + "] email: " + getEmail() + + "registrationKey: " + getRegistrationKey() + + " name: " + getName() + " institute: " + getInstitute(); + } + + @Override + public int hashCode() { + return (this.email + this.registrationKey + this.name + this.institute).hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } else if (this == other) { + return true; + } else if (this.getClass() == other.getClass()) { + AccountRequestAttributes otherCourse = (AccountRequestAttributes) other; + return Objects.equals(this.registrationKey, otherCourse.registrationKey) + && Objects.equals(this.email, otherCourse.email) + && Objects.equals(this.institute, otherCourse.institute) + && Objects.equals(this.name, otherCourse.name); + } else { + return false; + } + } + + @Override + public void sanitizeForSaving() { + this.institute = SanitizationHelper.sanitizeTitle(institute); + } + + /** + * Updates with {@link UpdateOptions}. + */ + public void update(UpdateOptions updateOptions) { + updateOptions.nameOption.ifPresent(s -> name = s); + updateOptions.registrationKeyOption.ifPresent(s -> registrationKey = s); + updateOptions.instituteOption.ifPresent(s -> institute = s); + } + + /** + * Returns a {@link UpdateOptions.Builder} to build {@link UpdateOptions} for a accountRequest. + */ + public static UpdateOptions.Builder updateOptionsBuilder(String email) { + return new UpdateOptions.Builder(email); + } + + /** + * A builder for {@link AccountRequestAttributes}. + */ + public static class Builder extends BasicBuilder { + + private final AccountRequestAttributes accountRequestAttributes; + + private Builder(String email) { + super(new UpdateOptions(email)); + thisBuilder = this; + + accountRequestAttributes = new AccountRequestAttributes(email); + } + + @Override + public AccountRequestAttributes build() { + accountRequestAttributes.update(updateOptions); + + return accountRequestAttributes; + } + } + + /** + * Helper class to specific the fields to update in {@link AccountAttributes}. + */ + public static class UpdateOptions { + private String email; + + private UpdateOption nameOption = UpdateOption.empty(); + private UpdateOption registrationKeyOption = UpdateOption.empty(); + private UpdateOption instituteOption = UpdateOption.empty(); + + private UpdateOptions(String email) { + assert email != null; + + this.email = email; + } + + public String getEmail() { + return email; + } + + @Override + public String toString() { + return "AccountRequestAttributes.UpdateOptions [" + + "email = " + email + + ", name = " + nameOption + + ", registrationKey = " + registrationKeyOption + + ", institute = " + instituteOption + + "]"; + } + + /** + * Builder class to build {@link UpdateOptions}. + */ + public static class Builder extends BasicBuilder { + + private Builder(String email) { + super(new UpdateOptions(email)); + thisBuilder = this; + } + + @Override + public UpdateOptions build() { + return updateOptions; + } + + } + + } + + /** + * Basic builder to build {@link AccountRequestAttributes} related classes. + * + * @param type to be built + * @param type of the builder + */ + private abstract static class BasicBuilder> { + + UpdateOptions updateOptions; + B thisBuilder; + + BasicBuilder(UpdateOptions updateOptions) { + this.updateOptions = updateOptions; + } + + public B withName(String name) { + assert name != null; + + updateOptions.nameOption = UpdateOption.of(name); + return thisBuilder; + } + + public B withRegistrationKey(String registrationKey) { + assert registrationKey != null; + + updateOptions.registrationKeyOption = UpdateOption.of(registrationKey); + return thisBuilder; + } + + public B withInstitute(String institute) { + assert institute != null; + + updateOptions.instituteOption = UpdateOption.of(institute); + return thisBuilder; + } + + public abstract T build(); + + } +} diff --git a/src/main/java/teammates/common/util/Const.java b/src/main/java/teammates/common/util/Const.java index 8c9ccabbae9..1ea03a53ea2 100644 --- a/src/main/java/teammates/common/util/Const.java +++ b/src/main/java/teammates/common/util/Const.java @@ -245,6 +245,7 @@ public static class WebPageURIs { private static final String MAINTAINER_PAGE = URI_PREFIX + "/" + EntityType.MAINTAINER; private static final String FRONT_PAGE = URI_PREFIX + "/front"; public static final String JOIN_PAGE = URI_PREFIX + "/join"; + public static final String CREATE_ACCOUNT_PAGE = URI_PREFIX + "/createaccount"; public static final String ADMIN_HOME_PAGE = ADMIN_PAGE + "/home"; public static final String ADMIN_ACCOUNTS_PAGE = ADMIN_PAGE + "/accounts"; @@ -297,6 +298,7 @@ public static class ResourceURIs { public static final String ACCOUNT = URI_PREFIX + "/account"; public static final String ACCOUNT_RESET = URI_PREFIX + "/account/reset"; public static final String ACCOUNT_DOWNGRADE = URI_PREFIX + "/account/downgrade"; + public static final String ACCOUNT_REQUEST = URI_PREFIX + "/account/request"; public static final String RESPONSE_COMMENT = URI_PREFIX + "/responsecomment"; public static final String COURSE = URI_PREFIX + "/course"; public static final String COURSE_ARCHIVE = URI_PREFIX + "/course/archive"; diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 6174a8c6b5c..a4a302ecdf4 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -10,6 +10,7 @@ import teammates.common.datatransfer.DataBundle; import teammates.common.datatransfer.SessionResultsBundle; import teammates.common.datatransfer.attributes.AccountAttributes; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; import teammates.common.datatransfer.attributes.FeedbackResponseAttributes; @@ -24,6 +25,7 @@ import teammates.common.exception.InstructorUpdateException; import teammates.common.exception.InvalidParametersException; import teammates.common.exception.SearchServiceException; +import teammates.logic.core.AccountRequestsLogic; import teammates.logic.core.AccountsLogic; import teammates.logic.core.CoursesLogic; import teammates.logic.core.DataBundleLogic; @@ -45,6 +47,7 @@ public class Logic { private static final Logic instance = new Logic(); final AccountsLogic accountsLogic = AccountsLogic.inst(); + final AccountRequestsLogic accountRequestsLogic = AccountRequestsLogic.inst(); final StudentsLogic studentsLogic = StudentsLogic.inst(); final InstructorsLogic instructorsLogic = InstructorsLogic.inst(); final CoursesLogic coursesLogic = CoursesLogic.inst(); @@ -1372,4 +1375,46 @@ public boolean isStudentsInSameTeam(String courseId, String student1Email, Strin assert student2Email != null; return studentsLogic.isStudentsInSameTeam(courseId, student1Email, student2Email); } + + /** + * Creates or updates an account request. + * + *

Preconditions:

+ * * All parameters are non-null. + * + * @return the created/updated account request + * @throws InvalidParametersException if the account request is not valid + */ + public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) + throws InvalidParametersException { + assert accountRequestToAdd != null; + + return accountRequestsLogic.createOrUpdateAccountRequest(accountRequestToAdd); + } + + /** + * Deletes an account request. + * + *

Preconditions:

+ * * All parameters are non-null. + */ + public void deleteAccountRequest(String registrationKey) { + assert registrationKey != null; + + accountRequestsLogic.deleteAccountRequest(registrationKey); + } + + /** + * Gets an account request by unique constraint registrationKey. + * + *

Preconditions:

+ * * All parameters are non-null. + * + * @return the account request or null if no match found + */ + public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { + assert registrationKey != null; + + return accountRequestsLogic.getAccountRequestForRegistrationKey(registrationKey); + } } diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java new file mode 100644 index 00000000000..0a1ebbcde5d --- /dev/null +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -0,0 +1,61 @@ +package teammates.logic.core; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.InvalidParametersException; +import teammates.storage.api.AccountRequestsDb; + +/** + * Handles the logic related to account requests. + */ +public final class AccountRequestsLogic { + + private static final AccountRequestsLogic instance = new AccountRequestsLogic(); + + private final AccountRequestsDb accountRequestsDb = AccountRequestsDb.inst(); + + private AccountRequestsLogic() { + // prevent initialization + } + + public static AccountRequestsLogic inst() { + return instance; + } + + void initLogicDependencies() { + // No dependency to other logic class + } + + /** + * Gets account request associated with the {@code registrationKey}. + * + * @return null if no match found. + */ + + /** + * Creates or updates an account request. + * + * @return the created/updated account request + * @throws InvalidParametersException if the account request is not valid + */ + public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) + throws InvalidParametersException { + return accountRequestsDb.createOrUpdateAccountRequest(accountRequestToAdd); + } + + /** + * Deletes the account request associated with the {@code registrationKey}. + * + *

Fails silently if the account request doesn't exist.

+ */ + public void deleteAccountRequest(String registrationKey) { + accountRequestsDb.deleteAccountRequest(registrationKey); + } + + /** + * Gets an account request by unique constraint registrationKey. + */ + public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { + return accountRequestsDb.getAccountRequestForRegistrationKey(registrationKey); + } + +} diff --git a/src/main/java/teammates/storage/api/AccountRequestsDb.java b/src/main/java/teammates/storage/api/AccountRequestsDb.java new file mode 100644 index 00000000000..3e616f46380 --- /dev/null +++ b/src/main/java/teammates/storage/api/AccountRequestsDb.java @@ -0,0 +1,119 @@ +package teammates.storage.api; + +import static com.googlecode.objectify.ObjectifyService.ofy; + +import java.security.InvalidParameterException; +import java.util.List; +import java.util.stream.Collectors; + +import com.googlecode.objectify.Key; +import com.googlecode.objectify.cmd.LoadType; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.storage.entity.AccountRequest; + +/** + * Handles CRUD operations for account requests. + * + * @see AccountRequest + * @see AccountRequestAttributes + */ +public final class AccountRequestsDb extends EntitiesDb { + + private static final AccountRequestsDb instance = new AccountRequestsDb(); + + private AccountRequestsDb() { + // prevent initialization + } + + public static AccountRequestsDb inst() { + return instance; + } + + /** + * Gets an account request. + */ + public AccountRequestAttributes getAccountRequest(String email) { + assert email != null; + + return makeAttributesOrNull(getAccountRequestEntity(email)); + } + + /** + * Deletes an accountRequest. + */ + public void deleteAccountRequest(String email) { + assert email != null; + + deleteEntity(Key.create(AccountRequest.class, email)); + } + + @Override + LoadType load() { + return ofy().load().type(AccountRequest.class); + } + + @Override + boolean hasExistingEntities(AccountRequestAttributes entityToCreate) { + Key keyToFind = Key.create(AccountRequest.class, entityToCreate.getEmail()); + return !load().filterKey(keyToFind).keys().list().isEmpty(); + } + + private AccountRequest getAccountRequestEntity(String email) { + return load().id(email).now(); + } + + @Override + AccountRequestAttributes makeAttributes(AccountRequest entity) { + assert entity != null; + + return AccountRequestAttributes.valueOf(entity); + } + + /** + * Creates or updates the account request using {@link AccountRequestAttributes.UpdateOptions}. + * + * @return updated account request + * @throws InvalidParametersException if attributes to update are not valid + */ + public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) + throws InvalidParameterException { + assert accountRequestToAdd != null; + + accountRequestToAdd.sanitizeForSaving(); + AccountRequest accountRequest = accountRequestToAdd.toEntity(); + saveEntity(accountRequest); + + return accountRequestToAdd; + } + + /** + * Gets an account request by unique constraint registrationKey. + */ + public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { + assert registrationKey != null; + + AccountRequestAttributes accountRequest = makeAttributesOrNull( + getAccountRequestEntityForRegistrationKey(registrationKey.trim())); + + return accountRequest; + } + + + private AccountRequest getAccountRequestEntityForRegistrationKey(String key) { + List accountRequestList = load().filter("registrationKey =", key).list(); + + // If registration key detected is not unique, something is wrong + if (accountRequestList.size() > 1) { + log.severe("Duplicate registration keys detected for: " + + accountRequestList.stream().map(i -> i.getEmail()).collect(Collectors.joining(", "))); + } + + if (accountRequestList.isEmpty()) { + return null; + } + + return accountRequestList.get(0); + } + +} diff --git a/src/main/java/teammates/storage/api/OfyHelper.java b/src/main/java/teammates/storage/api/OfyHelper.java index 387683490d8..20abcfe5915 100644 --- a/src/main/java/teammates/storage/api/OfyHelper.java +++ b/src/main/java/teammates/storage/api/OfyHelper.java @@ -9,6 +9,7 @@ import teammates.common.util.Config; import teammates.storage.entity.Account; +import teammates.storage.entity.AccountRequest; import teammates.storage.entity.BaseEntity; import teammates.storage.entity.Course; import teammates.storage.entity.CourseStudent; @@ -45,6 +46,7 @@ public static void registerEntityClasses() { ObjectifyService.register(FeedbackSession.class); ObjectifyService.register(Instructor.class); ObjectifyService.register(StudentProfile.class); + ObjectifyService.register(AccountRequest.class); // enable the ability to use java.time.Instant to issue query ObjectifyService.factory().getTranslators().add(new BaseEntity.InstantTranslatorFactory()); } diff --git a/src/main/java/teammates/storage/entity/AccountRequest.java b/src/main/java/teammates/storage/entity/AccountRequest.java new file mode 100644 index 00000000000..b4b30d0ebbe --- /dev/null +++ b/src/main/java/teammates/storage/entity/AccountRequest.java @@ -0,0 +1,100 @@ +package teammates.storage.entity; + +import java.time.Instant; + +import com.googlecode.objectify.annotation.Entity; +import com.googlecode.objectify.annotation.Id; +import com.googlecode.objectify.annotation.Index; +import com.googlecode.objectify.annotation.Translate; + +/** + * Represents an AccountRequest entity. + */ +@Entity +@Index +public class AccountRequest extends BaseEntity { + + @Id + private String email; + + private String registrationKey; + + private String name; + + private String institute; + + @Translate(InstantTranslatorFactory.class) + private Instant createdAt; + + @Translate(InstantTranslatorFactory.class) + private Instant deletedAt; + + @SuppressWarnings("unused") + private AccountRequest() { + // required by Objectify + } + + public AccountRequest(String email, String registrationKey, String name, String institute, + Instant createdAt, Instant deletedAt) { + this.setEmail(email); + this.setRegistrationKey(registrationKey); + this.setName(name); + this.setInstitute(institute); + + if (createdAt == null) { + this.setCreatedAt(Instant.now()); + } else { + this.setCreatedAt(createdAt); + } + this.setDeletedAt(deletedAt); + } + + public String getRegistrationKey() { + return registrationKey; + } + + public void setRegistrationKey(String registrationKey) { + this.registrationKey = registrationKey.trim(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name.trim(); + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getInstitute() { + return institute; + } + + public void setInstitute(String institute) { + this.institute = institute; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getDeletedAt() { + return deletedAt; + } + + public void setDeletedAt(Instant deletedAt) { + this.deletedAt = deletedAt; + } + +} diff --git a/src/main/java/teammates/ui/constants/ResourceEndpoints.java b/src/main/java/teammates/ui/constants/ResourceEndpoints.java index 258d9f5a464..92befc5bf26 100644 --- a/src/main/java/teammates/ui/constants/ResourceEndpoints.java +++ b/src/main/java/teammates/ui/constants/ResourceEndpoints.java @@ -15,6 +15,7 @@ public enum ResourceEndpoints { ACCOUNT(ResourceURIs.ACCOUNT), ACCOUNT_RESET(ResourceURIs.ACCOUNT_RESET), ACCOUNT_DOWNGRADE(ResourceURIs.ACCOUNT_DOWNGRADE), + ACCOUNT_REQUEST(ResourceURIs.ACCOUNT_REQUEST), RESPONSE_COMMENT(ResourceURIs.RESPONSE_COMMENT), COURSE(ResourceURIs.COURSE), COURSE_ARCHIVE(ResourceURIs.COURSE_ARCHIVE), diff --git a/src/main/java/teammates/ui/webapi/ActionFactory.java b/src/main/java/teammates/ui/webapi/ActionFactory.java index bc2984788db..a89c62c4bd4 100644 --- a/src/main/java/teammates/ui/webapi/ActionFactory.java +++ b/src/main/java/teammates/ui/webapi/ActionFactory.java @@ -47,6 +47,7 @@ public final class ActionFactory { map(ResourceURIs.ACCOUNT, DELETE, DeleteAccountAction.class); map(ResourceURIs.ACCOUNT_DOWNGRADE, PUT, DowngradeAccountAction.class); map(ResourceURIs.ACCOUNT_RESET, PUT, ResetAccountAction.class); + map(ResourceURIs.ACCOUNT_REQUEST, POST, CreateAccountRequestAction.class); map(ResourceURIs.COURSE, GET, GetCourseAction.class); map(ResourceURIs.COURSE, DELETE, DeleteCourseAction.class); map(ResourceURIs.COURSE, POST, CreateCourseAction.class); diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 2a2a000b373..15069d4c433 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -5,31 +5,54 @@ import org.apache.http.HttpStatus; import teammates.common.datatransfer.DataBundle; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.InstructorAttributes; import teammates.common.datatransfer.attributes.StudentAttributes; +import teammates.common.exception.EntityAlreadyExistsException; +import teammates.common.exception.EntityDoesNotExistException; +import teammates.common.exception.InvalidOperationException; import teammates.common.exception.InvalidParametersException; -import teammates.common.util.Config; import teammates.common.util.Const; -import teammates.common.util.EmailWrapper; import teammates.common.util.FieldValidator; import teammates.common.util.JsonUtils; import teammates.common.util.StringHelper; import teammates.common.util.Templates; -import teammates.ui.output.JoinLinkData; -import teammates.ui.request.AccountCreateRequest; /** * Creates a new instructor account with sample courses. */ -class CreateAccountAction extends AdminOnlyAction { +class CreateAccountAction extends Action { + + @Override + AuthType getMinAuthLevel() { + return AuthType.LOGGED_IN; + } + + @Override + void checkSpecificAccessControl() { + // Any user can create instructor account as long as the registration key is valid. + } @Override public JsonResult execute() { - AccountCreateRequest createRequest = getAndValidateRequestBody(AccountCreateRequest.class); + String registrationKey; + + try { + registrationKey = StringHelper.decrypt(getNonNullRequestParamValue(Const.ParamsNames.REGKEY)); + } catch (InvalidParametersException e) { + return new JsonResult(e.getMessage(), HttpStatus.SC_BAD_REQUEST); + } + + AccountRequestAttributes accountRequestAttributes = logic.getAccountRequestForRegistrationKey(registrationKey); + + if (accountRequestAttributes == null) { + return new JsonResult("Invalid registration key", HttpStatus.SC_BAD_REQUEST); + } + + String instructorEmail = accountRequestAttributes.getEmail(); + String instructorName = accountRequestAttributes.getName(); + String instructorInstitution = accountRequestAttributes.getInstitute(); - String instructorName = createRequest.getInstructorName().trim(); - String instructorEmail = createRequest.getInstructorEmail().trim(); - String instructorInstitution = createRequest.getInstructorInstitution().trim(); String courseId; try { @@ -37,19 +60,24 @@ public JsonResult execute() { } catch (InvalidParametersException e) { return new JsonResult(e.getMessage(), HttpStatus.SC_BAD_REQUEST); } + List instructorList = logic.getInstructorsForCourse(courseId); - String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) - .withRegistrationKey(instructorList.get(0).getEncryptedKey()) - .withInstructorInstitution(instructorInstitution) - .withInstitutionMac(StringHelper.generateSignature(instructorInstitution)) - .withEntityType(Const.EntityType.INSTRUCTOR) - .toAbsoluteString(); - EmailWrapper email = emailGenerator.generateNewInstructorAccountJoinEmail( - instructorList.get(0).getEmail(), instructorName, joinLink); - emailSender.sendEmail(email); - - JoinLinkData output = new JoinLinkData(joinLink); - return new JsonResult(output); + + try { + logic.joinCourseForInstructor(instructorList.get(0).getEncryptedKey(), userInfo.id, instructorInstitution, + StringHelper.generateSignature(instructorInstitution)); + } catch (EntityDoesNotExistException ednee) { + return new JsonResult(ednee.getMessage(), HttpStatus.SC_NOT_FOUND); + } catch (EntityAlreadyExistsException eaee) { + throw new InvalidOperationException(eaee); + } catch (InvalidParametersException ipe) { + return new JsonResult(ipe.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); + } + + // Delete account request as it is no longer needed + logic.deleteAccountRequest(registrationKey); + + return new JsonResult("Account successfully created", HttpStatus.SC_OK); } /** diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java new file mode 100644 index 00000000000..0a01bc66877 --- /dev/null +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -0,0 +1,65 @@ +package teammates.ui.webapi; + +import java.security.SecureRandom; + +import org.apache.http.HttpStatus; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.InvalidParametersException; +import teammates.common.util.Config; +import teammates.common.util.Const; +import teammates.common.util.EmailWrapper; +import teammates.common.util.StringHelper; +import teammates.ui.output.JoinLinkData; +import teammates.ui.request.AccountCreateRequest; + +/** + * Creates a new account request. + */ +class CreateAccountRequestAction extends AdminOnlyAction { + + @Override + public JsonResult execute() { + AccountCreateRequest createRequest = getAndValidateRequestBody(AccountCreateRequest.class); + + String instructorName = createRequest.getInstructorName().trim(); + String instructorEmail = createRequest.getInstructorEmail().trim(); + String instructorInstitution = createRequest.getInstructorInstitution().trim(); + String registrationKey = generateRegistrationKey(instructorEmail); + + AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes + .builder(instructorEmail) + .withName(instructorName) + .withRegistrationKey(registrationKey) + .withInstitute(instructorInstitution) + .build(); + + try { + logic.createOrUpdateAccountRequest(accountRequestAttributes); + } catch (InvalidParametersException e) { + return new JsonResult(e.getMessage(), HttpStatus.SC_BAD_REQUEST); + } + + String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE) + .withRegistrationKey(StringHelper.encrypt(registrationKey)) + .toAbsoluteString(); + + EmailWrapper email = emailGenerator.generateNewInstructorAccountJoinEmail( + instructorEmail, instructorName, joinLink); + emailSender.sendEmail(email); + + JoinLinkData output = new JoinLinkData(joinLink); + return new JsonResult(output); + } + + /** + * Generate unique registration key for the account. + * The key contains random elements to avoid being guessed. + */ + private String generateRegistrationKey(String uniqueId) { + SecureRandom prng = new SecureRandom(); + + return uniqueId + prng.nextInt(); + } + +} diff --git a/src/web/app/app.module.ts b/src/web/app/app.module.ts index b27c3798239..8f518f43e05 100644 --- a/src/web/app/app.module.ts +++ b/src/web/app/app.module.ts @@ -46,6 +46,11 @@ let routes: Routes = [ component: PublicPageComponent, loadChildren: () => import('./user-join-page.module').then((m: any) => m.UserJoinPageModule), }, + { + path: 'createaccount', + component: PublicPageComponent, + loadChildren: () => import('./user-create-account-page.module').then((m: any) => m.UserCreateAccountPageModule), + }, { path: 'sessions', component: PublicPageComponent, diff --git a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts index 28dc1fdc3cd..b2e71cce344 100644 --- a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts +++ b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts @@ -93,7 +93,7 @@ export class AdminHomePageComponent { instructor.status = 'ADDING'; this.isAddingInstructors = true; - this.accountService.createAccount({ + this.accountService.createAccountRequest({ instructorEmail: instructor.email, instructorName: instructor.name, instructorInstitution: instructor.institution, diff --git a/src/web/app/user-create-account-page.component.html b/src/web/app/user-create-account-page.component.html new file mode 100644 index 00000000000..097d879f7c5 --- /dev/null +++ b/src/web/app/user-create-account-page.component.html @@ -0,0 +1,34 @@ +
+
+
+

Not logged in

+
+
+ You are not logged in. Please log in first. +
+
+
+
+

Invalid account creation link

+
+
+ Please check that you copied the entire account creation link URL, or have it resent to you if this message persists. +
+
+ The account creation link has been used and is no longer valid. +
+
+
+
+

Confirm your Google account

+
+
+ You are currently logged in as {{ userId }}. +
If this is not you, please log out and log in using your own Google account. +
If this is you, please confirm below to complete your registration.
+
+ +
+
+
+
diff --git a/src/web/app/user-create-account-page.component.scss b/src/web/app/user-create-account-page.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/web/app/user-create-account-page.component.spec.ts b/src/web/app/user-create-account-page.component.spec.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/web/app/user-create-account-page.component.ts b/src/web/app/user-create-account-page.component.ts new file mode 100644 index 00000000000..4d86690ddcd --- /dev/null +++ b/src/web/app/user-create-account-page.component.ts @@ -0,0 +1,69 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { finalize } from "rxjs/operators"; +import { AccountService } from "../services/account.service"; +import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; +import { AuthService } from "../services/auth.service"; +import { AuthInfo } from "../types/api-output"; +import { ErrorMessageOutput } from "./error-message-output"; +import { NavigationService } from "../services/navigation.service"; +import { ErrorReportComponent } from "./components/error-report/error-report.component"; + +/** + * User create account page component. + */ +@Component({ + selector: "tm-user-create-account-page", + templateUrl: "./user-create-account-page.component.html", + styleUrls: ["./user-create-account-page.component.scss"], +}) +export class UserCreateAccountPageComponent implements OnInit { + isLoading: boolean = true; + isCreatingAccount: boolean = false; + hasJoined: boolean = false; + validUrl: boolean = true; + key: string = ""; + userId: string = ""; + + constructor( + private route: ActivatedRoute, + private router: Router, + private navigationService: NavigationService, + private accountService: AccountService, + private authService: AuthService, + private ngbModal: NgbModal + ) {} + + ngOnInit(): void { + this.route.queryParams.subscribe((queryParams: any) => { + this.key = queryParams.key; + + if (!this.key) { + this.validUrl = false; + return; + } + + this.authService.getAuthUser().subscribe((resp: AuthInfo) => { + this.userId = resp.user?.id || ""; + this.isLoading = false; + }); + }); + } + + createAccount(): void { + this.isCreatingAccount = true; + this.accountService + .createAccount(this.key) + .pipe(finalize(() => (this.isCreatingAccount = false))) + .subscribe( + (_resp) => { + this.navigationService.navigateByURL(this.router, `/web/instructor`); + }, + (resp: ErrorMessageOutput) => { + const modalRef: any = this.ngbModal.open(ErrorReportComponent); + modalRef.componentInstance.requestId = resp.error.requestId; + modalRef.componentInstance.errorMessage = resp.error.message; + } + ); + } +} diff --git a/src/web/app/user-create-account-page.module.ts b/src/web/app/user-create-account-page.module.ts new file mode 100644 index 00000000000..275ca2e6642 --- /dev/null +++ b/src/web/app/user-create-account-page.module.ts @@ -0,0 +1,28 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { UserCreateAccountPageComponent } from './user-create-account-page.component'; + +const routes: Routes = [ + { + path: '', + component: UserCreateAccountPageComponent, + }, +]; + +/** + * Module for user create account page. + */ +@NgModule({ + declarations: [ + UserCreateAccountPageComponent, + ], + exports: [ + UserCreateAccountPageComponent, + ], + imports: [ + CommonModule, + RouterModule.forChild(routes), + ], +}) +export class UserCreateAccountPageModule { } diff --git a/src/web/services/account.service.ts b/src/web/services/account.service.ts index ddc0ae6a008..fd128ef077a 100644 --- a/src/web/services/account.service.ts +++ b/src/web/services/account.service.ts @@ -18,8 +18,18 @@ export class AccountService { /** * Creates an account by calling API. */ - createAccount(request: AccountCreateRequest): Observable { - return this.httpRequestService.post(ResourceEndpoints.ACCOUNT, {}, request); + createAccount(key: string): Observable { + const paramMap: Record = { + key: key + }; + return this.httpRequestService.post(ResourceEndpoints.ACCOUNT, paramMap); + } + + /** + * Creates an account request by calling API. + */ + createAccountRequest(request: AccountCreateRequest): Observable { + return this.httpRequestService.post(ResourceEndpoints.ACCOUNT_REQUEST, {}, request); } /** From 5e2559188ae031357a9b7b720f468faecdcbe21b Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Fri, 27 Aug 2021 21:19:33 +0800 Subject: [PATCH 02/79] Fix deleteAccountRequest --- src/main/java/teammates/logic/api/Logic.java | 6 +++--- .../java/teammates/logic/core/AccountRequestsLogic.java | 6 +++--- .../java/teammates/ui/webapi/CreateAccountAction.java | 8 +++++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index a4a302ecdf4..645dff49816 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -1398,10 +1398,10 @@ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttri *

Preconditions:

* * All parameters are non-null. */ - public void deleteAccountRequest(String registrationKey) { - assert registrationKey != null; + public void deleteAccountRequest(String email) { + assert email != null; - accountRequestsLogic.deleteAccountRequest(registrationKey); + accountRequestsLogic.deleteAccountRequest(email); } /** diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index 0a1ebbcde5d..cb6452bd090 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -43,12 +43,12 @@ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttri } /** - * Deletes the account request associated with the {@code registrationKey}. + * Deletes the account request associated with the {@code email}. * *

Fails silently if the account request doesn't exist.

*/ - public void deleteAccountRequest(String registrationKey) { - accountRequestsDb.deleteAccountRequest(registrationKey); + public void deleteAccountRequest(String email) { + accountRequestsDb.deleteAccountRequest(email); } /** diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 15069d4c433..689bb9cd807 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -75,8 +75,14 @@ public JsonResult execute() { } // Delete account request as it is no longer needed - logic.deleteAccountRequest(registrationKey); + logic.deleteAccountRequest(instructorEmail); + if (logic.getAccountRequestForRegistrationKey(registrationKey) == null) { + System.out.println("YESSSS"); + } else { + System.out.println("NOOOOOO"); + } + return new JsonResult("Account successfully created", HttpStatus.SC_OK); } From a0f55dd69597962bebb8b80d073b37fd68a6a21e Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Fri, 27 Aug 2021 21:36:19 +0800 Subject: [PATCH 03/79] Add loading spinner to create-account-page --- src/web/app/user-create-account-page.component.html | 2 +- src/web/app/user-create-account-page.component.ts | 5 ++--- src/web/app/user-create-account-page.module.ts | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/web/app/user-create-account-page.component.html b/src/web/app/user-create-account-page.component.html index 097d879f7c5..0947e3653c5 100644 --- a/src/web/app/user-create-account-page.component.html +++ b/src/web/app/user-create-account-page.component.html @@ -1,4 +1,4 @@ -
+

Not logged in

diff --git a/src/web/app/user-create-account-page.component.ts b/src/web/app/user-create-account-page.component.ts index 4d86690ddcd..edb6164e322 100644 --- a/src/web/app/user-create-account-page.component.ts +++ b/src/web/app/user-create-account-page.component.ts @@ -19,7 +19,6 @@ import { ErrorReportComponent } from "./components/error-report/error-report.com }) export class UserCreateAccountPageComponent implements OnInit { isLoading: boolean = true; - isCreatingAccount: boolean = false; hasJoined: boolean = false; validUrl: boolean = true; key: string = ""; @@ -51,10 +50,10 @@ export class UserCreateAccountPageComponent implements OnInit { } createAccount(): void { - this.isCreatingAccount = true; + this.isLoading = true; this.accountService .createAccount(this.key) - .pipe(finalize(() => (this.isCreatingAccount = false))) + .pipe(finalize(() => (this.isLoading = false))) .subscribe( (_resp) => { this.navigationService.navigateByURL(this.router, `/web/instructor`); diff --git a/src/web/app/user-create-account-page.module.ts b/src/web/app/user-create-account-page.module.ts index 275ca2e6642..92c2b5ef5c4 100644 --- a/src/web/app/user-create-account-page.module.ts +++ b/src/web/app/user-create-account-page.module.ts @@ -1,6 +1,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { LoadingSpinnerModule } from './components/loading-spinner/loading-spinner.module'; import { UserCreateAccountPageComponent } from './user-create-account-page.component'; const routes: Routes = [ @@ -23,6 +24,7 @@ const routes: Routes = [ imports: [ CommonModule, RouterModule.forChild(routes), + LoadingSpinnerModule ], }) export class UserCreateAccountPageModule { } From 2a0aced9ecefa07b67f95a8ed15f622f1ed17df0 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Fri, 27 Aug 2021 22:28:21 +0800 Subject: [PATCH 04/79] Add check for whether user already has account --- src/web/app/user-create-account-page.component.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/web/app/user-create-account-page.component.ts b/src/web/app/user-create-account-page.component.ts index edb6164e322..cd4e5c1d451 100644 --- a/src/web/app/user-create-account-page.component.ts +++ b/src/web/app/user-create-account-page.component.ts @@ -39,11 +39,18 @@ export class UserCreateAccountPageComponent implements OnInit { if (!this.key) { this.validUrl = false; + this.isLoading = false; return; } this.authService.getAuthUser().subscribe((resp: AuthInfo) => { this.userId = resp.user?.id || ""; + + if (resp.user?.isInstructor) { + // User already has instructor account + this.navigationService.navigateByURL(this.router, `/web/instructor`); + } + this.isLoading = false; }); }); From 5e7f2a8543a5dc62a33fdeb50f379dc0fb5bfe80 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Fri, 27 Aug 2021 22:45:38 +0800 Subject: [PATCH 05/79] Update frontend tests --- ...create-account-page.component.spec.ts.snap | 117 ++++++++++++++ .../admin-home-page.component.spec.ts.snap | 2 +- .../admin-home-page.component.spec.ts | 12 +- ...user-create-account-page.component.spec.ts | 146 ++++++++++++++++++ src/web/services/account.service.spec.ts | 13 +- 5 files changed, 281 insertions(+), 9 deletions(-) create mode 100644 src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap diff --git a/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap b/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap new file mode 100644 index 00000000000..6b885f89bcb --- /dev/null +++ b/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap @@ -0,0 +1,117 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UserCreateAccountPageComponent should snap if user has not created an account and has a valid url 1`] = ` + +
+ +
+
+

+ Not logged in +

+
+
+ You are not logged in. Please log in first. +
+
+ + +
+
+`; + +exports[`UserCreateAccountPageComponent should snap with default fields 1`] = ` + + +
+
+
+ Loading... +
+
+ + +`; + +exports[`UserCreateAccountPageComponent should snap with invalid url 1`] = ` + +
+ + +
+
+

+ Invalid account creation link +

+
+ +
+ Please check that you copied the entire account creation link URL, or have it resent to you if this message persists. +
+ +
+ +
+
+`; diff --git a/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap b/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap index 0eeb8e3fc96..ba03a0b004c 100644 --- a/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap +++ b/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap @@ -554,7 +554,7 @@ exports[`AdminHomePageComponent should snap with some instructors details 1`] = > Instructor "Instructor B" has been successfully created. [ join link diff --git a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts index c277a98c204..641ad627d65 100644 --- a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts +++ b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts @@ -156,8 +156,8 @@ describe('AdminHomePageComponent', () => { message: 'This should not be displayed', }, ]; - spyOn(service, 'createAccount').and.returnValue(of({ - joinLink: 'http://localhost:4200/web/join', + spyOn(service, 'createAccountRequest').and.returnValue(of({ + joinLink: 'http://localhost:4200/web/createaccount', })); fixture.detectChanges(); @@ -165,7 +165,7 @@ describe('AdminHomePageComponent', () => { button.click(); expect(component.instructorsConsolidated[0].status).toEqual('SUCCESS'); - expect(component.instructorsConsolidated[0].joinLink).toEqual('http://localhost:4200/web/join'); + expect(component.instructorsConsolidated[0].joinLink).toEqual('http://localhost:4200/web/createaccount'); expect(component.activeRequests).toEqual(0); }); @@ -180,7 +180,7 @@ describe('AdminHomePageComponent', () => { message: 'This should not be displayed', }, ]; - spyOn(service, 'createAccount').and.returnValue(throwError({ + spyOn(service, 'createAccountRequest').and.returnValue(throwError({ error: { message: 'This is the error message', }, @@ -210,7 +210,7 @@ describe('AdminHomePageComponent', () => { email: 'instructorb@example.com', institution: 'Sample Institution B', status: 'SUCCESS', - joinLink: 'http://localhost:4200/web/join', + joinLink: 'http://localhost:4200/web/createaccount', message: 'This should not be displayed', }, { @@ -254,7 +254,7 @@ describe('AdminHomePageComponent', () => { email: 'instructorb@example.com', institution: 'Sample Institution B', status: 'SUCCESS', - joinLink: 'http://localhost:4200/web/join', + joinLink: 'http://localhost:4200/web/createaccount', message: 'This should not be displayed', }, { diff --git a/src/web/app/user-create-account-page.component.spec.ts b/src/web/app/user-create-account-page.component.spec.ts index e69de29bb2d..12335a67c74 100644 --- a/src/web/app/user-create-account-page.component.spec.ts +++ b/src/web/app/user-create-account-page.component.spec.ts @@ -0,0 +1,146 @@ +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; +import { ActivatedRoute } from "@angular/router"; +import { RouterTestingModule } from "@angular/router/testing"; +import { of } from "rxjs"; +import { AccountService } from "../services/account.service"; +import { AuthService } from "../services/auth.service"; +import { NavigationService } from "../services/navigation.service"; +import { LoadingSpinnerModule } from "./components/loading-spinner/loading-spinner.module"; +import { UserCreateAccountPageComponent } from "./user-create-account-page.component"; +import Spy = jasmine.Spy; + +describe("UserCreateAccountPageComponent", () => { + let component: UserCreateAccountPageComponent; + let fixture: ComponentFixture; + let navService: NavigationService; + let accountService: AccountService; + let authService: AuthService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [UserCreateAccountPageComponent], + imports: [ + HttpClientTestingModule, + RouterTestingModule, + LoadingSpinnerModule, + ], + providers: [ + NavigationService, + AccountService, + AuthService, + { + provide: ActivatedRoute, + useValue: { + queryParams: of({ + key: "key", + }), + }, + }, + ], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserCreateAccountPageComponent); + component = fixture.componentInstance; + navService = TestBed.inject(NavigationService); + accountService = TestBed.inject(AccountService); + authService = TestBed.inject(AuthService); + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); + + it("should snap with default fields", () => { + expect(fixture).toMatchSnapshot(); + }); + + it("should snap if user has not created an account and has a valid url", () => { + component.hasJoined = false; + component.userId = ""; + component.validUrl = true; + component.isLoading = false; + + fixture.detectChanges(); + + expect(fixture).toMatchSnapshot(); + }); + + it("should snap with invalid url", () => { + component.validUrl = false; + component.isLoading = false; + + fixture.detectChanges(); + + expect(fixture).toMatchSnapshot(); + }); + + it("should create account when create account button is clicked on", () => { + const params: string[] = ["key"]; + component.isLoading = false; + component.userId = "user"; + component.key = params[0]; + component.validUrl = true; + + const accountSpy: Spy = spyOn( + accountService, + "createAccount" + ).and.returnValue(of({})); + const navSpy: Spy = spyOn(navService, "navigateByURL"); + + fixture.detectChanges(); + + const btn: any = + fixture.debugElement.nativeElement.querySelector("#btn-confirm"); + btn.click(); + + expect(accountSpy.calls.count()).toEqual(1); + expect(accountSpy.calls.mostRecent().args).toEqual(params); + expect(navSpy.calls.count()).toEqual(1); + expect(navSpy.calls.mostRecent().args[1]).toEqual(`/web/instructor`); + }); + + it("should redirect user to home page if user already has account", () => { + spyOn(authService, "getAuthUser").and.returnValue( + of({ + user: { + id: "user", + isInstructor: true, + }, + }) + ); + const navSpy: Spy = spyOn(navService, "navigateByURL"); + + component.ngOnInit(); + + expect(component.userId).toEqual("user"); + expect(navSpy.calls.count()).toEqual(1); + expect(navSpy.calls.mostRecent().args[1]).toEqual("/web/instructor"); + }); + + it("should stop loading if user is not logged in", () => { + spyOn(authService, "getAuthUser").and.returnValue(of({})); + + component.ngOnInit(); + + expect(component.isLoading).toBeFalsy(); + }); + + it("should stop loading if user does not have an account", () => { + spyOn(authService, "getAuthUser").and.returnValue( + of({ + user: { + id: "user", + isInstructor: false, + }, + }) + ); + + component.ngOnInit(); + + expect(component.isLoading).toBeFalsy(); + }); +}); diff --git a/src/web/services/account.service.spec.ts b/src/web/services/account.service.spec.ts index cd61a52b919..173726c398a 100644 --- a/src/web/services/account.service.spec.ts +++ b/src/web/services/account.service.spec.ts @@ -42,13 +42,22 @@ describe('AccountService', () => { }); it('should execute POST on account endpoint', () => { + const testKey = 'testKey'; + const paramMap: Record = { + key: testKey, + }; + service.createAccount(testKey); + expect(spyHttpRequestService.post).toHaveBeenCalledWith(ResourceEndpoints.ACCOUNT, paramMap); + }); + + it('should execute POST on account request endpoint', () => { const testRequest: AccountCreateRequest = new class implements AccountCreateRequest { instructorEmail: string = 'testEmail'; instructorInstitution: string = 'testInstitution'; instructorName: string = 'testName'; }; - service.createAccount(testRequest); - expect(spyHttpRequestService.post).toHaveBeenCalledWith(ResourceEndpoints.ACCOUNT, {}, testRequest); + service.createAccountRequest(testRequest); + expect(spyHttpRequestService.post).toHaveBeenCalledWith(ResourceEndpoints.ACCOUNT_REQUEST, {}, testRequest); }); it('should execute DELETE on account endpoint', () => { From 05051bc5d6d9e8dd2e16b363c9d9d99df1c54b1a Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 01:15:19 +0800 Subject: [PATCH 06/79] Add component tests --- .../teammates/e2e/cases/BaseE2ETestCase.java | 6 + .../java/teammates/common/util/Const.java | 1 + src/main/java/teammates/logic/api/Logic.java | 15 ++ .../logic/core/AccountRequestsLogic.java | 7 + .../storage/api/AccountRequestsDb.java | 16 +- .../ui/output/AccountRequestData.java | 45 +++++ .../logic/core/AccountRequestsLogicTest.java | 161 ++++++++++++++++++ .../storage/api/AccountRequestsDbTest.java | 148 ++++++++++++++++ .../java/teammates/test/AbstractBackDoor.java | 41 +++++ .../test/BaseTestCaseWithDatabaseAccess.java | 17 ++ .../BaseTestCaseWithLocalDatabaseAccess.java | 6 + 11 files changed, 457 insertions(+), 6 deletions(-) create mode 100644 src/main/java/teammates/ui/output/AccountRequestData.java create mode 100644 src/test/java/teammates/logic/core/AccountRequestsLogicTest.java create mode 100644 src/test/java/teammates/storage/api/AccountRequestsDbTest.java diff --git a/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java b/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java index 18300033488..2e937dd6ad9 100644 --- a/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java +++ b/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java @@ -11,6 +11,7 @@ import teammates.common.datatransfer.DataBundle; import teammates.common.datatransfer.attributes.AccountAttributes; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; import teammates.common.datatransfer.attributes.FeedbackResponseAttributes; @@ -298,6 +299,11 @@ String getKeyForStudent(StudentAttributes student) { return getStudent(student).getKey(); } + @Override + protected AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest) { + return BACKDOOR.getAccountRequest(accountRequest.getEmail()); + } + @Override protected boolean doRemoveAndRestoreDataBundle(DataBundle testData) { try { diff --git a/src/main/java/teammates/common/util/Const.java b/src/main/java/teammates/common/util/Const.java index 1ea03a53ea2..9aaeb74d595 100644 --- a/src/main/java/teammates/common/util/Const.java +++ b/src/main/java/teammates/common/util/Const.java @@ -147,6 +147,7 @@ public static class ParamsNames { public static final String USER_CAPTCHA_RESPONSE = "captcharesponse"; + public static final String EMAIL = "email"; public static final String EMAIL_TYPE = "emailtype"; public static final String ENTITY_TYPE = "entitytype"; diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 645dff49816..7eb8c278cdc 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -1417,4 +1417,19 @@ public AccountRequestAttributes getAccountRequestForRegistrationKey(String regis return accountRequestsLogic.getAccountRequestForRegistrationKey(registrationKey); } + + /** + * Gets an account request by email address. + * + *

Preconditions:

+ * * All parameters are non-null. + * + * @return the account request or null if no match found + */ + public AccountRequestAttributes getAccountRequest(String email) { + assert email != null; + + return accountRequestsLogic.getAccountRequest(email); + } + } diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index cb6452bd090..9d7ffb1981d 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -58,4 +58,11 @@ public AccountRequestAttributes getAccountRequestForRegistrationKey(String regis return accountRequestsDb.getAccountRequestForRegistrationKey(registrationKey); } + /** + * Gets an account request by email address. + */ + public AccountRequestAttributes getAccountRequest(String email) { + return accountRequestsDb.getAccountRequest(email); + } + } diff --git a/src/main/java/teammates/storage/api/AccountRequestsDb.java b/src/main/java/teammates/storage/api/AccountRequestsDb.java index 3e616f46380..e31b71a51e8 100644 --- a/src/main/java/teammates/storage/api/AccountRequestsDb.java +++ b/src/main/java/teammates/storage/api/AccountRequestsDb.java @@ -2,7 +2,6 @@ import static com.googlecode.objectify.ObjectifyService.ofy; -import java.security.InvalidParameterException; import java.util.List; import java.util.stream.Collectors; @@ -10,6 +9,7 @@ import com.googlecode.objectify.cmd.LoadType; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.InvalidParametersException; import teammates.storage.entity.AccountRequest; /** @@ -39,6 +39,10 @@ public AccountRequestAttributes getAccountRequest(String email) { return makeAttributesOrNull(getAccountRequestEntity(email)); } + private AccountRequest getAccountRequestEntity(String email) { + return load().id(email).now(); + } + /** * Deletes an accountRequest. */ @@ -59,10 +63,6 @@ boolean hasExistingEntities(AccountRequestAttributes entityToCreate) { return !load().filterKey(keyToFind).keys().list().isEmpty(); } - private AccountRequest getAccountRequestEntity(String email) { - return load().id(email).now(); - } - @Override AccountRequestAttributes makeAttributes(AccountRequest entity) { assert entity != null; @@ -77,10 +77,14 @@ AccountRequestAttributes makeAttributes(AccountRequest entity) { * @throws InvalidParametersException if attributes to update are not valid */ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) - throws InvalidParameterException { + throws InvalidParametersException { assert accountRequestToAdd != null; accountRequestToAdd.sanitizeForSaving(); + if (!accountRequestToAdd.isValid()) { + throw new InvalidParametersException(accountRequestToAdd.getInvalidityInfo()); + } + AccountRequest accountRequest = accountRequestToAdd.toEntity(); saveEntity(accountRequest); diff --git a/src/main/java/teammates/ui/output/AccountRequestData.java b/src/main/java/teammates/ui/output/AccountRequestData.java new file mode 100644 index 00000000000..459910f87be --- /dev/null +++ b/src/main/java/teammates/ui/output/AccountRequestData.java @@ -0,0 +1,45 @@ +package teammates.ui.output; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; + +/** + * Output format of account request data. + */ +public class AccountRequestData extends ApiOutput { + + private final String email; + private final String name; + private final String institute; + private final String registrationKey; + private final long createdAtTimeStamp; + + public AccountRequestData(AccountRequestAttributes accountRequestInfo) { + this.name = accountRequestInfo.getName(); + this.email = accountRequestInfo.getEmail(); + this.institute = accountRequestInfo.getInstitute(); + this.registrationKey = accountRequestInfo.getRegistrationKey(); + this.createdAtTimeStamp = accountRequestInfo.getCreatedAt().toEpochMilli(); + } + + public String getInstitute() { + return institute; + } + + public String getEmail() { + return email; + } + + + public long getCreatedAtTimeStamp() { + return createdAtTimeStamp; + } + + public String getName() { + return name; + } + + public String getRegistrationKey() { + return registrationKey; + } + +} diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java new file mode 100644 index 00000000000..35529145c60 --- /dev/null +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -0,0 +1,161 @@ +package teammates.logic.core; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.InvalidParametersException; +import teammates.common.util.FieldValidator; +import teammates.storage.api.AccountRequestsDb; +import teammates.test.AssertHelper; + +/** + * SUT: {@link AccountRequestsLogic}. + */ +public class AccountRequestsLogicTest extends BaseLogicTest { + + private final AccountRequestsLogic accountRequestsLogic = AccountRequestsLogic.inst(); + private final AccountRequestsDb accountRequestsDb = AccountRequestsDb.inst(); + + @Override + protected void prepareTestData() { + // test data is refreshed before each test case + } + + @BeforeMethod + public void refreshTestData() { + dataBundle = getTypicalDataBundle(); + removeAndRestoreTypicalDataBundle(); + } + + @Test + public void testCreateOrUpdateAccountRequest() throws Exception { + + ______TS("typical success case"); + + AccountRequestAttributes a = AccountRequestAttributes.builder("valid@test.com") + .withName("Test account Name") + .withRegistrationKey("ValidRegistrationKey") + .withInstitute("TEAMMATES Test Institute 1") + .build(); + + accountRequestsLogic.createOrUpdateAccountRequest(a); + verifyPresentInDatabase(a); + + ______TS("duplicate account, account request updated"); + + AccountRequestAttributes duplicateAccount = AccountRequestAttributes.builder("valid@test.com") + .withName("Test account Name 2") + .withRegistrationKey("ValidRegistrationKey2") + .withInstitute("TEAMMATES Test Institute 2") + .build(); + + accountRequestsLogic.createOrUpdateAccountRequest(duplicateAccount); + assertEquals(getAccountRequest(duplicateAccount), duplicateAccount); + + ______TS("failure case: invalid parameter"); + + a.setEmail("invalid email"); + InvalidParametersException ipe = assertThrows(InvalidParametersException.class, + () -> accountRequestsLogic.createOrUpdateAccountRequest(a)); + AssertHelper.assertContains( + getPopulatedErrorMessage( + FieldValidator.EMAIL_ERROR_MESSAGE, "invalid email", + FieldValidator.EMAIL_FIELD_NAME, FieldValidator.REASON_INCORRECT_FORMAT, + FieldValidator.EMAIL_MAX_LENGTH), + ipe.getMessage()); + + ______TS("failure: null parameter"); + assertThrows(AssertionError.class, + () -> accountRequestsLogic.createOrUpdateAccountRequest(null)); + } + + @Test + public void testDeleteAccountRequest() throws Exception { + AccountRequestAttributes a = AccountRequestAttributes.builder("valid2@test.com") + .withName("Test account Name") + .withRegistrationKey("ValidRegistrationKey2") + .withInstitute("TEAMMATES Test Institute 1") + .build(); + + accountRequestsDb.createEntity(a); + + ______TS("silent deletion of non-existent account"); + + accountRequestsLogic.deleteAccountRequest("not_exist"); + + ______TS("typical success case"); + + verifyPresentInDatabase(a); + + accountRequestsLogic.deleteAccountRequest(a.getEmail()); + + verifyAbsentInDatabase(a); + + ______TS("silent deletion of same account"); + + accountRequestsLogic.deleteAccountRequest(a.getEmail()); + + ______TS("failure null parameter"); + + assertThrows(AssertionError.class, + () -> accountRequestsLogic.deleteAccountRequest(null)); + } + + @Test + public void testgetAccountRequestForRegistrationKey() throws Exception { + AccountRequestAttributes a = AccountRequestAttributes.builder("valid3@test.com") + .withName("Test account Name") + .withRegistrationKey("ValidRegistrationKey3") + .withInstitute("TEAMMATES Test Institute 1") + .build(); + + accountRequestsDb.createEntity(a); + + ______TS("typical success case"); + + AccountRequestAttributes accountRequestAttributes = + accountRequestsLogic.getAccountRequestForRegistrationKey(a.getRegistrationKey()); + assertEquals(a, accountRequestAttributes); + + ______TS("account request not found"); + + AccountRequestAttributes notFoundRequestAttributes = + accountRequestsLogic.getAccountRequestForRegistrationKey("not-found"); + assertNull(notFoundRequestAttributes); + + ______TS("failure null parameter"); + + assertThrows(AssertionError.class, + () -> accountRequestsLogic.getAccountRequestForRegistrationKey(null)); + } + + @Test + public void testGetAccountRequest() throws Exception { + AccountRequestAttributes a = AccountRequestAttributes.builder("valid4@test.com") + .withName("Test account Name") + .withRegistrationKey("ValidRegistrationKey4") + .withInstitute("TEAMMATES Test Institute 1") + .build(); + + accountRequestsDb.createEntity(a); + + ______TS("typical success case"); + + AccountRequestAttributes accountRequestAttributes = + accountRequestsLogic.getAccountRequest(a.getEmail()); + assertEquals(a, accountRequestAttributes); + + ______TS("account request not found"); + + AccountRequestAttributes notFoundRequestAttributes = + accountRequestsLogic.getAccountRequest("not-found@test.com"); + assertNull(notFoundRequestAttributes); + + ______TS("failure null parameter"); + + assertThrows(AssertionError.class, + () -> accountRequestsLogic.getAccountRequest(null)); + } + +} diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java new file mode 100644 index 00000000000..2308cfe8270 --- /dev/null +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -0,0 +1,148 @@ +package teammates.storage.api; + +import org.testng.annotations.Test; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.InvalidParametersException; +import teammates.common.util.FieldValidator; +import teammates.test.AssertHelper; +import teammates.test.BaseTestCaseWithLocalDatabaseAccess; + +/** + * SUT: {@link AccountRequestsDb}. + */ +public class AccountRequestsDbTest extends BaseTestCaseWithLocalDatabaseAccess { + + private final AccountRequestsDb accountRequestsDb = AccountRequestsDb.inst(); + + @Test + public void testCreateOrUpdateAccountRequest() throws Exception { + + ______TS("typical success case"); + + AccountRequestAttributes a = AccountRequestAttributes.builder("valid@test.com") + .withName("Test account Name") + .withRegistrationKey("ValidRegistrationKey") + .withInstitute("TEAMMATES Test Institute 1") + .build(); + + accountRequestsDb.createOrUpdateAccountRequest(a); + verifyPresentInDatabase(a); + + ______TS("duplicate account, account request updated"); + + AccountRequestAttributes duplicateAccount = AccountRequestAttributes.builder("valid@test.com") + .withName("Test account Name 2") + .withRegistrationKey("ValidRegistrationKey2") + .withInstitute("TEAMMATES Test Institute 2") + .build(); + + accountRequestsDb.createOrUpdateAccountRequest(duplicateAccount); + assertEquals(getAccountRequest(duplicateAccount), duplicateAccount); + + ______TS("failure case: invalid parameter"); + + a.setEmail("invalid email"); + InvalidParametersException ipe = assertThrows(InvalidParametersException.class, + () -> accountRequestsDb.createOrUpdateAccountRequest(a)); + AssertHelper.assertContains( + getPopulatedErrorMessage( + FieldValidator.EMAIL_ERROR_MESSAGE, "invalid email", + FieldValidator.EMAIL_FIELD_NAME, FieldValidator.REASON_INCORRECT_FORMAT, + FieldValidator.EMAIL_MAX_LENGTH), + ipe.getMessage()); + + ______TS("failure: null parameter"); + assertThrows(AssertionError.class, + () -> accountRequestsDb.createOrUpdateAccountRequest(null)); + } + + @Test + public void testDeleteAccountRequest() throws Exception { + AccountRequestAttributes a = AccountRequestAttributes.builder("valid2@test.com") + .withName("Test account Name") + .withRegistrationKey("ValidRegistrationKey2") + .withInstitute("TEAMMATES Test Institute 1") + .build(); + + accountRequestsDb.createEntity(a); + + ______TS("silent deletion of non-existent account"); + + accountRequestsDb.deleteAccountRequest("not_exist"); + + ______TS("typical success case"); + + verifyPresentInDatabase(a); + + accountRequestsDb.deleteAccountRequest(a.getEmail()); + + verifyAbsentInDatabase(a); + + ______TS("silent deletion of same account"); + + accountRequestsDb.deleteAccountRequest(a.getEmail()); + + ______TS("failure null parameter"); + + assertThrows(AssertionError.class, + () -> accountRequestsDb.deleteAccountRequest(null)); + } + + @Test + public void testGetAccountRequestForRegistrationKey() throws Exception { + AccountRequestAttributes a = AccountRequestAttributes.builder("valid3@test.com") + .withName("Test account Name") + .withRegistrationKey("ValidRegistrationKey3") + .withInstitute("TEAMMATES Test Institute 1") + .build(); + + accountRequestsDb.createEntity(a); + + ______TS("typical success case"); + + AccountRequestAttributes accountRequestAttributes = + accountRequestsDb.getAccountRequestForRegistrationKey(a.getRegistrationKey()); + assertEquals(a, accountRequestAttributes); + + ______TS("account request not found"); + + AccountRequestAttributes notFoundRequestAttributes = + accountRequestsDb.getAccountRequestForRegistrationKey("not-found"); + assertNull(notFoundRequestAttributes); + + ______TS("failure null parameter"); + + assertThrows(AssertionError.class, + () -> accountRequestsDb.getAccountRequestForRegistrationKey(null)); + } + + @Test + public void testGetAccountRequest() throws Exception { + AccountRequestAttributes a = AccountRequestAttributes.builder("valid4@test.com") + .withName("Test account Name") + .withRegistrationKey("ValidRegistrationKey4") + .withInstitute("TEAMMATES Test Institute 1") + .build(); + + accountRequestsDb.createEntity(a); + + ______TS("typical success case"); + + AccountRequestAttributes accountRequestAttributes = + accountRequestsDb.getAccountRequest(a.getEmail()); + assertEquals(a, accountRequestAttributes); + + ______TS("account request not found"); + + AccountRequestAttributes notFoundRequestAttributes = + accountRequestsDb.getAccountRequest("not-found@test.com"); + assertNull(notFoundRequestAttributes); + + ______TS("failure null parameter"); + + assertThrows(AssertionError.class, + () -> accountRequestsDb.getAccountRequest(null)); + } + +} diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 0fc6c583b76..7ca62e84e96 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -34,6 +34,7 @@ import teammates.common.datatransfer.DataBundle; import teammates.common.datatransfer.FeedbackParticipantType; import teammates.common.datatransfer.attributes.AccountAttributes; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; import teammates.common.datatransfer.attributes.FeedbackResponseAttributes; @@ -45,6 +46,7 @@ import teammates.common.util.Const; import teammates.common.util.JsonUtils; import teammates.ui.output.AccountData; +import teammates.ui.output.AccountRequestData; import teammates.ui.output.CourseData; import teammates.ui.output.CoursesData; import teammates.ui.output.FeedbackQuestionData; @@ -739,4 +741,43 @@ private static final class ResponseBodyAndCode { } } + + /** + * Gets account request data from the database. + */ + public AccountRequestData getAccountRequestData(String email) { + Map params = new HashMap<>(); + params.put(Const.ParamsNames.EMAIL, email); + ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.ACCOUNT_REQUEST, params); + if (response.responseCode == HttpStatus.SC_NOT_FOUND) { + return null; + } + return JsonUtils.fromJson(response.responseBody, AccountData.class); + } + + /** + * Get account request from database. + */ + public AccountRequestAttributes getAccountRequest(String email) { + AccountRequestData accountRequestData = getAccountRequestData(email); + if (accountRequestData == null) { + return null; + } + AccountRequestAttributes.Builder builder = + AccountRequestAttributes.builder(accountRequestData.getEmail()); + + if (accountRequestData.getName() != null) { + builder.withName(accountRequestData.getName()); + } + if (accountRequestData.getRegistrationKey() != null) { + builder.withRegistrationKey(accountRequestData.getRegistrationKey()); + } + if (accountRequestData.getInstitute() != null) { + builder.withInstitute(accountRequestData.getInstitute()); + } + + AccountRequestAttributes accountRequest = builder.build(); + + return accountRequest; + } } diff --git a/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java b/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java index ae21b385ec9..85053dec096 100644 --- a/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java +++ b/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java @@ -2,6 +2,7 @@ import teammates.common.datatransfer.DataBundle; import teammates.common.datatransfer.attributes.AccountAttributes; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.EntityAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; @@ -75,6 +76,9 @@ private EntityAttributes getEntity(EntityAttributes expected) { } else if (expected instanceof StudentAttributes) { return getStudent((StudentAttributes) expected); + } else if (expected instanceof AccountRequestAttributes) { + return getAccountRequest((AccountRequestAttributes) expected); + } else { throw new RuntimeException("Unknown entity type!"); } @@ -147,6 +151,12 @@ private void verifyEquals(EntityAttributes expected, EntityAttributes actu StudentAttributes actualStudent = (StudentAttributes) actual; equalizeIrrelevantData(expectedStudent, actualStudent); assertEquals(JsonUtils.toJson(expectedStudent), JsonUtils.toJson(actualStudent)); + + } else if (expected instanceof AccountRequestAttributes) { + AccountRequestAttributes expectedAccountRequest = (AccountRequestAttributes) expected; + AccountRequestAttributes actualAccountRequest = (AccountRequestAttributes) actual; + equalizeIrrelevantData(expectedAccountRequest, actualAccountRequest); + assertEquals(JsonUtils.toJson(expectedAccountRequest), JsonUtils.toJson(actualAccountRequest)); } else { throw new RuntimeException("Unknown entity type!"); @@ -210,6 +220,11 @@ private void equalizeIrrelevantData(StudentAttributes expected, StudentAttribute expected.setLastName(StringHelper.splitName(expected.getName())[1]); } + private void equalizeIrrelevantData(AccountRequestAttributes expected, AccountRequestAttributes actual) { + // Ignore time field as it is stamped at the time of creation in testing + expected.setCreatedAt(actual.getCreatedAt()); + } + protected abstract StudentProfileAttributes getStudentProfile(StudentProfileAttributes studentProfileAttributes); protected abstract CourseAttributes getCourse(CourseAttributes course); @@ -226,6 +241,8 @@ private void equalizeIrrelevantData(StudentAttributes expected, StudentAttribute protected abstract StudentAttributes getStudent(StudentAttributes student); + protected abstract AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest); + protected void removeAndRestoreDataBundle(DataBundle testData) { int retryLimit = OPERATION_RETRY_COUNT; boolean isOperationSuccess = doRemoveAndRestoreDataBundle(testData); diff --git a/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java b/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java index e207ce0d7ed..8d42ed2f871 100644 --- a/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java +++ b/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java @@ -14,6 +14,7 @@ import teammates.common.datatransfer.DataBundle; import teammates.common.datatransfer.attributes.AccountAttributes; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; import teammates.common.datatransfer.attributes.FeedbackResponseAttributes; @@ -132,6 +133,11 @@ protected StudentAttributes getStudent(StudentAttributes student) { return logic.getStudentForEmail(student.getCourse(), student.getEmail()); } + @Override + protected AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest) { + return logic.getAccountRequest(accountRequest.getEmail()); + } + protected void removeAndRestoreTypicalDataBundle() { DataBundle dataBundle = getTypicalDataBundle(); removeAndRestoreDataBundle(dataBundle); From 074bae4ad34e1563f46604772f66b131aa41641f Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 01:51:54 +0800 Subject: [PATCH 07/79] Add tests for AccountRequestAttributes --- .../AccountRequestAttributesTest.java | 187 ++++++++++++++++++ .../storage/api/AccountRequestsDbTest.java | 1 - 2 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java diff --git a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java new file mode 100644 index 00000000000..66bd8ba79cd --- /dev/null +++ b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java @@ -0,0 +1,187 @@ +package teammates.common.datatransfer.attributes; + +import java.time.Instant; + +import org.testng.annotations.Test; + +import teammates.common.util.FieldValidator; +import teammates.common.util.StringHelper; +import teammates.storage.entity.AccountRequest; +import teammates.test.BaseTestCase; + +/** + * SUT: {@link AccountRequestAttributes}. + */ +public class AccountRequestAttributesTest extends BaseTestCase { + + @Test + public void testValueOf_withTypicalData_shouldGenerateAttributesCorrectly() { + Instant typicalInstant = Instant.now(); + AccountRequest accountRequest = + new AccountRequest("valid@test.com", "registrationkey", "Valid Name", + "Valid Institute", typicalInstant, typicalInstant); + + AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes.valueOf(accountRequest); + + assertEquals("registrationkey", accountRequestAttributes.getRegistrationKey()); + assertEquals("Valid Name", accountRequestAttributes.getName()); + assertEquals("valid@test.com", accountRequestAttributes.getEmail()); + assertEquals("Valid Institute", accountRequestAttributes.getInstitute()); + assertEquals(typicalInstant, accountRequestAttributes.getCreatedAt()); + assertEquals(typicalInstant, accountRequestAttributes.getDeletedAt()); + } + + @Test + public void testBuilder_withTypicalData_shouldBuildCorrectAttributes() { + String validName = "validName"; + String validEmail = "valid@test.com"; + String validInstitute = "validInstitute"; + String validRegKey = "validRegKey123"; + + AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes + .builder(validEmail) + .withName(validName) + .withInstitute(validInstitute) + .withRegistrationKey(validRegKey) + .build(); + + assertNotNull(accountRequestAttributes.getCreatedAt()); + assertNull(accountRequestAttributes.getDeletedAt()); + assertEquals(validEmail, accountRequestAttributes.getEmail()); + assertEquals(validName, accountRequestAttributes.getName()); + assertEquals(validRegKey, accountRequestAttributes.getRegistrationKey()); + assertEquals(validInstitute, accountRequestAttributes.getInstitute()); + } + + @Test + public void testBuilder_buildNothing_shouldUseDefaultValues() { + AccountRequestAttributes accountRequestAttributes = + AccountRequestAttributes.builder("valid@test.com").build(); + + assertEquals("valid@test.com", accountRequestAttributes.getEmail()); + assertNull(accountRequestAttributes.getName()); + assertNull(accountRequestAttributes.getInstitute()); + assertNull(accountRequestAttributes.getRegistrationKey()); + assertNotNull(accountRequestAttributes.getCreatedAt()); + assertNull(accountRequestAttributes.getDeletedAt()); + } + + @Test + public void testBuilder_withNullArguments_shouldThrowException() { + assertThrows(AssertionError.class, () -> { + AccountRequestAttributes + .builder(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + AccountRequestAttributes + .builder("valid@test.com") + .withName(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + AccountRequestAttributes + .builder("valid@test.com") + .withRegistrationKey(null) + .build(); + }); + + assertThrows(AssertionError.class, () -> { + AccountRequestAttributes + .builder("valid@test.com") + .withInstitute(null) + .build(); + }); + } + + @Test + public void testValidate() throws Exception { + AccountRequestAttributes validAccountRequest = generateValidAccountRequestAttributesObject(); + + assertTrue("valid value", validAccountRequest.isValid()); + + String invalidEmail = "invalid-email"; + String emptyName = ""; + AccountRequestAttributes invalidAccountRequest = AccountRequestAttributes + .builder(invalidEmail) + .withName(emptyName) + .withInstitute("institute") + .build(); + + assertFalse("invalid value", invalidAccountRequest.isValid()); + String errorMessage = + getPopulatedErrorMessage( + FieldValidator.EMAIL_ERROR_MESSAGE, invalidAccountRequest.getEmail(), + FieldValidator.EMAIL_FIELD_NAME, FieldValidator.REASON_INCORRECT_FORMAT, + FieldValidator.EMAIL_MAX_LENGTH) + System.lineSeparator() + + getPopulatedEmptyStringErrorMessage( + FieldValidator.SIZE_CAPPED_NON_EMPTY_STRING_ERROR_MESSAGE_EMPTY_STRING, + FieldValidator.PERSON_NAME_FIELD_NAME, FieldValidator.PERSON_NAME_MAX_LENGTH); + assertEquals("invalid value", errorMessage, StringHelper.toString(invalidAccountRequest.getInvalidityInfo())); + } + + @Test + public void testGetValidityInfo() { + // already tested in testValidate() above + } + + @Test + public void testIsValid() { + // already tested in testValidate() above + } + + @Test + public void testToString() { + AccountRequestAttributes a = generateValidAccountRequestAttributesObject(); + assertEquals("[AccountRequestAttributes] email: valid@test.comregistrationKey: " + + "valid123 name: valid-name institute: valid-institute", a.toString()); + } + + @Test + public void testEquals() { + AccountRequestAttributes accountRequest = generateValidAccountRequestAttributesObject(); + + // When the two account requests have same values + AccountRequestAttributes similarAccountRequest = generateValidAccountRequestAttributesObject(); + + assertTrue(accountRequest.equals(similarAccountRequest)); + + // When the two account requests are different + AccountRequestAttributes differentAccountRequest = AccountRequestAttributes.builder("test@test.com") + .withName("Another Name") + .build(); + + assertFalse(accountRequest.equals(differentAccountRequest)); + + // When the other object is of different class + assertFalse(accountRequest.equals(3)); + } + + @Test + public void testHashCode() { + AccountRequestAttributes accountRequest = generateValidAccountRequestAttributesObject(); + + // When the two account requests have same values, they should have the same hash code + AccountRequestAttributes accountRequestSimilar = generateValidAccountRequestAttributesObject(); + + assertTrue(accountRequest.hashCode() == accountRequestSimilar.hashCode()); + + // When the two account requests are different, they should have different hash code + AccountRequestAttributes accountRequestDifferent = AccountRequestAttributes.builder("test@test.com") + .withName("Another Name") + .build(); + + assertFalse(accountRequest.hashCode() == accountRequestDifferent.hashCode()); + } + + private static AccountRequestAttributes generateValidAccountRequestAttributesObject() { + return AccountRequestAttributes.builder("valid@test.com") + .withName("valid-name") + .withRegistrationKey("valid123") + .withInstitute("valid-institute") + .build(); + } + +} diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 2308cfe8270..78520c33fe5 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -38,7 +38,6 @@ public void testCreateOrUpdateAccountRequest() throws Exception { .build(); accountRequestsDb.createOrUpdateAccountRequest(duplicateAccount); - assertEquals(getAccountRequest(duplicateAccount), duplicateAccount); ______TS("failure case: invalid parameter"); From abb06595c39e69a6c4003229decb76446fb4f10a Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 16:24:20 +0800 Subject: [PATCH 08/79] Add component tests for create account actions --- .../ui/webapi/CreateAccountAction.java | 6 - .../ui/webapi/CreateAccountActionTest.java | 91 ++++--------- .../CreateAccountRequestActionTest.java | 127 ++++++++++++++++++ .../ui/webapi/GetActionClassesActionTest.java | 1 + 4 files changed, 157 insertions(+), 68 deletions(-) create mode 100644 src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 689bb9cd807..6c839247de6 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -76,12 +76,6 @@ public JsonResult execute() { // Delete account request as it is no longer needed logic.deleteAccountRequest(instructorEmail); - - if (logic.getAccountRequestForRegistrationKey(registrationKey) == null) { - System.out.println("YESSSS"); - } else { - System.out.println("NOOOOOO"); - } return new JsonResult("Account successfully created", HttpStatus.SC_OK); } diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index 783d7f3508d..1cb495bbff6 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -5,18 +5,16 @@ import org.apache.http.HttpStatus; import org.testng.annotations.Test; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.InstructorAttributes; import teammates.common.datatransfer.attributes.StudentAttributes; -import teammates.common.exception.InvalidHttpRequestBodyException; -import teammates.common.util.Config; +import teammates.common.exception.NullHttpParameterException; import teammates.common.util.Const; -import teammates.common.util.EmailType; -import teammates.common.util.EmailWrapper; import teammates.common.util.FieldValidator; import teammates.common.util.StringHelper; import teammates.common.util.StringHelperExtension; -import teammates.ui.output.JoinLinkData; +import teammates.storage.api.AccountRequestsDb; import teammates.ui.request.AccountCreateRequest; /** @@ -36,36 +34,37 @@ protected String getRequestMethod() { @Override @Test - protected void testExecute() { - loginAsAdmin(); + protected void testExecute() throws Exception { String name = "JamesBond"; String email = "jamesbond89@gmail.tmt"; String institute = "TEAMMATES Test Institute 1"; + String regKey = "validregkey123"; - ______TS("Not enough parameters"); + AccountRequestsDb accountRequestsDb = AccountRequestsDb.inst(); + accountRequestsDb.createEntity(AccountRequestAttributes.builder(email).withName(name) + .withInstitute(institute).withRegistrationKey(regKey).build()); - Exception ex = assertThrows(InvalidHttpRequestBodyException.class, - () -> getAction(buildCreateRequest(null, institute, email)).execute()); - assertEquals("name cannot be null", ex.getMessage()); + ______TS("Not enough parameters"); - ex = assertThrows(InvalidHttpRequestBodyException.class, - () -> getAction(buildCreateRequest(name, null, email)).execute()); - assertEquals("institute cannot be null", ex.getMessage()); + verifyHttpParameterFailure(); + + ______TS("Null parameters"); - ex = assertThrows(InvalidHttpRequestBodyException.class, - () -> getAction(buildCreateRequest(name, institute, null)).execute()); - assertEquals("email cannot be null", ex.getMessage()); + String[] nullParams = new String[] { + Const.ParamsNames.REGKEY, null, + }; + Exception ex = assertThrows(NullHttpParameterException.class, + () -> getAction(nullParams).execute()); + assertEquals("The [key] HTTP parameter is null.", ex.getMessage()); verifyNoTasksAdded(); ______TS("Normal case"); - String nameWithSpaces = " " + name + " "; - String emailWithSpaces = " " + email + " "; - String instituteWithSpaces = " " + institute + " "; - - AccountCreateRequest req = buildCreateRequest(nameWithSpaces, instituteWithSpaces, emailWithSpaces); - CreateAccountAction a = getAction(req); + String[] params = new String[] { + Const.ParamsNames.REGKEY, StringHelper.encrypt(regKey), + }; + CreateAccountAction a = getAction(params); JsonResult r = getJsonResult(a); assertEquals(HttpStatus.SC_OK, r.getStatusCode()); @@ -78,50 +77,27 @@ protected void testExecute() { assertEquals(institute, course.getInstitute()); InstructorAttributes instructor = logic.getInstructorForEmail(courseId, email); - - String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) - .withRegistrationKey(instructor.getEncryptedKey()) - .withInstructorInstitution(institute) - .withInstitutionMac(StringHelper.generateSignature(institute)) - .withEntityType(Const.EntityType.INSTRUCTOR) - .toAbsoluteString(); - JoinLinkData output = (JoinLinkData) r.getOutput(); - assertEquals(joinLink, output.getJoinLink()); - - verifyNumberOfEmailsSent(1); - - EmailWrapper emailSent = mockEmailSender.getEmailsSent().get(0); - assertEquals(String.format(EmailType.NEW_INSTRUCTOR_ACCOUNT.getSubject(), name), - emailSent.getSubject()); - assertEquals(email, emailSent.getRecipient()); + assertEquals(email, instructor.getEmail()); + assertEquals(name, instructor.getName()); List studentList = logic.getStudentsForCourse(courseId); List instructorList = logic.getInstructorsForCourse(courseId); verifySpecifiedTasksAdded(Const.TaskQueue.SEARCH_INDEXING_QUEUE_NAME, studentList.size() + instructorList.size()); - ______TS("Error: invalid parameter"); + ______TS("Error: reg key not found"); - String invalidName = "James%20Bond99"; + a = getAction(params); + r = getJsonResult(a); + assertEquals(HttpStatus.SC_BAD_REQUEST, r.getStatusCode()); - req = buildCreateRequest(invalidName, institute, emailWithSpaces); - - final CreateAccountAction finalA = getAction(req); - - ex = assertThrows(InvalidHttpRequestBodyException.class, finalA::execute); - assertEquals("\"" + invalidName + "\" is not acceptable to TEAMMATES as a/an person name because " - + "it contains invalid characters. A/An person name must start with an " - + "alphanumeric character, and cannot contain any vertical bar (|) or percent sign (%).", - ex.getMessage()); - - verifyNoEmailsSent(); verifyNoTasksAdded(); } @Override @Test protected void testAccessControl() { - verifyOnlyAdminCanAccess(); + verifyAnyLoggedInUserCanAccess(); } @Test @@ -170,13 +146,4 @@ private String generateNextDemoCourseId(String instructorEmailOrProposedCourseId return a.generateNextDemoCourseId(instructorEmailOrProposedCourseId, maximumIdLength); } - private AccountCreateRequest buildCreateRequest(String name, String institution, String email) { - AccountCreateRequest req = new AccountCreateRequest(); - - req.setInstructorName(name); - req.setInstructorInstitution(institution); - req.setInstructorEmail(email); - - return req; - } } diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java new file mode 100644 index 00000000000..5d31c8278cf --- /dev/null +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -0,0 +1,127 @@ +package teammates.ui.webapi; + +import org.apache.http.HttpStatus; +import org.testng.annotations.Test; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.InvalidHttpRequestBodyException; +import teammates.common.util.Config; +import teammates.common.util.Const; +import teammates.common.util.EmailType; +import teammates.common.util.EmailWrapper; +import teammates.common.util.StringHelper; +import teammates.ui.output.JoinLinkData; +import teammates.ui.request.AccountCreateRequest; + +/** + * SUT: {@link CreateAccountRequestAction}. + */ +public class CreateAccountRequestActionTest extends BaseActionTest { + + @Override + protected String getActionUri() { + return Const.ResourceURIs.ACCOUNT_REQUEST; + } + + @Override + protected String getRequestMethod() { + return POST; + } + + @Override + @Test + protected void testExecute() throws Exception { + loginAsAdmin(); + String name = "JamesBond"; + String email = "jamesbond89@gmail.tmt"; + String institute = "TEAMMATES Test Institute 1"; + + ______TS("Not enough parameters"); + + verifyHttpParameterFailure(); + + ______TS("Null parameters"); + + Exception ex = assertThrows(InvalidHttpRequestBodyException.class, + () -> getAction(buildCreateRequest(null, institute, email)).execute()); + assertEquals("name cannot be null", ex.getMessage()); + + ex = assertThrows(InvalidHttpRequestBodyException.class, + () -> getAction(buildCreateRequest(name, null, email)).execute()); + assertEquals("institute cannot be null", ex.getMessage()); + + ex = assertThrows(InvalidHttpRequestBodyException.class, + () -> getAction(buildCreateRequest(name, institute, null)).execute()); + assertEquals("email cannot be null", ex.getMessage()); + + verifyNoTasksAdded(); + + ______TS("Normal case"); + + String nameWithSpaces = " " + name + " "; + String emailWithSpaces = " " + email + " "; + String instituteWithSpaces = " " + institute + " "; + + AccountCreateRequest req = buildCreateRequest(nameWithSpaces, instituteWithSpaces, emailWithSpaces); + CreateAccountRequestAction a = getAction(req); + JsonResult r = getJsonResult(a); + + assertEquals(HttpStatus.SC_OK, r.getStatusCode()); + + AccountRequestAttributes accountRequestAttributes = logic.getAccountRequest(email); + + assertEquals(name, accountRequestAttributes.getName()); + assertEquals(email, accountRequestAttributes.getEmail()); + assertEquals(institute, accountRequestAttributes.getInstitute()); + assertNotNull(accountRequestAttributes.getRegistrationKey()); + + String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE) + .withRegistrationKey(StringHelper.encrypt(accountRequestAttributes.getRegistrationKey())) + .toAbsoluteString(); + + JoinLinkData output = (JoinLinkData) r.getOutput(); + assertEquals(joinLink, output.getJoinLink()); + + verifyNumberOfEmailsSent(1); + + EmailWrapper emailSent = mockEmailSender.getEmailsSent().get(0); + assertEquals(String.format(EmailType.NEW_INSTRUCTOR_ACCOUNT.getSubject(), name), + emailSent.getSubject()); + assertEquals(email, emailSent.getRecipient()); + + ______TS("Error: invalid parameter"); + + String invalidName = "James%20Bond99"; + + req = buildCreateRequest(invalidName, institute, emailWithSpaces); + + final CreateAccountRequestAction finalA = getAction(req); + + ex = assertThrows(InvalidHttpRequestBodyException.class, finalA::execute); + assertEquals("\"" + invalidName + "\" is not acceptable to TEAMMATES as a/an person name because " + + "it contains invalid characters. A/An person name must start with an " + + "alphanumeric character, and cannot contain any vertical bar (|) or percent sign (%).", + ex.getMessage()); + + verifyNoEmailsSent(); + verifyNoTasksAdded(); + + } + + @Override + @Test + protected void testAccessControl() { + verifyOnlyAdminCanAccess(); + } + + private AccountCreateRequest buildCreateRequest(String name, String institution, String email) { + AccountCreateRequest req = new AccountCreateRequest(); + + req.setInstructorName(name); + req.setInstructorInstitution(institution); + req.setInstructorEmail(email); + + return req; + } + +} diff --git a/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java b/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java index e077d00cfdc..a05124ab49a 100644 --- a/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java @@ -89,6 +89,7 @@ protected void testExecute() { BinCourseAction.class, DeleteAccountAction.class, CreateAccountAction.class, + CreateAccountRequestAction.class, GetAccountAction.class, FeedbackSessionPublishedRemindersAction.class, QueryLogsAction.class, From 2ccf79428c49e25d8f6c0738ff9526025e3ec746 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 16:41:18 +0800 Subject: [PATCH 09/79] Fix frontend lint errors --- ...user-create-account-page.component.spec.ts | 74 +++++++++---------- .../app/user-create-account-page.component.ts | 44 +++++------ .../app/user-create-account-page.module.ts | 2 +- src/web/services/account.service.spec.ts | 2 +- src/web/services/account.service.ts | 6 +- 5 files changed, 63 insertions(+), 65 deletions(-) diff --git a/src/web/app/user-create-account-page.component.spec.ts b/src/web/app/user-create-account-page.component.spec.ts index 12335a67c74..6d1e6f11209 100644 --- a/src/web/app/user-create-account-page.component.spec.ts +++ b/src/web/app/user-create-account-page.component.spec.ts @@ -1,16 +1,16 @@ -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { async, ComponentFixture, TestBed } from "@angular/core/testing"; -import { ActivatedRoute } from "@angular/router"; -import { RouterTestingModule } from "@angular/router/testing"; -import { of } from "rxjs"; -import { AccountService } from "../services/account.service"; -import { AuthService } from "../services/auth.service"; -import { NavigationService } from "../services/navigation.service"; -import { LoadingSpinnerModule } from "./components/loading-spinner/loading-spinner.module"; -import { UserCreateAccountPageComponent } from "./user-create-account-page.component"; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import { AccountService } from '../services/account.service'; +import { AuthService } from '../services/auth.service'; +import { NavigationService } from '../services/navigation.service'; +import { LoadingSpinnerModule } from './components/loading-spinner/loading-spinner.module'; +import { UserCreateAccountPageComponent } from './user-create-account-page.component'; import Spy = jasmine.Spy; -describe("UserCreateAccountPageComponent", () => { +describe('UserCreateAccountPageComponent', () => { let component: UserCreateAccountPageComponent; let fixture: ComponentFixture; let navService: NavigationService; @@ -33,7 +33,7 @@ describe("UserCreateAccountPageComponent", () => { provide: ActivatedRoute, useValue: { queryParams: of({ - key: "key", + key: 'key', }), }, }, @@ -50,17 +50,17 @@ describe("UserCreateAccountPageComponent", () => { fixture.detectChanges(); }); - it("should create", () => { + it('should create', () => { expect(component).toBeTruthy(); }); - it("should snap with default fields", () => { + it('should snap with default fields', () => { expect(fixture).toMatchSnapshot(); }); - it("should snap if user has not created an account and has a valid url", () => { + it('should snap if user has not created an account and has a valid url', () => { component.hasJoined = false; - component.userId = ""; + component.userId = ''; component.validUrl = true; component.isLoading = false; @@ -69,7 +69,7 @@ describe("UserCreateAccountPageComponent", () => { expect(fixture).toMatchSnapshot(); }); - it("should snap with invalid url", () => { + it('should snap with invalid url', () => { component.validUrl = false; component.isLoading = false; @@ -78,65 +78,65 @@ describe("UserCreateAccountPageComponent", () => { expect(fixture).toMatchSnapshot(); }); - it("should create account when create account button is clicked on", () => { - const params: string[] = ["key"]; + it('should create account when create account button is clicked on', () => { + const params: string[] = ['key']; component.isLoading = false; - component.userId = "user"; + component.userId = 'user'; component.key = params[0]; component.validUrl = true; const accountSpy: Spy = spyOn( accountService, - "createAccount" + 'createAccount', ).and.returnValue(of({})); - const navSpy: Spy = spyOn(navService, "navigateByURL"); + const navSpy: Spy = spyOn(navService, 'navigateByURL'); fixture.detectChanges(); const btn: any = - fixture.debugElement.nativeElement.querySelector("#btn-confirm"); + fixture.debugElement.nativeElement.querySelector('#btn-confirm'); btn.click(); expect(accountSpy.calls.count()).toEqual(1); expect(accountSpy.calls.mostRecent().args).toEqual(params); expect(navSpy.calls.count()).toEqual(1); - expect(navSpy.calls.mostRecent().args[1]).toEqual(`/web/instructor`); + expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor'); }); - it("should redirect user to home page if user already has account", () => { - spyOn(authService, "getAuthUser").and.returnValue( + it('should redirect user to home page if user already has account', () => { + spyOn(authService, 'getAuthUser').and.returnValue( of({ user: { - id: "user", + id: 'user', isInstructor: true, }, - }) + }), ); - const navSpy: Spy = spyOn(navService, "navigateByURL"); + const navSpy: Spy = spyOn(navService, 'navigateByURL'); component.ngOnInit(); - expect(component.userId).toEqual("user"); + expect(component.userId).toEqual('user'); expect(navSpy.calls.count()).toEqual(1); - expect(navSpy.calls.mostRecent().args[1]).toEqual("/web/instructor"); + expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor'); }); - it("should stop loading if user is not logged in", () => { - spyOn(authService, "getAuthUser").and.returnValue(of({})); + it('should stop loading if user is not logged in', () => { + spyOn(authService, 'getAuthUser').and.returnValue(of({})); component.ngOnInit(); expect(component.isLoading).toBeFalsy(); }); - it("should stop loading if user does not have an account", () => { - spyOn(authService, "getAuthUser").and.returnValue( + it('should stop loading if user does not have an account', () => { + spyOn(authService, 'getAuthUser').and.returnValue( of({ user: { - id: "user", + id: 'user', isInstructor: false, }, - }) + }), ); component.ngOnInit(); diff --git a/src/web/app/user-create-account-page.component.ts b/src/web/app/user-create-account-page.component.ts index cd4e5c1d451..79c57f1b3b6 100644 --- a/src/web/app/user-create-account-page.component.ts +++ b/src/web/app/user-create-account-page.component.ts @@ -1,28 +1,28 @@ -import { Component, OnInit } from "@angular/core"; -import { ActivatedRoute, Router } from "@angular/router"; -import { finalize } from "rxjs/operators"; -import { AccountService } from "../services/account.service"; -import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; -import { AuthService } from "../services/auth.service"; -import { AuthInfo } from "../types/api-output"; -import { ErrorMessageOutput } from "./error-message-output"; -import { NavigationService } from "../services/navigation.service"; -import { ErrorReportComponent } from "./components/error-report/error-report.component"; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { finalize } from 'rxjs/operators'; +import { AccountService } from '../services/account.service'; +import { AuthService } from '../services/auth.service'; +import { NavigationService } from '../services/navigation.service'; +import { AuthInfo, MessageOutput } from '../types/api-output'; +import { ErrorReportComponent } from './components/error-report/error-report.component'; +import { ErrorMessageOutput } from './error-message-output'; /** * User create account page component. */ @Component({ - selector: "tm-user-create-account-page", - templateUrl: "./user-create-account-page.component.html", - styleUrls: ["./user-create-account-page.component.scss"], + selector: 'tm-user-create-account-page', + templateUrl: './user-create-account-page.component.html', + styleUrls: ['./user-create-account-page.component.scss'], }) export class UserCreateAccountPageComponent implements OnInit { isLoading: boolean = true; hasJoined: boolean = false; validUrl: boolean = true; - key: string = ""; - userId: string = ""; + key: string = ''; + userId: string = ''; constructor( private route: ActivatedRoute, @@ -30,7 +30,7 @@ export class UserCreateAccountPageComponent implements OnInit { private navigationService: NavigationService, private accountService: AccountService, private authService: AuthService, - private ngbModal: NgbModal + private ngbModal: NgbModal, ) {} ngOnInit(): void { @@ -44,11 +44,11 @@ export class UserCreateAccountPageComponent implements OnInit { } this.authService.getAuthUser().subscribe((resp: AuthInfo) => { - this.userId = resp.user?.id || ""; - + this.userId = resp.user?.id || ''; + if (resp.user?.isInstructor) { // User already has instructor account - this.navigationService.navigateByURL(this.router, `/web/instructor`); + this.navigationService.navigateByURL(this.router, '/web/instructor'); } this.isLoading = false; @@ -62,14 +62,14 @@ export class UserCreateAccountPageComponent implements OnInit { .createAccount(this.key) .pipe(finalize(() => (this.isLoading = false))) .subscribe( - (_resp) => { - this.navigationService.navigateByURL(this.router, `/web/instructor`); + (_resp: MessageOutput) => { + this.navigationService.navigateByURL(this.router, '/web/instructor'); }, (resp: ErrorMessageOutput) => { const modalRef: any = this.ngbModal.open(ErrorReportComponent); modalRef.componentInstance.requestId = resp.error.requestId; modalRef.componentInstance.errorMessage = resp.error.message; - } + }, ); } } diff --git a/src/web/app/user-create-account-page.module.ts b/src/web/app/user-create-account-page.module.ts index 92c2b5ef5c4..1606c449d64 100644 --- a/src/web/app/user-create-account-page.module.ts +++ b/src/web/app/user-create-account-page.module.ts @@ -24,7 +24,7 @@ const routes: Routes = [ imports: [ CommonModule, RouterModule.forChild(routes), - LoadingSpinnerModule + LoadingSpinnerModule, ], }) export class UserCreateAccountPageModule { } diff --git a/src/web/services/account.service.spec.ts b/src/web/services/account.service.spec.ts index 173726c398a..f9046cff960 100644 --- a/src/web/services/account.service.spec.ts +++ b/src/web/services/account.service.spec.ts @@ -42,7 +42,7 @@ describe('AccountService', () => { }); it('should execute POST on account endpoint', () => { - const testKey = 'testKey'; + const testKey: string = 'testKey'; const paramMap: Record = { key: testKey, }; diff --git a/src/web/services/account.service.ts b/src/web/services/account.service.ts index fd128ef077a..e65362a3516 100644 --- a/src/web/services/account.service.ts +++ b/src/web/services/account.service.ts @@ -18,10 +18,8 @@ export class AccountService { /** * Creates an account by calling API. */ - createAccount(key: string): Observable { - const paramMap: Record = { - key: key - }; + createAccount(key: string): Observable { + const paramMap: Record = { key }; return this.httpRequestService.post(ResourceEndpoints.ACCOUNT, paramMap); } From d1a4ab910c9344554f96d62ac84560d2068540d6 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 17:06:12 +0800 Subject: [PATCH 10/79] Fix backend lint errors --- .../attributes/AccountRequestAttributes.java | 7 ++-- src/main/java/teammates/logic/api/Logic.java | 14 ++++---- .../logic/core/AccountRequestsLogic.java | 6 ---- .../storage/api/AccountRequestsDb.java | 10 ++---- .../ui/output/AccountRequestData.java | 1 - .../ui/webapi/CreateAccountAction.java | 10 +++--- .../ui/webapi/CreateAccountRequestAction.java | 4 +-- .../AccountRequestAttributesTest.java | 8 ++--- .../logic/core/AccountRequestsLogicTest.java | 14 ++++---- .../storage/api/AccountRequestsDbTest.java | 14 ++++---- .../java/teammates/test/AbstractBackDoor.java | 32 +++++++++---------- .../test/BaseTestCaseWithDatabaseAccess.java | 2 +- .../ui/webapi/CreateAccountActionTest.java | 15 +++------ .../CreateAccountRequestActionTest.java | 4 +-- 14 files changed, 63 insertions(+), 78 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 4a6a742d22e..a8d3ed4c725 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -113,12 +113,13 @@ public List getInvalidityInfo() { @Override public AccountRequest toEntity() { - return new AccountRequest(getEmail(), getRegistrationKey(), getName(), getInstitute(), getCreatedAt(), getDeletedAt()); + return new AccountRequest(getEmail(), getRegistrationKey(), getName(), + getInstitute(), getCreatedAt(), getDeletedAt()); } @Override public String toString() { - return "[" + AccountRequestAttributes.class.getSimpleName() + "] email: " + getEmail() + return "[" + AccountRequestAttributes.class.getSimpleName() + "] email: " + getEmail() + "registrationKey: " + getRegistrationKey() + " name: " + getName() + " institute: " + getInstitute(); } @@ -148,6 +149,8 @@ public boolean equals(Object other) { @Override public void sanitizeForSaving() { this.institute = SanitizationHelper.sanitizeTitle(institute); + this.name = SanitizationHelper.sanitizeName(name); + this.email = SanitizationHelper.sanitizeEmail(email); } /** diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 7eb8c278cdc..6fd7e466bbe 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -1388,13 +1388,13 @@ public boolean isStudentsInSameTeam(String courseId, String student1Email, Strin public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) throws InvalidParametersException { assert accountRequestToAdd != null; - + return accountRequestsLogic.createOrUpdateAccountRequest(accountRequestToAdd); } /** * Deletes an account request. - * + * *

Preconditions:

* * All parameters are non-null. */ @@ -1406,10 +1406,10 @@ public void deleteAccountRequest(String email) { /** * Gets an account request by unique constraint registrationKey. - * + * *

Preconditions:

* * All parameters are non-null. - * + * * @return the account request or null if no match found */ public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { @@ -1420,10 +1420,10 @@ public AccountRequestAttributes getAccountRequestForRegistrationKey(String regis /** * Gets an account request by email address. - * + * *

Preconditions:

* * All parameters are non-null. - * + * * @return the account request or null if no match found */ public AccountRequestAttributes getAccountRequest(String email) { @@ -1431,5 +1431,5 @@ public AccountRequestAttributes getAccountRequest(String email) { return accountRequestsLogic.getAccountRequest(email); } - + } diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index 9d7ffb1981d..ae3dd72be68 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -25,12 +25,6 @@ void initLogicDependencies() { // No dependency to other logic class } - /** - * Gets account request associated with the {@code registrationKey}. - * - * @return null if no match found. - */ - /** * Creates or updates an account request. * diff --git a/src/main/java/teammates/storage/api/AccountRequestsDb.java b/src/main/java/teammates/storage/api/AccountRequestsDb.java index e31b71a51e8..987a9d9fd5d 100644 --- a/src/main/java/teammates/storage/api/AccountRequestsDb.java +++ b/src/main/java/teammates/storage/api/AccountRequestsDb.java @@ -76,10 +76,10 @@ AccountRequestAttributes makeAttributes(AccountRequest entity) { * @return updated account request * @throws InvalidParametersException if attributes to update are not valid */ - public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) + public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) throws InvalidParametersException { assert accountRequestToAdd != null; - + accountRequestToAdd.sanitizeForSaving(); if (!accountRequestToAdd.isValid()) { throw new InvalidParametersException(accountRequestToAdd.getInvalidityInfo()); @@ -97,13 +97,9 @@ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttri public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { assert registrationKey != null; - AccountRequestAttributes accountRequest = makeAttributesOrNull( - getAccountRequestEntityForRegistrationKey(registrationKey.trim())); - - return accountRequest; + return makeAttributesOrNull(getAccountRequestEntityForRegistrationKey(registrationKey.trim())); } - private AccountRequest getAccountRequestEntityForRegistrationKey(String key) { List accountRequestList = load().filter("registrationKey =", key).list(); diff --git a/src/main/java/teammates/ui/output/AccountRequestData.java b/src/main/java/teammates/ui/output/AccountRequestData.java index 459910f87be..83f72dc7006 100644 --- a/src/main/java/teammates/ui/output/AccountRequestData.java +++ b/src/main/java/teammates/ui/output/AccountRequestData.java @@ -29,7 +29,6 @@ public String getEmail() { return email; } - public long getCreatedAtTimeStamp() { return createdAtTimeStamp; } diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 6c839247de6..2a812b7cdeb 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -36,7 +36,7 @@ void checkSpecificAccessControl() { @Override public JsonResult execute() { String registrationKey; - + try { registrationKey = StringHelper.decrypt(getNonNullRequestParamValue(Const.ParamsNames.REGKEY)); } catch (InvalidParametersException e) { @@ -44,7 +44,7 @@ public JsonResult execute() { } AccountRequestAttributes accountRequestAttributes = logic.getAccountRequestForRegistrationKey(registrationKey); - + if (accountRequestAttributes == null) { return new JsonResult("Invalid registration key", HttpStatus.SC_BAD_REQUEST); } @@ -60,9 +60,9 @@ public JsonResult execute() { } catch (InvalidParametersException e) { return new JsonResult(e.getMessage(), HttpStatus.SC_BAD_REQUEST); } - + List instructorList = logic.getInstructorsForCourse(courseId); - + try { logic.joinCourseForInstructor(instructorList.get(0).getEncryptedKey(), userInfo.id, instructorInstitution, StringHelper.generateSignature(instructorInstitution)); @@ -76,7 +76,7 @@ public JsonResult execute() { // Delete account request as it is no longer needed logic.deleteAccountRequest(instructorEmail); - + return new JsonResult("Account successfully created", HttpStatus.SC_OK); } diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index 0a01bc66877..43372fd4f1d 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -43,9 +43,9 @@ public JsonResult execute() { String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE) .withRegistrationKey(StringHelper.encrypt(registrationKey)) .toAbsoluteString(); - + EmailWrapper email = emailGenerator.generateNewInstructorAccountJoinEmail( - instructorEmail, instructorName, joinLink); + instructorEmail, instructorName, joinLink); emailSender.sendEmail(email); JoinLinkData output = new JoinLinkData(joinLink); diff --git a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java index 66bd8ba79cd..b82b242dd74 100644 --- a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java +++ b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java @@ -17,7 +17,7 @@ public class AccountRequestAttributesTest extends BaseTestCase { @Test public void testValueOf_withTypicalData_shouldGenerateAttributesCorrectly() { Instant typicalInstant = Instant.now(); - AccountRequest accountRequest = + AccountRequest accountRequest = new AccountRequest("valid@test.com", "registrationkey", "Valid Name", "Valid Institute", typicalInstant, typicalInstant); @@ -55,7 +55,7 @@ public void testBuilder_withTypicalData_shouldBuildCorrectAttributes() { @Test public void testBuilder_buildNothing_shouldUseDefaultValues() { - AccountRequestAttributes accountRequestAttributes = + AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes.builder("valid@test.com").build(); assertEquals("valid@test.com", accountRequestAttributes.getEmail()); @@ -141,10 +141,10 @@ public void testToString() { @Test public void testEquals() { - AccountRequestAttributes accountRequest = generateValidAccountRequestAttributesObject(); + AccountRequestAttributes accountRequest = generateValidAccountRequestAttributesObject(); // When the two account requests have same values - AccountRequestAttributes similarAccountRequest = generateValidAccountRequestAttributesObject(); + AccountRequestAttributes similarAccountRequest = generateValidAccountRequestAttributesObject(); assertTrue(accountRequest.equals(similarAccountRequest)); diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 35529145c60..5ed2280ff75 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -66,7 +66,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ipe.getMessage()); ______TS("failure: null parameter"); - assertThrows(AssertionError.class, + assertThrows(AssertionError.class, () -> accountRequestsLogic.createOrUpdateAccountRequest(null)); } @@ -114,19 +114,19 @@ public void testgetAccountRequestForRegistrationKey() throws Exception { ______TS("typical success case"); - AccountRequestAttributes accountRequestAttributes = + AccountRequestAttributes accountRequestAttributes = accountRequestsLogic.getAccountRequestForRegistrationKey(a.getRegistrationKey()); assertEquals(a, accountRequestAttributes); ______TS("account request not found"); - AccountRequestAttributes notFoundRequestAttributes = + AccountRequestAttributes notFoundRequestAttributes = accountRequestsLogic.getAccountRequestForRegistrationKey("not-found"); assertNull(notFoundRequestAttributes); ______TS("failure null parameter"); - assertThrows(AssertionError.class, + assertThrows(AssertionError.class, () -> accountRequestsLogic.getAccountRequestForRegistrationKey(null)); } @@ -142,19 +142,19 @@ public void testGetAccountRequest() throws Exception { ______TS("typical success case"); - AccountRequestAttributes accountRequestAttributes = + AccountRequestAttributes accountRequestAttributes = accountRequestsLogic.getAccountRequest(a.getEmail()); assertEquals(a, accountRequestAttributes); ______TS("account request not found"); - AccountRequestAttributes notFoundRequestAttributes = + AccountRequestAttributes notFoundRequestAttributes = accountRequestsLogic.getAccountRequest("not-found@test.com"); assertNull(notFoundRequestAttributes); ______TS("failure null parameter"); - assertThrows(AssertionError.class, + assertThrows(AssertionError.class, () -> accountRequestsLogic.getAccountRequest(null)); } diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 78520c33fe5..bc7d5eee826 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -52,7 +52,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ipe.getMessage()); ______TS("failure: null parameter"); - assertThrows(AssertionError.class, + assertThrows(AssertionError.class, () -> accountRequestsDb.createOrUpdateAccountRequest(null)); } @@ -100,19 +100,19 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { ______TS("typical success case"); - AccountRequestAttributes accountRequestAttributes = + AccountRequestAttributes accountRequestAttributes = accountRequestsDb.getAccountRequestForRegistrationKey(a.getRegistrationKey()); assertEquals(a, accountRequestAttributes); ______TS("account request not found"); - AccountRequestAttributes notFoundRequestAttributes = + AccountRequestAttributes notFoundRequestAttributes = accountRequestsDb.getAccountRequestForRegistrationKey("not-found"); assertNull(notFoundRequestAttributes); ______TS("failure null parameter"); - assertThrows(AssertionError.class, + assertThrows(AssertionError.class, () -> accountRequestsDb.getAccountRequestForRegistrationKey(null)); } @@ -128,19 +128,19 @@ public void testGetAccountRequest() throws Exception { ______TS("typical success case"); - AccountRequestAttributes accountRequestAttributes = + AccountRequestAttributes accountRequestAttributes = accountRequestsDb.getAccountRequest(a.getEmail()); assertEquals(a, accountRequestAttributes); ______TS("account request not found"); - AccountRequestAttributes notFoundRequestAttributes = + AccountRequestAttributes notFoundRequestAttributes = accountRequestsDb.getAccountRequest("not-found@test.com"); assertNull(notFoundRequestAttributes); ______TS("failure null parameter"); - assertThrows(AssertionError.class, + assertThrows(AssertionError.class, () -> accountRequestsDb.getAccountRequest(null)); } diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 7ca62e84e96..1c8c0e83edb 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -730,18 +730,6 @@ public void deleteCourse(String courseId) { executeDeleteRequest(Const.ResourceURIs.COURSE, params); } - private static final class ResponseBodyAndCode { - - String responseBody; - int responseCode; - - ResponseBodyAndCode(String responseBody, int responseCode) { - this.responseBody = responseBody; - this.responseCode = responseCode; - } - - } - /** * Gets account request data from the database. */ @@ -763,7 +751,7 @@ public AccountRequestAttributes getAccountRequest(String email) { if (accountRequestData == null) { return null; } - AccountRequestAttributes.Builder builder = + AccountRequestAttributes.Builder builder = AccountRequestAttributes.builder(accountRequestData.getEmail()); if (accountRequestData.getName() != null) { @@ -776,8 +764,20 @@ public AccountRequestAttributes getAccountRequest(String email) { builder.withInstitute(accountRequestData.getInstitute()); } - AccountRequestAttributes accountRequest = builder.build(); - - return accountRequest; + return builder.build(); + + } + + private static final class ResponseBodyAndCode { + + String responseBody; + int responseCode; + + ResponseBodyAndCode(String responseBody, int responseCode) { + this.responseBody = responseBody; + this.responseCode = responseCode; + } + } + } diff --git a/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java b/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java index 85053dec096..366988cab7f 100644 --- a/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java +++ b/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java @@ -151,7 +151,7 @@ private void verifyEquals(EntityAttributes expected, EntityAttributes actu StudentAttributes actualStudent = (StudentAttributes) actual; equalizeIrrelevantData(expectedStudent, actualStudent); assertEquals(JsonUtils.toJson(expectedStudent), JsonUtils.toJson(actualStudent)); - + } else if (expected instanceof AccountRequestAttributes) { AccountRequestAttributes expectedAccountRequest = (AccountRequestAttributes) expected; AccountRequestAttributes actualAccountRequest = (AccountRequestAttributes) actual; diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index 1cb495bbff6..6cd95fb9367 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -14,8 +14,6 @@ import teammates.common.util.FieldValidator; import teammates.common.util.StringHelper; import teammates.common.util.StringHelperExtension; -import teammates.storage.api.AccountRequestsDb; -import teammates.ui.request.AccountCreateRequest; /** * SUT: {@link CreateAccountAction}. @@ -40,19 +38,16 @@ protected void testExecute() throws Exception { String institute = "TEAMMATES Test Institute 1"; String regKey = "validregkey123"; - AccountRequestsDb accountRequestsDb = AccountRequestsDb.inst(); - accountRequestsDb.createEntity(AccountRequestAttributes.builder(email).withName(name) + logic.createOrUpdateAccountRequest(AccountRequestAttributes.builder(email).withName(name) .withInstitute(institute).withRegistrationKey(regKey).build()); ______TS("Not enough parameters"); verifyHttpParameterFailure(); - + ______TS("Null parameters"); - String[] nullParams = new String[] { - Const.ParamsNames.REGKEY, null, - }; + String[] nullParams = new String[] { Const.ParamsNames.REGKEY, null, }; Exception ex = assertThrows(NullHttpParameterException.class, () -> getAction(nullParams).execute()); assertEquals("The [key] HTTP parameter is null.", ex.getMessage()); @@ -61,9 +56,7 @@ protected void testExecute() throws Exception { ______TS("Normal case"); - String[] params = new String[] { - Const.ParamsNames.REGKEY, StringHelper.encrypt(regKey), - }; + String[] params = new String[] { Const.ParamsNames.REGKEY, StringHelper.encrypt(regKey), }; CreateAccountAction a = getAction(params); JsonResult r = getJsonResult(a); diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index 5d31c8278cf..c28c6c7dd25 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -39,7 +39,7 @@ protected void testExecute() throws Exception { ______TS("Not enough parameters"); verifyHttpParameterFailure(); - + ______TS("Null parameters"); Exception ex = assertThrows(InvalidHttpRequestBodyException.class, @@ -78,7 +78,7 @@ protected void testExecute() throws Exception { String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE) .withRegistrationKey(StringHelper.encrypt(accountRequestAttributes.getRegistrationKey())) .toAbsoluteString(); - + JoinLinkData output = (JoinLinkData) r.getOutput(); assertEquals(joinLink, output.getJoinLink()); From 95314524c3c1e60737ce0bba43ffae10ee29de95 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 18:17:44 +0800 Subject: [PATCH 11/79] Fix e2e tests --- .../java/teammates/e2e/cases/AdminHomePageE2ETest.java | 8 -------- src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java | 4 +++- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java b/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java index 46da0fc2053..6c3f36af219 100644 --- a/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java @@ -2,7 +2,6 @@ import org.testng.annotations.Test; -import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.util.AppUrl; import teammates.common.util.Const; import teammates.e2e.pageobjects.AdminHomePage; @@ -26,9 +25,6 @@ public void testAll() { String name = "AHPUiT Instrúctör WithPlusInEmail"; String email = "AHPUiT+++_.instr1!@gmail.tmt"; String institute = "TEAMMATES Test Institute 1"; - String demoCourseId = "AHPUiT____.instr1_.gma-demo"; - - BACKDOOR.deleteCourse(demoCourseId); homePage.queueInstructorForAdding(name, email, institute); @@ -42,14 +38,10 @@ public void testAll() { assertTrue(successMessage.contains( "Instructor \"AHPUiT Instrúctör WithPlusInEmail\" has been successfully created")); - CourseAttributes demoCourse = getCourse(demoCourseId); - assertNotNull(demoCourse); - String failureMessage = homePage.getMessageForInstructor(1); assertTrue(failureMessage.contains( "\"invalidemail\" is not acceptable to TEAMMATES as a/an email because it is not in the correct format.")); - BACKDOOR.deleteCourse(demoCourseId); } } diff --git a/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java b/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java index 2e937dd6ad9..27917916fa5 100644 --- a/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java +++ b/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java @@ -301,7 +301,9 @@ String getKeyForStudent(StudentAttributes student) { @Override protected AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest) { - return BACKDOOR.getAccountRequest(accountRequest.getEmail()); + assert false: "This method has no implementation"; + + return null; } @Override From 8f5624242d8a62e89c5fc1e5e857c6dd28589e6d Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 18:18:04 +0800 Subject: [PATCH 12/79] Add account requests to data bundle --- .../common/datatransfer/DataBundle.java | 2 + .../teammates/logic/core/DataBundleLogic.java | 6 +++ .../java/teammates/test/AbstractBackDoor.java | 38 ------------------- .../resources/data/typicalDataBundle.json | 8 ++++ 4 files changed, 16 insertions(+), 38 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/DataBundle.java b/src/main/java/teammates/common/datatransfer/DataBundle.java index 5de250faf8d..2286f55592b 100644 --- a/src/main/java/teammates/common/datatransfer/DataBundle.java +++ b/src/main/java/teammates/common/datatransfer/DataBundle.java @@ -4,6 +4,7 @@ import java.util.Map; import teammates.common.datatransfer.attributes.AccountAttributes; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; import teammates.common.datatransfer.attributes.FeedbackResponseAttributes; @@ -21,6 +22,7 @@ // CHECKSTYLE.OFF:JavadocVariable each field represents different entity types public class DataBundle { public Map accounts = new LinkedHashMap<>(); + public Map accountRequests = new LinkedHashMap<>(); public Map courses = new LinkedHashMap<>(); public Map instructors = new LinkedHashMap<>(); public Map students = new LinkedHashMap<>(); diff --git a/src/main/java/teammates/logic/core/DataBundleLogic.java b/src/main/java/teammates/logic/core/DataBundleLogic.java index 7eea979705e..2ed08a71b67 100644 --- a/src/main/java/teammates/logic/core/DataBundleLogic.java +++ b/src/main/java/teammates/logic/core/DataBundleLogic.java @@ -11,6 +11,7 @@ import teammates.common.datatransfer.DataBundle; import teammates.common.datatransfer.InstructorPrivileges; import teammates.common.datatransfer.attributes.AccountAttributes; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.EntityAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; @@ -24,6 +25,7 @@ import teammates.common.exception.SearchServiceException; import teammates.common.util.Const; import teammates.common.util.StringHelper; +import teammates.storage.api.AccountRequestsDb; import teammates.storage.api.AccountsDb; import teammates.storage.api.CoursesDb; import teammates.storage.api.FeedbackQuestionsDb; @@ -44,6 +46,7 @@ public final class DataBundleLogic { private static final DataBundleLogic instance = new DataBundleLogic(); private final AccountsDb accountsDb = AccountsDb.inst(); + private final AccountRequestsDb accountRequestsDb = AccountRequestsDb.inst(); private final ProfilesDb profilesDb = ProfilesDb.inst(); private final CoursesDb coursesDb = CoursesDb.inst(); private final StudentsDb studentsDb = StudentsDb.inst(); @@ -78,6 +81,7 @@ public DataBundle persistDataBundle(DataBundle dataBundle) throws InvalidParamet } Collection accounts = dataBundle.accounts.values(); + Collection accountRequests = dataBundle.accountRequests.values(); Collection profiles = dataBundle.profiles.values(); Collection courses = dataBundle.courses.values(); Collection instructors = dataBundle.instructors.values(); @@ -98,6 +102,7 @@ public DataBundle persistDataBundle(DataBundle dataBundle) throws InvalidParamet processQuestions(questions); List newAccounts = accountsDb.putEntities(googleIdAccountMap.values()); + List newAccountRequests = accountRequestsDb.putEntities(accountRequests); List newProfiles = profilesDb.putEntities(profiles); List newCourses = coursesDb.putEntities(courses); @@ -112,6 +117,7 @@ public DataBundle persistDataBundle(DataBundle dataBundle) throws InvalidParamet List newFeedbackResponseComments = fcDb.putEntities(responseComments); updateDataBundleValue(newAccounts, dataBundle.accounts); + updateDataBundleValue(newAccountRequests, dataBundle.accountRequests); updateDataBundleValue(newProfiles, dataBundle.profiles); updateDataBundleValue(newCourses, dataBundle.courses); updateDataBundleValue(newInstructors, dataBundle.instructors); diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 1c8c0e83edb..1d5d213c763 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -730,44 +730,6 @@ public void deleteCourse(String courseId) { executeDeleteRequest(Const.ResourceURIs.COURSE, params); } - /** - * Gets account request data from the database. - */ - public AccountRequestData getAccountRequestData(String email) { - Map params = new HashMap<>(); - params.put(Const.ParamsNames.EMAIL, email); - ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.ACCOUNT_REQUEST, params); - if (response.responseCode == HttpStatus.SC_NOT_FOUND) { - return null; - } - return JsonUtils.fromJson(response.responseBody, AccountData.class); - } - - /** - * Get account request from database. - */ - public AccountRequestAttributes getAccountRequest(String email) { - AccountRequestData accountRequestData = getAccountRequestData(email); - if (accountRequestData == null) { - return null; - } - AccountRequestAttributes.Builder builder = - AccountRequestAttributes.builder(accountRequestData.getEmail()); - - if (accountRequestData.getName() != null) { - builder.withName(accountRequestData.getName()); - } - if (accountRequestData.getRegistrationKey() != null) { - builder.withRegistrationKey(accountRequestData.getRegistrationKey()); - } - if (accountRequestData.getInstitute() != null) { - builder.withInstitute(accountRequestData.getInstitute()); - } - - return builder.build(); - - } - private static final class ResponseBodyAndCode { String responseBody; diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index 67aca773567..53ab13b6a2d 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1705,5 +1705,13 @@ "gender": "OTHER", "moreInfo": "I am just a student :P" } + }, + "accountRequests": { + "accountRequest1": { + "email": "typical@gmail.tmt", + "institute": "TEAMMATES Test Institute 1", + "registrationKey": "typical@gmail.tmt12345", + "name": "Typical Instructor Name" + } } } From f70d0a28b7f319268d044d1589028e18919d65dc Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 18:55:00 +0800 Subject: [PATCH 13/79] Update account requests logic test --- .../attributes/AccountRequestAttributes.java | 2 +- .../teammates/logic/core/DataBundleLogic.java | 3 +++ .../logic/core/AccountRequestsLogicTest.java | 24 +++---------------- .../resources/data/typicalDataBundle.json | 5 ++-- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index a8d3ed4c725..e1d4d149160 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -17,7 +17,7 @@ public class AccountRequestAttributes extends EntityAttributes { private String email; private String name; private String institute; - private transient String registrationKey; + private String registrationKey; private transient Instant createdAt; private transient Instant deletedAt; diff --git a/src/main/java/teammates/logic/core/DataBundleLogic.java b/src/main/java/teammates/logic/core/DataBundleLogic.java index 2ed08a71b67..c38a9afb493 100644 --- a/src/main/java/teammates/logic/core/DataBundleLogic.java +++ b/src/main/java/teammates/logic/core/DataBundleLogic.java @@ -369,6 +369,9 @@ public void removeDataBundle(DataBundle dataBundle) { dataBundle.profiles.values().forEach(profile -> { profilesDb.deleteStudentProfile(profile.getGoogleId()); }); + dataBundle.accountRequests.values().forEach(accountRequest -> { + accountRequestsDb.deleteAccountRequest(accountRequest.getEmail()); + }); } private void deleteCourses(Collection courses) { diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 5ed2280ff75..1855a28aa69 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -72,13 +72,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { @Test public void testDeleteAccountRequest() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes.builder("valid2@test.com") - .withName("Test account Name") - .withRegistrationKey("ValidRegistrationKey2") - .withInstitute("TEAMMATES Test Institute 1") - .build(); - - accountRequestsDb.createEntity(a); + AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); ______TS("silent deletion of non-existent account"); @@ -104,13 +98,7 @@ public void testDeleteAccountRequest() throws Exception { @Test public void testgetAccountRequestForRegistrationKey() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes.builder("valid3@test.com") - .withName("Test account Name") - .withRegistrationKey("ValidRegistrationKey3") - .withInstitute("TEAMMATES Test Institute 1") - .build(); - - accountRequestsDb.createEntity(a); + AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); ______TS("typical success case"); @@ -132,13 +120,7 @@ public void testgetAccountRequestForRegistrationKey() throws Exception { @Test public void testGetAccountRequest() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes.builder("valid4@test.com") - .withName("Test account Name") - .withRegistrationKey("ValidRegistrationKey4") - .withInstitute("TEAMMATES Test Institute 1") - .build(); - - accountRequestsDb.createEntity(a); + AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); ______TS("typical success case"); diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index 53ab13b6a2d..e4a7140fe25 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1709,9 +1709,10 @@ "accountRequests": { "accountRequest1": { "email": "typical@gmail.tmt", + "registrationKey": "typical@gmail.tmt123", "institute": "TEAMMATES Test Institute 1", - "registrationKey": "typical@gmail.tmt12345", - "name": "Typical Instructor Name" + "name": "Typical Instructor Name", + "createdAt": "2026-03-01T23:59:00Z" } } } From 30837d7c8ee0952f8a496b4cc27101fc88eef423 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 20:12:06 +0800 Subject: [PATCH 14/79] Add e2e Tests --- ...rCreateAccountConfirmationPageE2ETest.java | 47 +++++++++++++++++++ .../CreateAccountConfirmationPage.java | 40 ++++++++++++++++ ...rCreateAccountConfirmationPageE2ETest.json | 11 +++++ src/e2e/resources/testng-e2e.xml | 1 + .../app/user-create-account-page.component.ts | 10 +++- 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java create mode 100644 src/e2e/java/teammates/e2e/pageobjects/CreateAccountConfirmationPage.java create mode 100644 src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json diff --git a/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java new file mode 100644 index 00000000000..f30c2cfe6a9 --- /dev/null +++ b/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java @@ -0,0 +1,47 @@ +package teammates.e2e.cases; + +import org.testng.annotations.Test; + +import teammates.common.util.AppUrl; +import teammates.common.util.Const; +import teammates.common.util.StringHelper; +import teammates.e2e.pageobjects.CreateAccountConfirmationPage; +import teammates.e2e.pageobjects.InstructorHomePage; + +/** + * SUT: {@link Const.WebPageURIs#CREATE_ACCOUNT_PAGE}. + */ +public class InstructorCreateAccountConfirmationPageE2ETest extends BaseE2ETestCase { + @Override + protected void prepareTestData() { + testData = loadDataBundle("/InstructorCreateAccountConfirmationPageE2ETest.json"); + removeAndRestoreDataBundle(testData); + } + + @Test + @Override + public void testAll() { + String userId = "test"; + + ______TS("Invalid Join Link"); + + AppUrl joinLink = createUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE); + CreateAccountConfirmationPage errorPage = + getNewPageInstance(joinLink, CreateAccountConfirmationPage.class); + + assertTrue(errorPage.isInvalidLinkMessageShowing()); + + ______TS("Click join link: valid key"); + + String regKey = StringHelper.encrypt(testData.accountRequests.get("accountRequest1").getRegistrationKey()); + joinLink = createUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE).withRegistrationKey(regKey); + CreateAccountConfirmationPage confirmationPage = loginToPage(joinLink, CreateAccountConfirmationPage.class, userId); + + confirmationPage.verifyJoiningUser(userId); + confirmationPage.confirmJoinCourse(InstructorHomePage.class); + + ______TS("Already joined, no confirmation page"); + + getNewPageInstance(joinLink, InstructorHomePage.class); + } +} diff --git a/src/e2e/java/teammates/e2e/pageobjects/CreateAccountConfirmationPage.java b/src/e2e/java/teammates/e2e/pageobjects/CreateAccountConfirmationPage.java new file mode 100644 index 00000000000..aa4b79253b9 --- /dev/null +++ b/src/e2e/java/teammates/e2e/pageobjects/CreateAccountConfirmationPage.java @@ -0,0 +1,40 @@ +package teammates.e2e.pageobjects; + +import static org.junit.Assert.assertEquals; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** + * Page Object Model for the create account confirmation page. + */ +public class CreateAccountConfirmationPage extends AppPage { + @FindBy(id = "btn-confirm") + private WebElement confirmButton; + + public CreateAccountConfirmationPage(Browser browser) { + super(browser); + } + + @Override + public boolean containsExpectedPageContents() { + String text = waitForElementPresence(By.tagName("h3")).getText(); + return text.contains("Confirm your Google account") || text.contains("Invalid account creation link"); + } + + public void verifyJoiningUser(String googleId) { + assertEquals(browser.driver.findElement(By.id("user-id")).getText(), googleId); + } + + public T confirmJoinCourse(Class typeOfPage) { + click(confirmButton); + waitForPageToLoad(); + return changePageType(typeOfPage); + } + + public boolean isInvalidLinkMessageShowing() { + return waitForElementPresence(By.tagName("h3")).getText().contains("Invalid account creation link"); + } + +} diff --git a/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json b/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json new file mode 100644 index 00000000000..0ddbbb618ba --- /dev/null +++ b/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json @@ -0,0 +1,11 @@ +{ + "accountRequests": { + "accountRequest1": { + "email": "typical@gmail.tmt", + "registrationKey": "typical@gmail.tmt123", + "institute": "TEAMMATES Test Institute 1", + "name": "Typical Instructor Name", + "createdAt": "2026-03-01T23:59:00Z" + } + } +} diff --git a/src/e2e/resources/testng-e2e.xml b/src/e2e/resources/testng-e2e.xml index 3c20f22f41e..253c0b93225 100644 --- a/src/e2e/resources/testng-e2e.xml +++ b/src/e2e/resources/testng-e2e.xml @@ -32,6 +32,7 @@ + diff --git a/src/web/app/user-create-account-page.component.ts b/src/web/app/user-create-account-page.component.ts index 79c57f1b3b6..3954581c90b 100644 --- a/src/web/app/user-create-account-page.component.ts +++ b/src/web/app/user-create-account-page.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { finalize } from 'rxjs/operators'; +import { environment } from '../environments/environment'; import { AccountService } from '../services/account.service'; import { AuthService } from '../services/auth.service'; import { NavigationService } from '../services/navigation.service'; @@ -24,6 +25,8 @@ export class UserCreateAccountPageComponent implements OnInit { key: string = ''; userId: string = ''; + private backendUrl: string = environment.backendUrl; + constructor( private route: ActivatedRoute, private router: Router, @@ -43,7 +46,12 @@ export class UserCreateAccountPageComponent implements OnInit { return; } - this.authService.getAuthUser().subscribe((resp: AuthInfo) => { + const nextUrl: string = `${window.location.pathname}${window.location.search.replace(/&/g, '%26')}`; + this.authService.getAuthUser(undefined, nextUrl).subscribe((resp: AuthInfo) => { + if (!resp.user) { + window.location.href = `${this.backendUrl}${resp.instructorLoginUrl}`; + } + this.userId = resp.user?.id || ''; if (resp.user?.isInstructor) { From 6a4c9b7c5655113324183ca5ae6ea9ded9006984 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 20:31:09 +0800 Subject: [PATCH 15/79] Fix page not redirecting to login --- .../teammates/e2e/cases/BaseE2ETestCase.java | 2 +- ...rCreateAccountConfirmationPageE2ETest.java | 3 +-- .../java/teammates/test/AbstractBackDoor.java | 2 -- ...create-account-page.component.spec.ts.snap | 3 +++ ...user-create-account-page.component.spec.ts | 23 ------------------- .../app/user-create-account-page.component.ts | 4 ++-- 6 files changed, 7 insertions(+), 30 deletions(-) diff --git a/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java b/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java index 27917916fa5..51e00ad74b8 100644 --- a/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java +++ b/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java @@ -301,7 +301,7 @@ String getKeyForStudent(StudentAttributes student) { @Override protected AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest) { - assert false: "This method has no implementation"; + assert false : "This method has no implementation"; return null; } diff --git a/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java index f30c2cfe6a9..70bb6833002 100644 --- a/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java @@ -26,8 +26,7 @@ public void testAll() { ______TS("Invalid Join Link"); AppUrl joinLink = createUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE); - CreateAccountConfirmationPage errorPage = - getNewPageInstance(joinLink, CreateAccountConfirmationPage.class); + CreateAccountConfirmationPage errorPage = getNewPageInstance(joinLink, CreateAccountConfirmationPage.class); assertTrue(errorPage.isInvalidLinkMessageShowing()); diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 1d5d213c763..54d1e7890e1 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -34,7 +34,6 @@ import teammates.common.datatransfer.DataBundle; import teammates.common.datatransfer.FeedbackParticipantType; import teammates.common.datatransfer.attributes.AccountAttributes; -import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; import teammates.common.datatransfer.attributes.FeedbackResponseAttributes; @@ -46,7 +45,6 @@ import teammates.common.util.Const; import teammates.common.util.JsonUtils; import teammates.ui.output.AccountData; -import teammates.ui.output.AccountRequestData; import teammates.ui.output.CourseData; import teammates.ui.output.CoursesData; import teammates.ui.output.FeedbackQuestionData; diff --git a/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap b/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap index 6b885f89bcb..0a4270d0f07 100644 --- a/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap +++ b/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap @@ -4,6 +4,7 @@ exports[`UserCreateAccountPageComponent should snap if user has not created an a { expect(navSpy.calls.count()).toEqual(1); expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor'); }); - - it('should stop loading if user is not logged in', () => { - spyOn(authService, 'getAuthUser').and.returnValue(of({})); - - component.ngOnInit(); - - expect(component.isLoading).toBeFalsy(); - }); - - it('should stop loading if user does not have an account', () => { - spyOn(authService, 'getAuthUser').and.returnValue( - of({ - user: { - id: 'user', - isInstructor: false, - }, - }), - ); - - component.ngOnInit(); - - expect(component.isLoading).toBeFalsy(); - }); }); diff --git a/src/web/app/user-create-account-page.component.ts b/src/web/app/user-create-account-page.component.ts index 3954581c90b..0a46dbc30ae 100644 --- a/src/web/app/user-create-account-page.component.ts +++ b/src/web/app/user-create-account-page.component.ts @@ -48,12 +48,12 @@ export class UserCreateAccountPageComponent implements OnInit { const nextUrl: string = `${window.location.pathname}${window.location.search.replace(/&/g, '%26')}`; this.authService.getAuthUser(undefined, nextUrl).subscribe((resp: AuthInfo) => { + this.userId = resp.user?.id || ''; + if (!resp.user) { window.location.href = `${this.backendUrl}${resp.instructorLoginUrl}`; } - this.userId = resp.user?.id || ''; - if (resp.user?.isInstructor) { // User already has instructor account this.navigationService.navigateByURL(this.router, '/web/instructor'); From 1de8a020eec551e241e7047e536604c59877133c Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 28 Aug 2021 20:36:27 +0800 Subject: [PATCH 16/79] Fix lint errors --- .../java/teammates/logic/core/AccountRequestsLogicTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 1855a28aa69..42bbebffd6e 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -6,7 +6,6 @@ import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.exception.InvalidParametersException; import teammates.common.util.FieldValidator; -import teammates.storage.api.AccountRequestsDb; import teammates.test.AssertHelper; /** @@ -15,7 +14,6 @@ public class AccountRequestsLogicTest extends BaseLogicTest { private final AccountRequestsLogic accountRequestsLogic = AccountRequestsLogic.inst(); - private final AccountRequestsDb accountRequestsDb = AccountRequestsDb.inst(); @Override protected void prepareTestData() { From 188b7d9011beb931d30f16b31c9f594872a9be3d Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Mon, 6 Sep 2021 13:28:34 +0800 Subject: [PATCH 17/79] Fix linting issues --- src/main/java/teammates/ui/webapi/CreateAccountAction.java | 4 ++-- .../java/teammates/ui/webapi/CreateAccountActionTest.java | 4 ++-- .../teammates/ui/webapi/CreateAccountRequestActionTest.java | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 6289558a999..2d38c81b8bc 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -24,6 +24,8 @@ */ class CreateAccountAction extends Action { + private static final Logger log = Logger.getLogger(); + @Override AuthType getMinAuthLevel() { return AuthType.LOGGED_IN; @@ -34,8 +36,6 @@ void checkSpecificAccessControl() { // Any user can create instructor account as long as the registration key is valid. } - private static final Logger log = Logger.getLogger(); - @Override public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOperationException { String registrationKey; diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index 6841ddc6af1..cc040b65aba 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -56,7 +56,7 @@ protected void testExecute() throws Exception { String[] params = new String[] { Const.ParamsNames.REGKEY, StringHelper.encrypt(regKey), }; CreateAccountAction a = getAction(params); - JsonResult r = getJsonResult(a); + getJsonResult(a); String courseId = generateNextDemoCourseId(email, FieldValidator.COURSE_ID_MAX_LENGTH); @@ -77,7 +77,7 @@ protected void testExecute() throws Exception { ______TS("Error: reg key not found"); a = getAction(params); - r = getJsonResult(a, HttpStatus.SC_BAD_REQUEST); + getJsonResult(a, HttpStatus.SC_BAD_REQUEST); verifyNoTasksAdded(); } diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index d2303eceb3f..a701db87613 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -44,7 +44,7 @@ protected void testExecute() throws Exception { assertEquals("institute cannot be null", ex.getMessage()); ex = verifyHttpRequestBodyFailure(buildCreateRequest(name, institute, null)); - + assertEquals("email cannot be null", ex.getMessage()); verifyNoTasksAdded(); @@ -88,8 +88,6 @@ protected void testExecute() throws Exception { req = buildCreateRequest(invalidName, institute, emailWithSpaces); - final CreateAccountRequestAction finalA = getAction(req); - ex = verifyHttpRequestBodyFailure(req); assertEquals("\"" + invalidName + "\" is not acceptable to TEAMMATES as a/an person name because " + "it contains invalid characters. A/An person name must start with an " From f3d1631a9667815d784aab12a1337e0e896f1ed5 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Mon, 6 Sep 2021 18:31:35 +0800 Subject: [PATCH 18/79] Change id of AccountRequest to use both email and institute --- ...rCreateAccountConfirmationPageE2ETest.json | 3 +- .../attributes/AccountRequestAttributes.java | 110 +++++------------- src/main/java/teammates/logic/api/Logic.java | 19 +-- .../logic/core/AccountRequestsLogic.java | 35 ++++-- .../teammates/logic/core/DataBundleLogic.java | 2 +- .../storage/api/AccountRequestsDb.java | 95 ++++++++------- .../storage/entity/AccountRequest.java | 50 +++++--- .../ui/output/AccountRequestData.java | 6 - .../ui/webapi/CreateAccountAction.java | 18 ++- .../ui/webapi/CreateAccountRequestAction.java | 24 +--- .../AccountRequestAttributesTest.java | 67 ++++------- .../logic/core/AccountRequestsLogicTest.java | 56 +++++---- .../storage/api/AccountRequestsDbTest.java | 63 +++++----- .../test/BaseTestCaseWithDatabaseAccess.java | 6 - .../BaseTestCaseWithLocalDatabaseAccess.java | 2 +- .../ui/webapi/CreateAccountActionTest.java | 17 ++- .../CreateAccountRequestActionTest.java | 5 +- .../resources/data/typicalDataBundle.json | 3 +- 18 files changed, 257 insertions(+), 324 deletions(-) diff --git a/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json b/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json index 0ddbbb618ba..54a4cdad3cb 100644 --- a/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json +++ b/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json @@ -4,8 +4,7 @@ "email": "typical@gmail.tmt", "registrationKey": "typical@gmail.tmt123", "institute": "TEAMMATES Test Institute 1", - "name": "Typical Instructor Name", - "createdAt": "2026-03-01T23:59:00Z" + "name": "Typical Instructor Name" } } } diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index e1d4d149160..5dcd6aa3ae3 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -1,6 +1,5 @@ package teammates.common.datatransfer.attributes; -import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -18,32 +17,24 @@ public class AccountRequestAttributes extends EntityAttributes { private String name; private String institute; private String registrationKey; - private transient Instant createdAt; - private transient Instant deletedAt; - private AccountRequestAttributes(String email) { + private AccountRequestAttributes(String email, String institute) { this.email = email; - this.registrationKey = null; - this.institute = null; + this.institute = institute; + this.name = null; - this.createdAt = Instant.now(); - this.deletedAt = null; + this.registrationKey = null; } /** * Gets the {@link AccountRequestAttributes} instance of the given {@link AccountRequest}. */ public static AccountRequestAttributes valueOf(AccountRequest accountRequest) { - AccountRequestAttributes accountRequestAttributes = new AccountRequestAttributes(accountRequest.getEmail()); + AccountRequestAttributes accountRequestAttributes = new AccountRequestAttributes(accountRequest.getEmail(), + accountRequest.getInstitute()); accountRequestAttributes.registrationKey = accountRequest.getRegistrationKey(); accountRequestAttributes.name = accountRequest.getName(); - accountRequestAttributes.institute = accountRequest.getInstitute(); - - if (accountRequest.getCreatedAt() != null) { - accountRequestAttributes.createdAt = accountRequest.getCreatedAt(); - } - accountRequestAttributes.deletedAt = accountRequest.getDeletedAt(); return accountRequestAttributes; } @@ -51,8 +42,8 @@ public static AccountRequestAttributes valueOf(AccountRequest accountRequest) { /** * Returns a builder for {@link AccountRequestAttributes}. */ - public static Builder builder(String email) { - return new Builder(email); + public static Builder builder(String email, String institute) { + return new Builder(email, institute); } public String getRegistrationKey() { @@ -63,42 +54,14 @@ public String getName() { return name; } - public void setName(String name) { - this.name = name; - } - public String getEmail() { return email; } - public void setEmail(String email) { - this.email = email; - } - public String getInstitute() { return institute; } - public Instant getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Instant createdAt) { - this.createdAt = createdAt; - } - - public Instant getDeletedAt() { - return deletedAt; - } - - public void setDeletedAt(Instant deletedAt) { - this.deletedAt = deletedAt; - } - - public boolean isAccountRequestDeleted() { - return this.deletedAt != null; - } - @Override public List getInvalidityInfo() { @@ -113,20 +76,18 @@ public List getInvalidityInfo() { @Override public AccountRequest toEntity() { - return new AccountRequest(getEmail(), getRegistrationKey(), getName(), - getInstitute(), getCreatedAt(), getDeletedAt()); + return new AccountRequest(getEmail(), getName(), getInstitute()); } @Override public String toString() { - return "[" + AccountRequestAttributes.class.getSimpleName() + "] email: " + getEmail() - + "registrationKey: " + getRegistrationKey() - + " name: " + getName() + " institute: " + getInstitute(); + return "[" + AccountRequestAttributes.class.getSimpleName() + "] registrationKey: " + getRegistrationKey() + + " email: " + getEmail() + " name: " + getName() + " institute: " + getInstitute(); } @Override public int hashCode() { - return (this.email + this.registrationKey + this.name + this.institute).hashCode(); + return (this.email + this.name + this.institute).hashCode(); } @Override @@ -137,8 +98,7 @@ public boolean equals(Object other) { return true; } else if (this.getClass() == other.getClass()) { AccountRequestAttributes otherCourse = (AccountRequestAttributes) other; - return Objects.equals(this.registrationKey, otherCourse.registrationKey) - && Objects.equals(this.email, otherCourse.email) + return Objects.equals(this.email, otherCourse.email) && Objects.equals(this.institute, otherCourse.institute) && Objects.equals(this.name, otherCourse.name); } else { @@ -157,16 +117,15 @@ public void sanitizeForSaving() { * Updates with {@link UpdateOptions}. */ public void update(UpdateOptions updateOptions) { - updateOptions.nameOption.ifPresent(s -> name = s); updateOptions.registrationKeyOption.ifPresent(s -> registrationKey = s); - updateOptions.instituteOption.ifPresent(s -> institute = s); + updateOptions.nameOption.ifPresent(s -> name = s); } /** * Returns a {@link UpdateOptions.Builder} to build {@link UpdateOptions} for a accountRequest. */ - public static UpdateOptions.Builder updateOptionsBuilder(String email) { - return new UpdateOptions.Builder(email); + public static UpdateOptions.Builder updateOptionsBuilder(String email, String institute) { + return new UpdateOptions.Builder(email, institute); } /** @@ -176,11 +135,11 @@ public static class Builder extends BasicBuilder nameOption = UpdateOption.empty(); private UpdateOption registrationKeyOption = UpdateOption.empty(); - private UpdateOption instituteOption = UpdateOption.empty(); - private UpdateOptions(String email) { + private UpdateOptions(String email, String institute) { assert email != null; + assert institute != null; this.email = email; - } - - public String getEmail() { - return email; + this.institute = institute; } @Override public String toString() { return "AccountRequestAttributes.UpdateOptions [" - + "email = " + email + + ", email = " + email + + ", institute = " + institute + ", name = " + nameOption + ", registrationKey = " + registrationKeyOption - + ", institute = " + instituteOption + "]"; } @@ -226,8 +183,8 @@ public String toString() { */ public static class Builder extends BasicBuilder { - private Builder(String email) { - super(new UpdateOptions(email)); + private Builder(String email, String institute) { + super(new UpdateOptions(email, institute)); thisBuilder = this; } @@ -262,17 +219,10 @@ public B withName(String name) { return thisBuilder; } - public B withRegistrationKey(String registrationKey) { - assert registrationKey != null; - - updateOptions.registrationKeyOption = UpdateOption.of(registrationKey); - return thisBuilder; - } - - public B withInstitute(String institute) { - assert institute != null; + public B withRegistrationKey(String registationKey) { + assert registationKey != null; - updateOptions.instituteOption = UpdateOption.of(institute); + updateOptions.registrationKeyOption = UpdateOption.of(registationKey); return thisBuilder; } diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 1ec26c42769..c8d184bc3f3 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -1382,7 +1382,7 @@ public boolean isStudentsInSameTeam(String courseId, String student1Email, Strin *

Preconditions:

* * All parameters are non-null. * - * @return the created/updated account request + * @return the created account request * @throws InvalidParametersException if the account request is not valid */ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) @@ -1398,38 +1398,41 @@ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttri *

Preconditions:

* * All parameters are non-null. */ - public void deleteAccountRequest(String email) { + public void deleteAccountRequest(String email, String institute) { assert email != null; - accountRequestsLogic.deleteAccountRequest(email); + accountRequestsLogic.deleteAccountRequest(email, institute); } /** - * Gets an account request by unique constraint registrationKey. + * Gets an account request by unique constraint {@code registrationKey}. * *

Preconditions:

* * All parameters are non-null. * * @return the account request or null if no match found + * @throws EntityDoesNotExistException if the account request does not exist */ - public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { + public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) + throws EntityDoesNotExistException { assert registrationKey != null; return accountRequestsLogic.getAccountRequestForRegistrationKey(registrationKey); } /** - * Gets an account request by email address. + * Gets an account request by email address and institute. * *

Preconditions:

* * All parameters are non-null. * * @return the account request or null if no match found */ - public AccountRequestAttributes getAccountRequest(String email) { + public AccountRequestAttributes getAccountRequest(String email, String institute) { assert email != null; + assert institute != null; - return accountRequestsLogic.getAccountRequest(email); + return accountRequestsLogic.getAccountRequest(email, institute); } } diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index ae3dd72be68..f9d3c0a4ed7 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -1,6 +1,7 @@ package teammates.logic.core; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityDoesNotExistException; import teammates.common.exception.InvalidParametersException; import teammates.storage.api.AccountRequestsDb; @@ -28,7 +29,7 @@ void initLogicDependencies() { /** * Creates or updates an account request. * - * @return the created/updated account request + * @return the created account request * @throws InvalidParametersException if the account request is not valid */ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) @@ -37,26 +38,40 @@ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttri } /** - * Deletes the account request associated with the {@code email}. + * Deletes the account request associated with the {@code id}. * *

Fails silently if the account request doesn't exist.

*/ - public void deleteAccountRequest(String email) { - accountRequestsDb.deleteAccountRequest(email); + public void deleteAccountRequest(String email, String institute) { + accountRequestsDb.deleteAccountRequest(email, institute); } /** - * Gets an account request by unique constraint registrationKey. + * Gets an account request by email address and institute. + * + * @return the account request or null if no match found */ - public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { - return accountRequestsDb.getAccountRequestForRegistrationKey(registrationKey); + public AccountRequestAttributes getAccountRequest(String email, String institute) { + return accountRequestsDb.getAccountRequest(email, institute); } /** - * Gets an account request by email address. + * Gets an account request by unique constraint {@code registrationKey}. + * + * @return the account request + * @throws EntityDoesNotExistException if account request does not exist */ - public AccountRequestAttributes getAccountRequest(String email) { - return accountRequestsDb.getAccountRequest(email); + public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) + throws EntityDoesNotExistException { + AccountRequestAttributes accountRequest = accountRequestsDb + .getAccountRequestForRegistrationKey(registrationKey); + + if (accountRequest == null) { + throw new EntityDoesNotExistException( + "Account request with registration key " + registrationKey + " does not exist"); + } + + return accountRequest; } } diff --git a/src/main/java/teammates/logic/core/DataBundleLogic.java b/src/main/java/teammates/logic/core/DataBundleLogic.java index c38a9afb493..c9f5a1914a3 100644 --- a/src/main/java/teammates/logic/core/DataBundleLogic.java +++ b/src/main/java/teammates/logic/core/DataBundleLogic.java @@ -370,7 +370,7 @@ public void removeDataBundle(DataBundle dataBundle) { profilesDb.deleteStudentProfile(profile.getGoogleId()); }); dataBundle.accountRequests.values().forEach(accountRequest -> { - accountRequestsDb.deleteAccountRequest(accountRequest.getEmail()); + accountRequestsDb.deleteAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); }); } diff --git a/src/main/java/teammates/storage/api/AccountRequestsDb.java b/src/main/java/teammates/storage/api/AccountRequestsDb.java index 987a9d9fd5d..e8a5b3d7c82 100644 --- a/src/main/java/teammates/storage/api/AccountRequestsDb.java +++ b/src/main/java/teammates/storage/api/AccountRequestsDb.java @@ -31,56 +31,26 @@ public static AccountRequestsDb inst() { } /** - * Gets an account request. + * Gets an account request by email and institute. */ - public AccountRequestAttributes getAccountRequest(String email) { + public AccountRequestAttributes getAccountRequest(String email, String institute) { assert email != null; + assert institute != null; - return makeAttributesOrNull(getAccountRequestEntity(email)); - } - - private AccountRequest getAccountRequestEntity(String email) { - return load().id(email).now(); + return makeAttributesOrNull(getAccountRequestEntity(AccountRequest.generateId(email, institute))); } /** - * Deletes an accountRequest. - */ - public void deleteAccountRequest(String email) { - assert email != null; - - deleteEntity(Key.create(AccountRequest.class, email)); - } - - @Override - LoadType load() { - return ofy().load().type(AccountRequest.class); - } - - @Override - boolean hasExistingEntities(AccountRequestAttributes entityToCreate) { - Key keyToFind = Key.create(AccountRequest.class, entityToCreate.getEmail()); - return !load().filterKey(keyToFind).keys().list().isEmpty(); - } - - @Override - AccountRequestAttributes makeAttributes(AccountRequest entity) { - assert entity != null; - - return AccountRequestAttributes.valueOf(entity); - } - - /** - * Creates or updates the account request using {@link AccountRequestAttributes.UpdateOptions}. + * Creates or updates an account request. * - * @return updated account request - * @throws InvalidParametersException if attributes to update are not valid + * @return the created account request + * @throws InvalidParametersException if the account request is not valid */ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) throws InvalidParametersException { assert accountRequestToAdd != null; - accountRequestToAdd.sanitizeForSaving(); + if (!accountRequestToAdd.isValid()) { throw new InvalidParametersException(accountRequestToAdd.getInvalidityInfo()); } @@ -88,32 +58,61 @@ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttri AccountRequest accountRequest = accountRequestToAdd.toEntity(); saveEntity(accountRequest); - return accountRequestToAdd; + return makeAttributes(accountRequest); } /** - * Gets an account request by unique constraint registrationKey. + * Gets an account request by unique constraint {@code registrationKey}. + * + * @return the account request or null if no match found */ public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { assert registrationKey != null; - return makeAttributesOrNull(getAccountRequestEntityForRegistrationKey(registrationKey.trim())); - } - - private AccountRequest getAccountRequestEntityForRegistrationKey(String key) { - List accountRequestList = load().filter("registrationKey =", key).list(); + List accountRequestList = load().filter("registrationKey =", registrationKey).list(); - // If registration key detected is not unique, something is wrong if (accountRequestList.size() > 1) { log.severe("Duplicate registration keys detected for: " - + accountRequestList.stream().map(i -> i.getEmail()).collect(Collectors.joining(", "))); + + accountRequestList.stream().map(i -> i.getId()).collect(Collectors.joining(", "))); } if (accountRequestList.isEmpty()) { return null; } - return accountRequestList.get(0); + return makeAttributes(accountRequestList.get(0)); + } + + private AccountRequest getAccountRequestEntity(String id) { + return load().id(id).now(); + } + + /** + * Deletes an accountRequest. + */ + public void deleteAccountRequest(String email, String institute) { + assert email != null; + assert institute != null; + + deleteEntity(Key.create(AccountRequest.class, AccountRequest.generateId(email, institute))); + } + + @Override + LoadType load() { + return ofy().load().type(AccountRequest.class); + } + + @Override + boolean hasExistingEntities(AccountRequestAttributes entityToCreate) { + Key keyToFind = Key.create(AccountRequest.class, entityToCreate.getRegistrationKey()); + return !load().filterKey(keyToFind).keys().list().isEmpty(); + } + + @Override + AccountRequestAttributes makeAttributes(AccountRequest entity) { + assert entity != null; + + return AccountRequestAttributes.valueOf(entity); } } diff --git a/src/main/java/teammates/storage/entity/AccountRequest.java b/src/main/java/teammates/storage/entity/AccountRequest.java index b4b30d0ebbe..bb703e4885e 100644 --- a/src/main/java/teammates/storage/entity/AccountRequest.java +++ b/src/main/java/teammates/storage/entity/AccountRequest.java @@ -1,5 +1,6 @@ package teammates.storage.entity; +import java.security.SecureRandom; import java.time.Instant; import com.googlecode.objectify.annotation.Entity; @@ -7,6 +8,8 @@ import com.googlecode.objectify.annotation.Index; import com.googlecode.objectify.annotation.Translate; +import teammates.common.util.StringHelper; + /** * Represents an AccountRequest entity. */ @@ -15,10 +18,12 @@ public class AccountRequest extends BaseEntity { @Id - private String email; + private String id; private String registrationKey; + private String email; + private String name; private String institute; @@ -26,27 +31,26 @@ public class AccountRequest extends BaseEntity { @Translate(InstantTranslatorFactory.class) private Instant createdAt; - @Translate(InstantTranslatorFactory.class) - private Instant deletedAt; - @SuppressWarnings("unused") private AccountRequest() { // required by Objectify } - public AccountRequest(String email, String registrationKey, String name, String institute, - Instant createdAt, Instant deletedAt) { + public AccountRequest(String email, String name, String institute) { this.setEmail(email); - this.setRegistrationKey(registrationKey); this.setName(name); this.setInstitute(institute); + this.setId(generateId(email, institute)); + this.setRegistrationKey(generateRegistrationKey()); + this.setCreatedAt(Instant.now()); + } + + public String getId() { + return id; + } - if (createdAt == null) { - this.setCreatedAt(Instant.now()); - } else { - this.setCreatedAt(createdAt); - } - this.setDeletedAt(deletedAt); + public void setId(String id) { + this.id = id.trim(); } public String getRegistrationKey() { @@ -89,12 +93,22 @@ public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; } - public Instant getDeletedAt() { - return deletedAt; + /** + * Generates an unique ID for the account request. + */ + public static String generateId(String email, String institute) { + // Format: email%institute e.g., adam@gmail.com%TEAMMATES_TEST_INSTITUTE + return email + '%' + institute; } - public void setDeletedAt(Instant deletedAt) { - this.deletedAt = deletedAt; - } + /** + * Generate unique registration key for the account request. + * The key contains random elements to avoid being guessed. + */ + private String generateRegistrationKey() { + String uniqueId = getId(); + SecureRandom prng = new SecureRandom(); + return StringHelper.encrypt(uniqueId + prng.nextInt()); + } } diff --git a/src/main/java/teammates/ui/output/AccountRequestData.java b/src/main/java/teammates/ui/output/AccountRequestData.java index 83f72dc7006..d6ea3d6fed1 100644 --- a/src/main/java/teammates/ui/output/AccountRequestData.java +++ b/src/main/java/teammates/ui/output/AccountRequestData.java @@ -11,14 +11,12 @@ public class AccountRequestData extends ApiOutput { private final String name; private final String institute; private final String registrationKey; - private final long createdAtTimeStamp; public AccountRequestData(AccountRequestAttributes accountRequestInfo) { this.name = accountRequestInfo.getName(); this.email = accountRequestInfo.getEmail(); this.institute = accountRequestInfo.getInstitute(); this.registrationKey = accountRequestInfo.getRegistrationKey(); - this.createdAtTimeStamp = accountRequestInfo.getCreatedAt().toEpochMilli(); } public String getInstitute() { @@ -29,10 +27,6 @@ public String getEmail() { return email; } - public long getCreatedAtTimeStamp() { - return createdAtTimeStamp; - } - public String getName() { return name; } diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 2d38c81b8bc..83911f3397a 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -38,18 +38,14 @@ void checkSpecificAccessControl() { @Override public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOperationException { - String registrationKey; + String registrationKey = getNonNullRequestParamValue(Const.ParamsNames.REGKEY); - try { - registrationKey = StringHelper.decrypt(getNonNullRequestParamValue(Const.ParamsNames.REGKEY)); - } catch (InvalidParametersException e) { - return new JsonResult(e.getMessage(), HttpStatus.SC_BAD_REQUEST); - } - - AccountRequestAttributes accountRequestAttributes = logic.getAccountRequestForRegistrationKey(registrationKey); + AccountRequestAttributes accountRequestAttributes; - if (accountRequestAttributes == null) { - return new JsonResult("Invalid registration key", HttpStatus.SC_BAD_REQUEST); + try { + accountRequestAttributes = logic.getAccountRequestForRegistrationKey(registrationKey); + } catch (EntityDoesNotExistException ednee) { + throw new EntityNotFoundException(ednee); } String instructorEmail = accountRequestAttributes.getEmail(); @@ -79,7 +75,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera } // Delete account request as it is no longer needed - logic.deleteAccountRequest(instructorEmail); + logic.deleteAccountRequest(instructorEmail, instructorInstitution); return new JsonResult("Account successfully created", HttpStatus.SC_OK); } diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index df73ae7ed53..f8c488d55d2 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -1,13 +1,10 @@ package teammates.ui.webapi; -import java.security.SecureRandom; - import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.exception.InvalidParametersException; import teammates.common.util.Config; import teammates.common.util.Const; import teammates.common.util.EmailWrapper; -import teammates.common.util.StringHelper; import teammates.ui.output.JoinLinkData; import teammates.ui.request.AccountCreateRequest; import teammates.ui.request.InvalidHttpRequestBodyException; @@ -18,29 +15,26 @@ class CreateAccountRequestAction extends AdminOnlyAction { @Override - public JsonResult execute() throws InvalidHttpRequestBodyException { + public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOperationException { AccountCreateRequest createRequest = getAndValidateRequestBody(AccountCreateRequest.class); String instructorName = createRequest.getInstructorName().trim(); String instructorEmail = createRequest.getInstructorEmail().trim(); String instructorInstitution = createRequest.getInstructorInstitution().trim(); - String registrationKey = generateRegistrationKey(instructorEmail); AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes - .builder(instructorEmail) + .builder(instructorEmail, instructorInstitution) .withName(instructorName) - .withRegistrationKey(registrationKey) - .withInstitute(instructorInstitution) .build(); try { - logic.createOrUpdateAccountRequest(accountRequestAttributes); + accountRequestAttributes = logic.createOrUpdateAccountRequest(accountRequestAttributes); } catch (InvalidParametersException ipe) { throw new InvalidHttpRequestBodyException(ipe); } String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE) - .withRegistrationKey(StringHelper.encrypt(registrationKey)) + .withRegistrationKey(accountRequestAttributes.getRegistrationKey()) .toAbsoluteString(); EmailWrapper email = emailGenerator.generateNewInstructorAccountJoinEmail( @@ -51,14 +45,4 @@ public JsonResult execute() throws InvalidHttpRequestBodyException { return new JsonResult(output); } - /** - * Generate unique registration key for the account. - * The key contains random elements to avoid being guessed. - */ - private String generateRegistrationKey(String uniqueId) { - SecureRandom prng = new SecureRandom(); - - return uniqueId + prng.nextInt(); - } - } diff --git a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java index b82b242dd74..f7a0cca0663 100644 --- a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java +++ b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java @@ -1,7 +1,5 @@ package teammates.common.datatransfer.attributes; -import java.time.Instant; - import org.testng.annotations.Test; import teammates.common.util.FieldValidator; @@ -16,19 +14,15 @@ public class AccountRequestAttributesTest extends BaseTestCase { @Test public void testValueOf_withTypicalData_shouldGenerateAttributesCorrectly() { - Instant typicalInstant = Instant.now(); AccountRequest accountRequest = - new AccountRequest("valid@test.com", "registrationkey", "Valid Name", - "Valid Institute", typicalInstant, typicalInstant); + new AccountRequest("valid@test.com", "Valid Name", "Valid Institute"); AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes.valueOf(accountRequest); - assertEquals("registrationkey", accountRequestAttributes.getRegistrationKey()); + assertNotNull(accountRequestAttributes.getRegistrationKey()); assertEquals("Valid Name", accountRequestAttributes.getName()); assertEquals("valid@test.com", accountRequestAttributes.getEmail()); assertEquals("Valid Institute", accountRequestAttributes.getInstitute()); - assertEquals(typicalInstant, accountRequestAttributes.getCreatedAt()); - assertEquals(typicalInstant, accountRequestAttributes.getDeletedAt()); } @Test @@ -39,14 +33,11 @@ public void testBuilder_withTypicalData_shouldBuildCorrectAttributes() { String validRegKey = "validRegKey123"; AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes - .builder(validEmail) + .builder(validEmail, validInstitute) .withName(validName) - .withInstitute(validInstitute) .withRegistrationKey(validRegKey) .build(); - assertNotNull(accountRequestAttributes.getCreatedAt()); - assertNull(accountRequestAttributes.getDeletedAt()); assertEquals(validEmail, accountRequestAttributes.getEmail()); assertEquals(validName, accountRequestAttributes.getName()); assertEquals(validRegKey, accountRequestAttributes.getRegistrationKey()); @@ -56,58 +47,49 @@ public void testBuilder_withTypicalData_shouldBuildCorrectAttributes() { @Test public void testBuilder_buildNothing_shouldUseDefaultValues() { AccountRequestAttributes accountRequestAttributes = - AccountRequestAttributes.builder("valid@test.com").build(); + AccountRequestAttributes.builder("valid@test.com", "valid institute").build(); assertEquals("valid@test.com", accountRequestAttributes.getEmail()); + assertEquals("valid institute", accountRequestAttributes.getInstitute()); assertNull(accountRequestAttributes.getName()); - assertNull(accountRequestAttributes.getInstitute()); assertNull(accountRequestAttributes.getRegistrationKey()); - assertNotNull(accountRequestAttributes.getCreatedAt()); - assertNull(accountRequestAttributes.getDeletedAt()); } @Test public void testBuilder_withNullArguments_shouldThrowException() { assertThrows(AssertionError.class, () -> { AccountRequestAttributes - .builder(null) + .builder(null, null) .build(); }); assertThrows(AssertionError.class, () -> { AccountRequestAttributes - .builder("valid@test.com") + .builder("valid@test.com", "valid institute") .withName(null) .build(); }); assertThrows(AssertionError.class, () -> { AccountRequestAttributes - .builder("valid@test.com") + .builder("valid@test.com", "valid institute") .withRegistrationKey(null) .build(); }); - assertThrows(AssertionError.class, () -> { - AccountRequestAttributes - .builder("valid@test.com") - .withInstitute(null) - .build(); - }); } @Test public void testValidate() throws Exception { - AccountRequestAttributes validAccountRequest = generateValidAccountRequestAttributesObject(); + AccountRequestAttributes validAccountRequest = getValidAccountRequestAttributesObject(); assertTrue("valid value", validAccountRequest.isValid()); String invalidEmail = "invalid-email"; String emptyName = ""; AccountRequestAttributes invalidAccountRequest = AccountRequestAttributes - .builder(invalidEmail) + .builder(invalidEmail, "institute") .withName(emptyName) - .withInstitute("institute") .build(); assertFalse("invalid value", invalidAccountRequest.isValid()); @@ -134,24 +116,23 @@ public void testIsValid() { @Test public void testToString() { - AccountRequestAttributes a = generateValidAccountRequestAttributesObject(); - assertEquals("[AccountRequestAttributes] email: valid@test.comregistrationKey: " - + "valid123 name: valid-name institute: valid-institute", a.toString()); + AccountRequestAttributes a = getValidAccountRequestAttributesObject(); + assertEquals("[AccountRequestAttributes] registrationKey: valid123 email: valid@test.com " + + "name: valid-name institute: valid-institute", a.toString()); } @Test public void testEquals() { - AccountRequestAttributes accountRequest = generateValidAccountRequestAttributesObject(); + AccountRequestAttributes accountRequest = getValidAccountRequestAttributesObject(); // When the two account requests have same values - AccountRequestAttributes similarAccountRequest = generateValidAccountRequestAttributesObject(); + AccountRequestAttributes similarAccountRequest = getValidAccountRequestAttributesObject(); assertTrue(accountRequest.equals(similarAccountRequest)); // When the two account requests are different - AccountRequestAttributes differentAccountRequest = AccountRequestAttributes.builder("test@test.com") - .withName("Another Name") - .build(); + AccountRequestAttributes differentAccountRequest = + AccountRequestAttributes.builder("test@test.com", "test-institute").withName("Another Name").build(); assertFalse(accountRequest.equals(differentAccountRequest)); @@ -161,26 +142,24 @@ public void testEquals() { @Test public void testHashCode() { - AccountRequestAttributes accountRequest = generateValidAccountRequestAttributesObject(); + AccountRequestAttributes accountRequest = getValidAccountRequestAttributesObject(); // When the two account requests have same values, they should have the same hash code - AccountRequestAttributes accountRequestSimilar = generateValidAccountRequestAttributesObject(); + AccountRequestAttributes accountRequestSimilar = getValidAccountRequestAttributesObject(); assertTrue(accountRequest.hashCode() == accountRequestSimilar.hashCode()); // When the two account requests are different, they should have different hash code - AccountRequestAttributes accountRequestDifferent = AccountRequestAttributes.builder("test@test.com") - .withName("Another Name") - .build(); + AccountRequestAttributes accountRequestDifferent = + AccountRequestAttributes.builder("test@test.com", "test-institute").withName("Another Name").build(); assertFalse(accountRequest.hashCode() == accountRequestDifferent.hashCode()); } - private static AccountRequestAttributes generateValidAccountRequestAttributesObject() { - return AccountRequestAttributes.builder("valid@test.com") + private static AccountRequestAttributes getValidAccountRequestAttributesObject() { + return AccountRequestAttributes.builder("valid@test.com", "valid-institute") .withName("valid-name") .withRegistrationKey("valid123") - .withInstitute("valid-institute") .build(); } diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 42bbebffd6e..9e7349edbf3 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -4,6 +4,7 @@ import org.testng.annotations.Test; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityDoesNotExistException; import teammates.common.exception.InvalidParametersException; import teammates.common.util.FieldValidator; import teammates.test.AssertHelper; @@ -31,31 +32,39 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ______TS("typical success case"); - AccountRequestAttributes a = AccountRequestAttributes.builder("valid@test.com") + AccountRequestAttributes accountRequest = AccountRequestAttributes + .builder("valid@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") - .withRegistrationKey("ValidRegistrationKey") - .withInstitute("TEAMMATES Test Institute 1") .build(); - accountRequestsLogic.createOrUpdateAccountRequest(a); - verifyPresentInDatabase(a); + AccountRequestAttributes createdAccountRequest = + accountRequestsLogic.createOrUpdateAccountRequest(accountRequest); + verifyPresentInDatabase(createdAccountRequest); + + assertEquals(accountRequest.getEmail(), createdAccountRequest.getEmail()); + assertEquals(accountRequest.getName(), createdAccountRequest.getName()); + assertEquals(accountRequest.getInstitute(), createdAccountRequest.getInstitute()); + assertNotNull(createdAccountRequest.getRegistrationKey()); ______TS("duplicate account, account request updated"); - AccountRequestAttributes duplicateAccount = AccountRequestAttributes.builder("valid@test.com") + AccountRequestAttributes duplicateAccount = AccountRequestAttributes + .builder("valid@test.com", "TEAMMATES Test Institute 2") .withName("Test account Name 2") - .withRegistrationKey("ValidRegistrationKey2") - .withInstitute("TEAMMATES Test Institute 2") .build(); - accountRequestsLogic.createOrUpdateAccountRequest(duplicateAccount); - assertEquals(getAccountRequest(duplicateAccount), duplicateAccount); + duplicateAccount = accountRequestsLogic.createOrUpdateAccountRequest(duplicateAccount); + verifyPresentInDatabase(duplicateAccount); ______TS("failure case: invalid parameter"); - a.setEmail("invalid email"); + AccountRequestAttributes invalidAccountRequest = AccountRequestAttributes + .builder("invalid email", "TEAMMATES Test Institute 1") + .withName("Test account Name") + .build(); + InvalidParametersException ipe = assertThrows(InvalidParametersException.class, - () -> accountRequestsLogic.createOrUpdateAccountRequest(a)); + () -> accountRequestsLogic.createOrUpdateAccountRequest(invalidAccountRequest)); AssertHelper.assertContains( getPopulatedErrorMessage( FieldValidator.EMAIL_ERROR_MESSAGE, "invalid email", @@ -71,32 +80,34 @@ public void testCreateOrUpdateAccountRequest() throws Exception { @Test public void testDeleteAccountRequest() throws Exception { AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); + a = getAccountRequest(a); ______TS("silent deletion of non-existent account"); - accountRequestsLogic.deleteAccountRequest("not_exist"); + accountRequestsLogic.deleteAccountRequest("not_exist", "not_exist"); ______TS("typical success case"); verifyPresentInDatabase(a); - accountRequestsLogic.deleteAccountRequest(a.getEmail()); + accountRequestsLogic.deleteAccountRequest(a.getEmail(), a.getInstitute()); verifyAbsentInDatabase(a); ______TS("silent deletion of same account"); - accountRequestsLogic.deleteAccountRequest(a.getEmail()); + accountRequestsLogic.deleteAccountRequest(a.getEmail(), a.getInstitute()); ______TS("failure null parameter"); assertThrows(AssertionError.class, - () -> accountRequestsLogic.deleteAccountRequest(null)); + () -> accountRequestsLogic.deleteAccountRequest(null, null)); } @Test - public void testgetAccountRequestForRegistrationKey() throws Exception { + public void testGetAccountRequestForRegistrationKey() throws Exception { AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); + a = getAccountRequest(a); ______TS("typical success case"); @@ -106,9 +117,8 @@ public void testgetAccountRequestForRegistrationKey() throws Exception { ______TS("account request not found"); - AccountRequestAttributes notFoundRequestAttributes = - accountRequestsLogic.getAccountRequestForRegistrationKey("not-found"); - assertNull(notFoundRequestAttributes); + assertThrows(EntityDoesNotExistException.class, + () -> accountRequestsLogic.getAccountRequestForRegistrationKey("not-found")); ______TS("failure null parameter"); @@ -123,19 +133,19 @@ public void testGetAccountRequest() throws Exception { ______TS("typical success case"); AccountRequestAttributes accountRequestAttributes = - accountRequestsLogic.getAccountRequest(a.getEmail()); + accountRequestsLogic.getAccountRequest(a.getEmail(), a.getInstitute()); assertEquals(a, accountRequestAttributes); ______TS("account request not found"); AccountRequestAttributes notFoundRequestAttributes = - accountRequestsLogic.getAccountRequest("not-found@test.com"); + accountRequestsLogic.getAccountRequest("not-found@test.com", "not-found"); assertNull(notFoundRequestAttributes); ______TS("failure null parameter"); assertThrows(AssertionError.class, - () -> accountRequestsLogic.getAccountRequest(null)); + () -> accountRequestsLogic.getAccountRequest(null, null)); } } diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index bc7d5eee826..9463d287815 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -20,30 +20,33 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ______TS("typical success case"); - AccountRequestAttributes a = AccountRequestAttributes.builder("valid@test.com") + AccountRequestAttributes accountRequest = AccountRequestAttributes + .builder("valid@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") - .withRegistrationKey("ValidRegistrationKey") - .withInstitute("TEAMMATES Test Institute 1") .build(); - accountRequestsDb.createOrUpdateAccountRequest(a); - verifyPresentInDatabase(a); + accountRequest = accountRequestsDb.createOrUpdateAccountRequest(accountRequest); + verifyPresentInDatabase(accountRequest); ______TS("duplicate account, account request updated"); - AccountRequestAttributes duplicateAccount = AccountRequestAttributes.builder("valid@test.com") + AccountRequestAttributes duplicateAccount = AccountRequestAttributes + .builder("valid@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name 2") - .withRegistrationKey("ValidRegistrationKey2") - .withInstitute("TEAMMATES Test Institute 2") .build(); - accountRequestsDb.createOrUpdateAccountRequest(duplicateAccount); + duplicateAccount = accountRequestsDb.createOrUpdateAccountRequest(duplicateAccount); + verifyPresentInDatabase(duplicateAccount); ______TS("failure case: invalid parameter"); - a.setEmail("invalid email"); + AccountRequestAttributes invalidAccountRequest = AccountRequestAttributes + .builder("invalid email", "TEAMMATES Test Institute 1") + .withName("Test account Name") + .build(); + InvalidParametersException ipe = assertThrows(InvalidParametersException.class, - () -> accountRequestsDb.createOrUpdateAccountRequest(a)); + () -> accountRequestsDb.createOrUpdateAccountRequest(invalidAccountRequest)); AssertHelper.assertContains( getPopulatedErrorMessage( FieldValidator.EMAIL_ERROR_MESSAGE, "invalid email", @@ -58,45 +61,42 @@ public void testCreateOrUpdateAccountRequest() throws Exception { @Test public void testDeleteAccountRequest() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes.builder("valid2@test.com") + AccountRequestAttributes a = AccountRequestAttributes + .builder("valid2@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") - .withRegistrationKey("ValidRegistrationKey2") - .withInstitute("TEAMMATES Test Institute 1") .build(); - accountRequestsDb.createEntity(a); + accountRequestsDb.saveEntity(a.toEntity()); + a = accountRequestsDb.getAccountRequest("valid2@test.com", "TEAMMATES Test Institute 1"); ______TS("silent deletion of non-existent account"); - accountRequestsDb.deleteAccountRequest("not_exist"); + accountRequestsDb.deleteAccountRequest("not_exist", "not_exist"); ______TS("typical success case"); verifyPresentInDatabase(a); - - accountRequestsDb.deleteAccountRequest(a.getEmail()); - + accountRequestsDb.deleteAccountRequest(a.getEmail(), a.getInstitute()); verifyAbsentInDatabase(a); ______TS("silent deletion of same account"); - accountRequestsDb.deleteAccountRequest(a.getEmail()); + accountRequestsDb.deleteAccountRequest(a.getEmail(), a.getInstitute()); ______TS("failure null parameter"); assertThrows(AssertionError.class, - () -> accountRequestsDb.deleteAccountRequest(null)); + () -> accountRequestsDb.deleteAccountRequest(null, null)); } @Test public void testGetAccountRequestForRegistrationKey() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes.builder("valid3@test.com") + AccountRequestAttributes a = AccountRequestAttributes.builder("valid3@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") - .withRegistrationKey("ValidRegistrationKey3") - .withInstitute("TEAMMATES Test Institute 1") .build(); - accountRequestsDb.createEntity(a); + accountRequestsDb.saveEntity(a.toEntity()); + a = accountRequestsDb.getAccountRequest("valid3@test.com", "TEAMMATES Test Institute 1"); ______TS("typical success case"); @@ -118,30 +118,29 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { @Test public void testGetAccountRequest() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes.builder("valid4@test.com") + AccountRequestAttributes a = AccountRequestAttributes.builder("valid4@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") - .withRegistrationKey("ValidRegistrationKey4") - .withInstitute("TEAMMATES Test Institute 1") .build(); - accountRequestsDb.createEntity(a); + accountRequestsDb.saveEntity(a.toEntity()); + a = accountRequestsDb.getAccountRequest("valid4@test.com", "TEAMMATES Test Institute 1"); ______TS("typical success case"); AccountRequestAttributes accountRequestAttributes = - accountRequestsDb.getAccountRequest(a.getEmail()); + accountRequestsDb.getAccountRequest(a.getEmail(), a.getInstitute()); assertEquals(a, accountRequestAttributes); ______TS("account request not found"); AccountRequestAttributes notFoundRequestAttributes = - accountRequestsDb.getAccountRequest("not-found@test.com"); + accountRequestsDb.getAccountRequest("not-found@test.com", "not found"); assertNull(notFoundRequestAttributes); ______TS("failure null parameter"); assertThrows(AssertionError.class, - () -> accountRequestsDb.getAccountRequest(null)); + () -> accountRequestsDb.getAccountRequest(null, null)); } } diff --git a/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java b/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java index 366988cab7f..44395aaa2c4 100644 --- a/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java +++ b/src/test/java/teammates/test/BaseTestCaseWithDatabaseAccess.java @@ -155,7 +155,6 @@ private void verifyEquals(EntityAttributes expected, EntityAttributes actu } else if (expected instanceof AccountRequestAttributes) { AccountRequestAttributes expectedAccountRequest = (AccountRequestAttributes) expected; AccountRequestAttributes actualAccountRequest = (AccountRequestAttributes) actual; - equalizeIrrelevantData(expectedAccountRequest, actualAccountRequest); assertEquals(JsonUtils.toJson(expectedAccountRequest), JsonUtils.toJson(actualAccountRequest)); } else { @@ -220,11 +219,6 @@ private void equalizeIrrelevantData(StudentAttributes expected, StudentAttribute expected.setLastName(StringHelper.splitName(expected.getName())[1]); } - private void equalizeIrrelevantData(AccountRequestAttributes expected, AccountRequestAttributes actual) { - // Ignore time field as it is stamped at the time of creation in testing - expected.setCreatedAt(actual.getCreatedAt()); - } - protected abstract StudentProfileAttributes getStudentProfile(StudentProfileAttributes studentProfileAttributes); protected abstract CourseAttributes getCourse(CourseAttributes course); diff --git a/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java b/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java index 8d42ed2f871..e021b5ff15a 100644 --- a/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java +++ b/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java @@ -135,7 +135,7 @@ protected StudentAttributes getStudent(StudentAttributes student) { @Override protected AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest) { - return logic.getAccountRequest(accountRequest.getEmail()); + return logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); } protected void removeAndRestoreTypicalDataBundle() { diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index cc040b65aba..0792bc4950b 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -2,7 +2,6 @@ import java.util.List; -import org.apache.http.HttpStatus; import org.testng.annotations.Test; import teammates.common.datatransfer.attributes.AccountRequestAttributes; @@ -11,7 +10,6 @@ import teammates.common.datatransfer.attributes.StudentAttributes; import teammates.common.util.Const; import teammates.common.util.FieldValidator; -import teammates.common.util.StringHelper; import teammates.common.util.StringHelperExtension; /** @@ -35,10 +33,13 @@ protected void testExecute() throws Exception { String name = "JamesBond"; String email = "jamesbond89@gmail.tmt"; String institute = "TEAMMATES Test Institute 1"; - String regKey = "validregkey123"; - logic.createOrUpdateAccountRequest(AccountRequestAttributes.builder(email).withName(name) - .withInstitute(institute).withRegistrationKey(regKey).build()); + AccountRequestAttributes accountRequest = AccountRequestAttributes + .builder(email, institute) + .withName(name) + .build(); + + accountRequest = logic.createOrUpdateAccountRequest(accountRequest); ______TS("Not enough parameters"); @@ -54,7 +55,7 @@ protected void testExecute() throws Exception { ______TS("Normal case"); - String[] params = new String[] { Const.ParamsNames.REGKEY, StringHelper.encrypt(regKey), }; + String[] params = new String[] { Const.ParamsNames.REGKEY, accountRequest.getRegistrationKey(), }; CreateAccountAction a = getAction(params); getJsonResult(a); @@ -76,9 +77,7 @@ protected void testExecute() throws Exception { ______TS("Error: reg key not found"); - a = getAction(params); - getJsonResult(a, HttpStatus.SC_BAD_REQUEST); - + verifyEntityNotFound(params); verifyNoTasksAdded(); } diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index a701db87613..be16bdcd2ba 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -8,7 +8,6 @@ import teammates.common.util.Const; import teammates.common.util.EmailType; import teammates.common.util.EmailWrapper; -import teammates.common.util.StringHelper; import teammates.ui.output.JoinLinkData; import teammates.ui.request.AccountCreateRequest; @@ -61,7 +60,7 @@ protected void testExecute() throws Exception { assertEquals(HttpStatus.SC_OK, r.getStatusCode()); - AccountRequestAttributes accountRequestAttributes = logic.getAccountRequest(email); + AccountRequestAttributes accountRequestAttributes = logic.getAccountRequest(email, institute); assertEquals(name, accountRequestAttributes.getName()); assertEquals(email, accountRequestAttributes.getEmail()); @@ -69,7 +68,7 @@ protected void testExecute() throws Exception { assertNotNull(accountRequestAttributes.getRegistrationKey()); String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE) - .withRegistrationKey(StringHelper.encrypt(accountRequestAttributes.getRegistrationKey())) + .withRegistrationKey(accountRequestAttributes.getRegistrationKey()) .toAbsoluteString(); JoinLinkData output = (JoinLinkData) r.getOutput(); diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index e4a7140fe25..f7b28a01b0f 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1711,8 +1711,7 @@ "email": "typical@gmail.tmt", "registrationKey": "typical@gmail.tmt123", "institute": "TEAMMATES Test Institute 1", - "name": "Typical Instructor Name", - "createdAt": "2026-03-01T23:59:00Z" + "name": "Typical Instructor Name" } } } From deadc7a991338e5d110c49309bc552f22adb5423 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 7 Sep 2021 21:54:35 +0800 Subject: [PATCH 19/79] Move create account page to user join page --- ...rCreateAccountConfirmationPageE2ETest.java | 46 ------- .../CreateAccountConfirmationPage.java | 40 ------ ...rCreateAccountConfirmationPageE2ETest.json | 10 -- src/e2e/resources/testng-e2e.xml | 1 - .../java/teammates/common/util/AppUrl.java | 4 + .../java/teammates/common/util/Const.java | 2 +- .../ui/webapi/CreateAccountRequestAction.java | 3 +- .../CreateAccountRequestActionTest.java | 3 +- src/web/app/app.module.ts | 5 - .../user-create-account-page.component.html | 34 ----- .../user-create-account-page.component.scss | 0 ...user-create-account-page.component.spec.ts | 123 ------------------ .../app/user-create-account-page.component.ts | 83 ------------ .../app/user-create-account-page.module.ts | 30 ----- src/web/app/user-join-page.component.html | 4 +- src/web/app/user-join-page.component.ts | 115 ++++++++++++---- src/web/app/user-join-page.module.ts | 2 + 17 files changed, 101 insertions(+), 404 deletions(-) delete mode 100644 src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java delete mode 100644 src/e2e/java/teammates/e2e/pageobjects/CreateAccountConfirmationPage.java delete mode 100644 src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json delete mode 100644 src/web/app/user-create-account-page.component.html delete mode 100644 src/web/app/user-create-account-page.component.scss delete mode 100644 src/web/app/user-create-account-page.component.spec.ts delete mode 100644 src/web/app/user-create-account-page.component.ts delete mode 100644 src/web/app/user-create-account-page.module.ts diff --git a/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java deleted file mode 100644 index 70bb6833002..00000000000 --- a/src/e2e/java/teammates/e2e/cases/InstructorCreateAccountConfirmationPageE2ETest.java +++ /dev/null @@ -1,46 +0,0 @@ -package teammates.e2e.cases; - -import org.testng.annotations.Test; - -import teammates.common.util.AppUrl; -import teammates.common.util.Const; -import teammates.common.util.StringHelper; -import teammates.e2e.pageobjects.CreateAccountConfirmationPage; -import teammates.e2e.pageobjects.InstructorHomePage; - -/** - * SUT: {@link Const.WebPageURIs#CREATE_ACCOUNT_PAGE}. - */ -public class InstructorCreateAccountConfirmationPageE2ETest extends BaseE2ETestCase { - @Override - protected void prepareTestData() { - testData = loadDataBundle("/InstructorCreateAccountConfirmationPageE2ETest.json"); - removeAndRestoreDataBundle(testData); - } - - @Test - @Override - public void testAll() { - String userId = "test"; - - ______TS("Invalid Join Link"); - - AppUrl joinLink = createUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE); - CreateAccountConfirmationPage errorPage = getNewPageInstance(joinLink, CreateAccountConfirmationPage.class); - - assertTrue(errorPage.isInvalidLinkMessageShowing()); - - ______TS("Click join link: valid key"); - - String regKey = StringHelper.encrypt(testData.accountRequests.get("accountRequest1").getRegistrationKey()); - joinLink = createUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE).withRegistrationKey(regKey); - CreateAccountConfirmationPage confirmationPage = loginToPage(joinLink, CreateAccountConfirmationPage.class, userId); - - confirmationPage.verifyJoiningUser(userId); - confirmationPage.confirmJoinCourse(InstructorHomePage.class); - - ______TS("Already joined, no confirmation page"); - - getNewPageInstance(joinLink, InstructorHomePage.class); - } -} diff --git a/src/e2e/java/teammates/e2e/pageobjects/CreateAccountConfirmationPage.java b/src/e2e/java/teammates/e2e/pageobjects/CreateAccountConfirmationPage.java deleted file mode 100644 index aa4b79253b9..00000000000 --- a/src/e2e/java/teammates/e2e/pageobjects/CreateAccountConfirmationPage.java +++ /dev/null @@ -1,40 +0,0 @@ -package teammates.e2e.pageobjects; - -import static org.junit.Assert.assertEquals; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; - -/** - * Page Object Model for the create account confirmation page. - */ -public class CreateAccountConfirmationPage extends AppPage { - @FindBy(id = "btn-confirm") - private WebElement confirmButton; - - public CreateAccountConfirmationPage(Browser browser) { - super(browser); - } - - @Override - public boolean containsExpectedPageContents() { - String text = waitForElementPresence(By.tagName("h3")).getText(); - return text.contains("Confirm your Google account") || text.contains("Invalid account creation link"); - } - - public void verifyJoiningUser(String googleId) { - assertEquals(browser.driver.findElement(By.id("user-id")).getText(), googleId); - } - - public T confirmJoinCourse(Class typeOfPage) { - click(confirmButton); - waitForPageToLoad(); - return changePageType(typeOfPage); - } - - public boolean isInvalidLinkMessageShowing() { - return waitForElementPresence(By.tagName("h3")).getText().contains("Invalid account creation link"); - } - -} diff --git a/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json b/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json deleted file mode 100644 index 54a4cdad3cb..00000000000 --- a/src/e2e/resources/data/InstructorCreateAccountConfirmationPageE2ETest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "accountRequests": { - "accountRequest1": { - "email": "typical@gmail.tmt", - "registrationKey": "typical@gmail.tmt123", - "institute": "TEAMMATES Test Institute 1", - "name": "Typical Instructor Name" - } - } -} diff --git a/src/e2e/resources/testng-e2e.xml b/src/e2e/resources/testng-e2e.xml index 253c0b93225..3c20f22f41e 100644 --- a/src/e2e/resources/testng-e2e.xml +++ b/src/e2e/resources/testng-e2e.xml @@ -32,7 +32,6 @@ - diff --git a/src/main/java/teammates/common/util/AppUrl.java b/src/main/java/teammates/common/util/AppUrl.java index 5cfd34faf34..df67c9236a6 100644 --- a/src/main/java/teammates/common/util/AppUrl.java +++ b/src/main/java/teammates/common/util/AppUrl.java @@ -26,6 +26,10 @@ public AppUrl withInstitutionMac(String mac) { return withParam(Const.ParamsNames.INSTITUTION_MAC, mac); } + public AppUrl withIsCreatingAccount(String isCreatingAccount) { + return withParam(Const.ParamsNames.IS_CREATING_ACCOUNT, isCreatingAccount); + } + public AppUrl withCourseId(String courseId) { return withParam(Const.ParamsNames.COURSE_ID, courseId); } diff --git a/src/main/java/teammates/common/util/Const.java b/src/main/java/teammates/common/util/Const.java index 9aaeb74d595..f26939bfcaa 100644 --- a/src/main/java/teammates/common/util/Const.java +++ b/src/main/java/teammates/common/util/Const.java @@ -109,6 +109,7 @@ public static class ParamsNames { public static final String INSTRUCTOR_EMAIL = "instructoremail"; public static final String INSTRUCTOR_INSTITUTION = "instructorinstitution"; public static final String INSTITUTION_MAC = "mac"; + public static final String IS_CREATING_ACCOUNT = "iscreatingaccount"; public static final String INSTRUCTOR_ROLE_NAME = "instructorrole"; @@ -246,7 +247,6 @@ public static class WebPageURIs { private static final String MAINTAINER_PAGE = URI_PREFIX + "/" + EntityType.MAINTAINER; private static final String FRONT_PAGE = URI_PREFIX + "/front"; public static final String JOIN_PAGE = URI_PREFIX + "/join"; - public static final String CREATE_ACCOUNT_PAGE = URI_PREFIX + "/createaccount"; public static final String ADMIN_HOME_PAGE = ADMIN_PAGE + "/home"; public static final String ADMIN_ACCOUNTS_PAGE = ADMIN_PAGE + "/accounts"; diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index f8c488d55d2..ec031eec19e 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -33,7 +33,8 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera throw new InvalidHttpRequestBodyException(ipe); } - String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE) + String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) + .withIsCreatingAccount(String.valueOf(true)) .withRegistrationKey(accountRequestAttributes.getRegistrationKey()) .toAbsoluteString(); diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index be16bdcd2ba..c2adf8355ea 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -67,7 +67,8 @@ protected void testExecute() throws Exception { assertEquals(institute, accountRequestAttributes.getInstitute()); assertNotNull(accountRequestAttributes.getRegistrationKey()); - String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.CREATE_ACCOUNT_PAGE) + String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) + .withIsCreatingAccount(String.valueOf(true)) .withRegistrationKey(accountRequestAttributes.getRegistrationKey()) .toAbsoluteString(); diff --git a/src/web/app/app.module.ts b/src/web/app/app.module.ts index 8f518f43e05..b27c3798239 100644 --- a/src/web/app/app.module.ts +++ b/src/web/app/app.module.ts @@ -46,11 +46,6 @@ let routes: Routes = [ component: PublicPageComponent, loadChildren: () => import('./user-join-page.module').then((m: any) => m.UserJoinPageModule), }, - { - path: 'createaccount', - component: PublicPageComponent, - loadChildren: () => import('./user-create-account-page.module').then((m: any) => m.UserCreateAccountPageModule), - }, { path: 'sessions', component: PublicPageComponent, diff --git a/src/web/app/user-create-account-page.component.html b/src/web/app/user-create-account-page.component.html deleted file mode 100644 index 0947e3653c5..00000000000 --- a/src/web/app/user-create-account-page.component.html +++ /dev/null @@ -1,34 +0,0 @@ -
-
-
-

Not logged in

-
-
- You are not logged in. Please log in first. -
-
-
-
-

Invalid account creation link

-
-
- Please check that you copied the entire account creation link URL, or have it resent to you if this message persists. -
-
- The account creation link has been used and is no longer valid. -
-
-
-
-

Confirm your Google account

-
-
- You are currently logged in as {{ userId }}. -
If this is not you, please log out and log in using your own Google account. -
If this is you, please confirm below to complete your registration.
-
- -
-
-
-
diff --git a/src/web/app/user-create-account-page.component.scss b/src/web/app/user-create-account-page.component.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/web/app/user-create-account-page.component.spec.ts b/src/web/app/user-create-account-page.component.spec.ts deleted file mode 100644 index d12d42930d5..00000000000 --- a/src/web/app/user-create-account-page.component.spec.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; -import { of } from 'rxjs'; -import { AccountService } from '../services/account.service'; -import { AuthService } from '../services/auth.service'; -import { NavigationService } from '../services/navigation.service'; -import { LoadingSpinnerModule } from './components/loading-spinner/loading-spinner.module'; -import { UserCreateAccountPageComponent } from './user-create-account-page.component'; -import Spy = jasmine.Spy; - -describe('UserCreateAccountPageComponent', () => { - let component: UserCreateAccountPageComponent; - let fixture: ComponentFixture; - let navService: NavigationService; - let accountService: AccountService; - let authService: AuthService; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [UserCreateAccountPageComponent], - imports: [ - HttpClientTestingModule, - RouterTestingModule, - LoadingSpinnerModule, - ], - providers: [ - NavigationService, - AccountService, - AuthService, - { - provide: ActivatedRoute, - useValue: { - queryParams: of({ - key: 'key', - }), - }, - }, - ], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(UserCreateAccountPageComponent); - component = fixture.componentInstance; - navService = TestBed.inject(NavigationService); - accountService = TestBed.inject(AccountService); - authService = TestBed.inject(AuthService); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should snap with default fields', () => { - expect(fixture).toMatchSnapshot(); - }); - - it('should snap if user has not created an account and has a valid url', () => { - component.hasJoined = false; - component.userId = ''; - component.validUrl = true; - component.isLoading = false; - - fixture.detectChanges(); - - expect(fixture).toMatchSnapshot(); - }); - - it('should snap with invalid url', () => { - component.validUrl = false; - component.isLoading = false; - - fixture.detectChanges(); - - expect(fixture).toMatchSnapshot(); - }); - - it('should create account when create account button is clicked on', () => { - const params: string[] = ['key']; - component.isLoading = false; - component.userId = 'user'; - component.key = params[0]; - component.validUrl = true; - - const accountSpy: Spy = spyOn( - accountService, - 'createAccount', - ).and.returnValue(of({})); - const navSpy: Spy = spyOn(navService, 'navigateByURL'); - - fixture.detectChanges(); - - const btn: any = - fixture.debugElement.nativeElement.querySelector('#btn-confirm'); - btn.click(); - - expect(accountSpy.calls.count()).toEqual(1); - expect(accountSpy.calls.mostRecent().args).toEqual(params); - expect(navSpy.calls.count()).toEqual(1); - expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor'); - }); - - it('should redirect user to home page if user already has account', () => { - spyOn(authService, 'getAuthUser').and.returnValue( - of({ - user: { - id: 'user', - isInstructor: true, - }, - }), - ); - const navSpy: Spy = spyOn(navService, 'navigateByURL'); - - component.ngOnInit(); - - expect(component.userId).toEqual('user'); - expect(navSpy.calls.count()).toEqual(1); - expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor'); - }); -}); diff --git a/src/web/app/user-create-account-page.component.ts b/src/web/app/user-create-account-page.component.ts deleted file mode 100644 index 0a46dbc30ae..00000000000 --- a/src/web/app/user-create-account-page.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { finalize } from 'rxjs/operators'; -import { environment } from '../environments/environment'; -import { AccountService } from '../services/account.service'; -import { AuthService } from '../services/auth.service'; -import { NavigationService } from '../services/navigation.service'; -import { AuthInfo, MessageOutput } from '../types/api-output'; -import { ErrorReportComponent } from './components/error-report/error-report.component'; -import { ErrorMessageOutput } from './error-message-output'; - -/** - * User create account page component. - */ -@Component({ - selector: 'tm-user-create-account-page', - templateUrl: './user-create-account-page.component.html', - styleUrls: ['./user-create-account-page.component.scss'], -}) -export class UserCreateAccountPageComponent implements OnInit { - isLoading: boolean = true; - hasJoined: boolean = false; - validUrl: boolean = true; - key: string = ''; - userId: string = ''; - - private backendUrl: string = environment.backendUrl; - - constructor( - private route: ActivatedRoute, - private router: Router, - private navigationService: NavigationService, - private accountService: AccountService, - private authService: AuthService, - private ngbModal: NgbModal, - ) {} - - ngOnInit(): void { - this.route.queryParams.subscribe((queryParams: any) => { - this.key = queryParams.key; - - if (!this.key) { - this.validUrl = false; - this.isLoading = false; - return; - } - - const nextUrl: string = `${window.location.pathname}${window.location.search.replace(/&/g, '%26')}`; - this.authService.getAuthUser(undefined, nextUrl).subscribe((resp: AuthInfo) => { - this.userId = resp.user?.id || ''; - - if (!resp.user) { - window.location.href = `${this.backendUrl}${resp.instructorLoginUrl}`; - } - - if (resp.user?.isInstructor) { - // User already has instructor account - this.navigationService.navigateByURL(this.router, '/web/instructor'); - } - - this.isLoading = false; - }); - }); - } - - createAccount(): void { - this.isLoading = true; - this.accountService - .createAccount(this.key) - .pipe(finalize(() => (this.isLoading = false))) - .subscribe( - (_resp: MessageOutput) => { - this.navigationService.navigateByURL(this.router, '/web/instructor'); - }, - (resp: ErrorMessageOutput) => { - const modalRef: any = this.ngbModal.open(ErrorReportComponent); - modalRef.componentInstance.requestId = resp.error.requestId; - modalRef.componentInstance.errorMessage = resp.error.message; - }, - ); - } -} diff --git a/src/web/app/user-create-account-page.module.ts b/src/web/app/user-create-account-page.module.ts deleted file mode 100644 index 1606c449d64..00000000000 --- a/src/web/app/user-create-account-page.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { LoadingSpinnerModule } from './components/loading-spinner/loading-spinner.module'; -import { UserCreateAccountPageComponent } from './user-create-account-page.component'; - -const routes: Routes = [ - { - path: '', - component: UserCreateAccountPageComponent, - }, -]; - -/** - * Module for user create account page. - */ -@NgModule({ - declarations: [ - UserCreateAccountPageComponent, - ], - exports: [ - UserCreateAccountPageComponent, - ], - imports: [ - CommonModule, - RouterModule.forChild(routes), - LoadingSpinnerModule, - ], -}) -export class UserCreateAccountPageModule { } diff --git a/src/web/app/user-join-page.component.html b/src/web/app/user-join-page.component.html index ff4dad3475c..a4667615e73 100644 --- a/src/web/app/user-join-page.component.html +++ b/src/web/app/user-join-page.component.html @@ -1,4 +1,4 @@ -
+

Not logged in

@@ -27,7 +27,7 @@

Confirm your Google account


If this is not you, please log out and log in using your own Google account.
If this is you, please confirm below to complete your registration.
- +
diff --git a/src/web/app/user-join-page.component.ts b/src/web/app/user-join-page.component.ts index e577446b22b..d11102de040 100644 --- a/src/web/app/user-join-page.component.ts +++ b/src/web/app/user-join-page.component.ts @@ -1,11 +1,13 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { finalize } from 'rxjs/operators'; import { environment } from '../environments/environment'; +import { AccountService } from '../services/account.service'; import { AuthService } from '../services/auth.service'; import { CourseService } from '../services/course.service'; import { NavigationService } from '../services/navigation.service'; -import { AuthInfo, JoinStatus } from '../types/api-output'; +import { AuthInfo, JoinStatus, MessageOutput } from '../types/api-output'; import { ErrorReportComponent } from './components/error-report/error-report.component'; import { ErrorMessageOutput } from './error-message-output'; @@ -20,6 +22,7 @@ import { ErrorMessageOutput } from './error-message-output'; export class UserJoinPageComponent implements OnInit { isLoading: boolean = true; + isCreatingAccount: boolean = false; hasJoined: boolean = false; validUrl: boolean = true; entityType: string = ''; @@ -32,6 +35,7 @@ export class UserJoinPageComponent implements OnInit { constructor(private route: ActivatedRoute, private router: Router, + private accountService: AccountService, private courseService: CourseService, private navigationService: NavigationService, private authService: AuthService, @@ -43,37 +47,70 @@ export class UserJoinPageComponent implements OnInit { this.key = queryParams.key; this.institute = queryParams.instructorinstitution; this.mac = queryParams.mac; + this.isCreatingAccount = queryParams.iscreatingaccount === 'true'; - if (this.institute != null && this.mac == null) { + if (this.key == null || (this.institute != null && this.mac == null)) { this.validUrl = false; return; } - this.courseService.getJoinCourseStatus(this.key, this.entityType).subscribe((resp: JoinStatus) => { - this.hasJoined = resp.hasJoined; - this.userId = resp.userId || ''; - if (this.hasJoined && this.userId) { - // The regkey has been used and there is a logged in user. - // Simply redirect the user to their home page, regardless of whether the regkey matches or not. - this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); - } else { - this.isLoading = false; - } - }, (resp: ErrorMessageOutput) => { - if (resp.status === 403) { - this.isLoading = false; - const nextUrl: string = `${window.location.pathname}${window.location.search.replace(/&/g, '%26')}`; - this.authService.getAuthUser(undefined, nextUrl).subscribe((auth: AuthInfo) => { - if (!auth.user) { - window.location.href = `${this.backendUrl}${auth.studentLoginUrl}`; - } - }); - } else { - const modalRef: any = this.ngbModal.open(ErrorReportComponent); - modalRef.componentInstance.requestId = resp.error.requestId; - modalRef.componentInstance.errorMessage = resp.error.message; - } - }); + if (this.isCreatingAccount) { + this.setupForCreateAccount(); + } else { + this.setupForJoinCourse(); + } + }); + } + + /** + * Setup page for instructor to join course. + */ + setupForJoinCourse(): void { + this.courseService.getJoinCourseStatus(this.key, this.entityType).subscribe((resp: JoinStatus) => { + this.hasJoined = resp.hasJoined; + this.userId = resp.userId || ''; + if (this.hasJoined && this.userId) { + // The regkey has been used and there is a logged in user. + // Simply redirect the user to their home page, regardless of whether the regkey matches or not. + this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); + } else { + this.isLoading = false; + } + }, (resp: ErrorMessageOutput) => { + if (resp.status === 403) { + this.isLoading = false; + const nextUrl: string = `${window.location.pathname}${window.location.search.replace(/&/g, '%26')}`; + this.authService.getAuthUser(undefined, nextUrl).subscribe((auth: AuthInfo) => { + if (!auth.user) { + window.location.href = `${this.backendUrl}${auth.studentLoginUrl}`; + } + }); + } else { + const modalRef: any = this.ngbModal.open(ErrorReportComponent); + modalRef.componentInstance.requestId = resp.error.requestId; + modalRef.componentInstance.errorMessage = resp.error.message; + } + }); + } + + /** + * Setup page for instructor to create account. + */ + setupForCreateAccount(): void { + const nextUrl: string = `${window.location.pathname}${window.location.search.replace(/&/g, '%26')}`; + this.authService.getAuthUser(undefined, nextUrl).subscribe((resp: AuthInfo) => { + this.userId = resp.user?.id || ''; + + if (!resp.user) { + window.location.href = `${this.backendUrl}${resp.instructorLoginUrl}`; + } + + if (resp.user?.isInstructor) { + // User already has instructor account + this.navigationService.navigateByURL(this.router, '/web/instructor'); + } + + this.isLoading = false; }); } @@ -91,4 +128,28 @@ export class UserJoinPageComponent implements OnInit { }); } + /** + * Creates an account. + */ + createAccount(): void { + this.isLoading = true; + this.accountService + .createAccount(this.key) + .pipe(finalize(() => (this.isLoading = false))) + .subscribe( + (_resp: MessageOutput) => { + this.navigationService.navigateByURL(this.router, '/web/instructor'); + }, + (resp: ErrorMessageOutput) => { + if (resp.status === 404) { + this.validUrl = false; + } else { + const modalRef: any = this.ngbModal.open(ErrorReportComponent); + modalRef.componentInstance.requestId = resp.error.requestId; + modalRef.componentInstance.errorMessage = resp.error.message; + } + }, + ); + } + } diff --git a/src/web/app/user-join-page.module.ts b/src/web/app/user-join-page.module.ts index 4c1b8396e73..8a40a743d09 100644 --- a/src/web/app/user-join-page.module.ts +++ b/src/web/app/user-join-page.module.ts @@ -1,6 +1,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { LoadingSpinnerModule } from './components/loading-spinner/loading-spinner.module'; import { UserJoinPageComponent } from './user-join-page.component'; const routes: Routes = [ @@ -23,6 +24,7 @@ const routes: Routes = [ imports: [ CommonModule, RouterModule.forChild(routes), + LoadingSpinnerModule, ], }) export class UserJoinPageModule { } From d61683d713e5e7f121b001dad2d810bb9b0cea69 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 7 Sep 2021 23:07:32 +0800 Subject: [PATCH 20/79] Update tests for user-join-page --- ...create-account-page.component.spec.ts.snap | 120 --------------- .../user-join-page.component.spec.ts.snap | 142 +++++++++++++++++- src/web/app/user-join-page.component.spec.ts | 72 +++++++++ 3 files changed, 213 insertions(+), 121 deletions(-) delete mode 100644 src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap diff --git a/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap b/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap deleted file mode 100644 index 0a4270d0f07..00000000000 --- a/src/web/app/__snapshots__/user-create-account-page.component.spec.ts.snap +++ /dev/null @@ -1,120 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UserCreateAccountPageComponent should snap if user has not created an account and has a valid url 1`] = ` - -
- -
-
-

- Not logged in -

-
-
- You are not logged in. Please log in first. -
-
- - -
-
-`; - -exports[`UserCreateAccountPageComponent should snap with default fields 1`] = ` - - -
-
-
- Loading... -
-
- - -`; - -exports[`UserCreateAccountPageComponent should snap with invalid url 1`] = ` - -
- - -
-
-

- Invalid account creation link -

-
- -
- Please check that you copied the entire account creation link URL, or have it resent to you if this message persists. -
- -
- -
-
-`; diff --git a/src/web/app/__snapshots__/user-join-page.component.spec.ts.snap b/src/web/app/__snapshots__/user-join-page.component.spec.ts.snap index b3b97fa9496..46fab84a40f 100644 --- a/src/web/app/__snapshots__/user-join-page.component.spec.ts.snap +++ b/src/web/app/__snapshots__/user-join-page.component.spec.ts.snap @@ -1,13 +1,62 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`UserJoinPageComponent should snap if user is not logged in and has a valid create account url 1`] = ` + +
+ +
+
+

+ Not logged in +

+
+
+ You are not logged in. Please log in first. +
+
+ + +
+
+`; + exports[`UserJoinPageComponent should snap if user is not logged in and has a valid url 1`] = ` - + +
+
+
+ Loading... +
+
+ `; exports[`UserJoinPageComponent should snap with invalid course join link 1`] = ` +
+ + + +
+
+

+ Confirm your Google account +

+
+
+ You are currently logged in as + + user + + . +
+ If this is not you, please log out and log in using your own Google account. +
+ If this is you, please confirm below to complete your registration. +
+
+ +
+
+
+
+
+`; + +exports[`UserJoinPageComponent should snap with valid create account link 1`] = ` + { let navService: NavigationService; let courseService: CourseService; let authService: AuthService; + let accountService: AccountService; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -22,11 +25,13 @@ describe('UserJoinPageComponent', () => { imports: [ HttpClientTestingModule, RouterTestingModule, + LoadingSpinnerModule, ], providers: [ NavigationService, CourseService, AuthService, + AccountService, { provide: ActivatedRoute, useValue: { @@ -49,6 +54,7 @@ describe('UserJoinPageComponent', () => { navService = TestBed.inject(NavigationService); courseService = TestBed.inject(CourseService); authService = TestBed.inject(AuthService); + accountService = TestBed.inject(AccountService); fixture.detectChanges(); }); @@ -71,6 +77,18 @@ describe('UserJoinPageComponent', () => { expect(fixture).toMatchSnapshot(); }); + it('should snap if user is not logged in and has a valid create account url', () => { + component.hasJoined = false; + component.userId = ''; + component.validUrl = true; + component.isLoading = false; + component.isCreatingAccount = true; + + fixture.detectChanges(); + + expect(fixture).toMatchSnapshot(); + }); + it('should snap with invalid course join link', () => { component.validUrl = false; component.isLoading = false; @@ -101,6 +119,17 @@ describe('UserJoinPageComponent', () => { expect(fixture).toMatchSnapshot(); }); + it('should snap with valid create account link', () => { + component.validUrl = true; + component.userId = 'user'; + component.isLoading = false; + component.isCreatingAccount = true; + + fixture.detectChanges(); + + expect(fixture).toMatchSnapshot(); + }); + it('should join course when join course button is clicked on', () => { const params: string[] = ['key', 'student', 'NUS', 'mac']; component.isLoading = false; @@ -126,6 +155,29 @@ describe('UserJoinPageComponent', () => { expect(navSpy.calls.mostRecent().args[1]).toEqual(`/web/${params[1]}`); }); + it('should create account when join course button is clicked on', () => { + TestBed.inject(ActivatedRoute).queryParams = of({ iscreatingaccount: 'true', key: 'key' }); + const params: string[] = ['key']; + component.isLoading = false; + component.userId = 'user'; + component.isCreatingAccount = true; + component.key = params[0]; + component.validUrl = true; + + const accountSpy: Spy = spyOn(accountService, 'createAccount').and.returnValue(of({})); + const navSpy: Spy = spyOn(navService, 'navigateByURL'); + + fixture.detectChanges(); + + const btn: any = fixture.debugElement.nativeElement.querySelector('#btn-confirm'); + btn.click(); + + expect(accountSpy.calls.count()).toEqual(1); + expect(accountSpy.calls.mostRecent().args).toEqual(params); + expect(navSpy.calls.count()).toEqual(1); + expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor'); + }); + it('should redirect user to home page if user is logged in', () => { spyOn(courseService, 'getJoinCourseStatus').and.returnValue(of({ hasJoined: true, @@ -141,6 +193,26 @@ describe('UserJoinPageComponent', () => { expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/student/home'); }); + it('should redirect user to home page when creating account if user already has account', () => { + TestBed.inject(ActivatedRoute).queryParams = of({ iscreatingaccount: 'true', key: 'key' }); + + spyOn(authService, 'getAuthUser').and.returnValue( + of({ + user: { + id: 'user', + isInstructor: true, + }, + }), + ); + const navSpy: Spy = spyOn(navService, 'navigateByURL'); + + component.ngOnInit(); + + expect(component.userId).toEqual('user'); + expect(navSpy.calls.count()).toEqual(1); + expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor'); + }); + it('should stop loading if user is not logged in', () => { spyOn(courseService, 'getJoinCourseStatus').and.returnValue(of({ hasJoined: true, From 8f8ce64afa27e075af9589da589b08a5b4d5e616 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 8 Sep 2021 20:43:05 +0800 Subject: [PATCH 21/79] Add delete account request action --- .../teammates/ui/webapi/ActionFactory.java | 1 + .../ui/webapi/DeleteAccountRequestAction.java | 18 +++++ .../DeleteAccountRequestActionTest.java | 67 +++++++++++++++++++ .../resources/data/typicalDataBundle.json | 2 +- 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java create mode 100644 src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java diff --git a/src/main/java/teammates/ui/webapi/ActionFactory.java b/src/main/java/teammates/ui/webapi/ActionFactory.java index db492c54455..bb5f29ed7b1 100644 --- a/src/main/java/teammates/ui/webapi/ActionFactory.java +++ b/src/main/java/teammates/ui/webapi/ActionFactory.java @@ -47,6 +47,7 @@ public final class ActionFactory { map(ResourceURIs.ACCOUNT_DOWNGRADE, PUT, DowngradeAccountAction.class); map(ResourceURIs.ACCOUNT_RESET, PUT, ResetAccountAction.class); map(ResourceURIs.ACCOUNT_REQUEST, POST, CreateAccountRequestAction.class); + map(ResourceURIs.ACCOUNT_REQUEST, DELETE, DeleteAccountRequestAction.class); map(ResourceURIs.COURSE, GET, GetCourseAction.class); map(ResourceURIs.COURSE, DELETE, DeleteCourseAction.class); map(ResourceURIs.COURSE, POST, CreateCourseAction.class); diff --git a/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java b/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java new file mode 100644 index 00000000000..ba32cc927bb --- /dev/null +++ b/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java @@ -0,0 +1,18 @@ +package teammates.ui.webapi; + +import teammates.common.util.Const; + +/** + * Deletes an existing account request. + */ +class DeleteAccountRequestAction extends AdminOnlyAction { + + @Override + public JsonResult execute() { + String email = getNonNullRequestParamValue(Const.ParamsNames.EMAIL); + String institute = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_INSTITUTION); + logic.deleteAccountRequest(email, institute); + return new JsonResult("Account request is successfully deleted."); + } + +} diff --git a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java new file mode 100644 index 00000000000..4dd3f3348b4 --- /dev/null +++ b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java @@ -0,0 +1,67 @@ +package teammates.ui.webapi; + +import org.testng.annotations.Test; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.util.Const; +import teammates.ui.output.MessageOutput; + +/** + * SUT: {@link DeleteAccountRequestAction}. + */ +public class DeleteAccountRequestActionTest extends BaseActionTest { + + @Override + protected String getActionUri() { + return Const.ResourceURIs.ACCOUNT_REQUEST; + } + + @Override + protected String getRequestMethod() { + return DELETE; + } + + @Override + @Test + protected void testExecute() { + AccountRequestAttributes accountRequest = typicalBundle.accountRequests.get("accountRequest1"); + assertNotNull(logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); + + ______TS("Not enough parameters"); + + verifyHttpParameterFailure(); + + ______TS("Typical case, delete an existing account request request"); + + String[] submissionParams = new String[] { + Const.ParamsNames.EMAIL, accountRequest.getEmail(), + Const.ParamsNames.INSTRUCTOR_INSTITUTION, accountRequest.getInstitute(), + }; + + DeleteAccountRequestAction action = getAction(submissionParams); + JsonResult result = getJsonResult(action); + + MessageOutput msg = (MessageOutput) result.getOutput(); + + assertEquals(msg.getMessage(), "Account request is successfully deleted."); + + assertNull(logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); + + ______TS("Typical case, delete non-existing account request"); + + action = getAction(submissionParams); + result = getJsonResult(action); + msg = (MessageOutput) result.getOutput(); + + // should fail silently. + assertEquals(msg.getMessage(), "Account request is successfully deleted."); + + } + + @Override + @Test + protected void testAccessControl() { + verifyOnlyAdminCanAccess(); + } + +} diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index f7b28a01b0f..5e8c30a23a6 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1709,7 +1709,7 @@ "accountRequests": { "accountRequest1": { "email": "typical@gmail.tmt", - "registrationKey": "typical@gmail.tmt123", + "registrationKey": "typical@gmail.tmt%TEAMMATES Test Institute 1123", "institute": "TEAMMATES Test Institute 1", "name": "Typical Instructor Name" } From 0380f048936c2f3eb624baa61f60d57e25ce8c95 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 8 Sep 2021 20:55:33 +0800 Subject: [PATCH 22/79] Delete account request in AdminHomePageE2E test --- .../java/teammates/e2e/cases/AdminHomePageE2ETest.java | 1 + src/test/java/teammates/test/AbstractBackDoor.java | 10 ++++++++++ .../ui/webapi/DeleteAccountRequestActionTest.java | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java b/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java index 6c3f36af219..0b1528a6703 100644 --- a/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java @@ -42,6 +42,7 @@ public void testAll() { assertTrue(failureMessage.contains( "\"invalidemail\" is not acceptable to TEAMMATES as a/an email because it is not in the correct format.")); + BACKDOOR.deleteAccountRequest(email, institute); } } diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 0fc6c583b76..774ff9dd713 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -728,6 +728,16 @@ public void deleteCourse(String courseId) { executeDeleteRequest(Const.ResourceURIs.COURSE, params); } + /** + * Deletes an account request from the database. + */ + public void deleteAccountRequest(String email, String institute) { + Map params = new HashMap<>(); + params.put(Const.ParamsNames.EMAIL, email); + params.put(Const.ParamsNames.INSTRUCTOR_INSTITUTION, institute); + executeDeleteRequest(Const.ResourceURIs.ACCOUNT_REQUEST, params); + } + private static final class ResponseBodyAndCode { String responseBody; diff --git a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java index 4dd3f3348b4..f0261e7dee6 100644 --- a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java @@ -31,7 +31,7 @@ protected void testExecute() { verifyHttpParameterFailure(); - ______TS("Typical case, delete an existing account request request"); + ______TS("Typical case, delete an existing account request"); String[] submissionParams = new String[] { Const.ParamsNames.EMAIL, accountRequest.getEmail(), From e64308fbd83d7afcdef163fe715eaadb410fba09 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 8 Sep 2021 23:13:52 +0800 Subject: [PATCH 23/79] Add GetAccountRequest action --- .../teammates/e2e/cases/BaseE2ETestCase.java | 4 +- .../teammates/ui/webapi/ActionFactory.java | 1 + .../ui/webapi/GetAccountRequestAction.java | 26 +++++++ .../java/teammates/test/AbstractBackDoor.java | 22 ++++++ .../webapi/GetAccountRequestActionTest.java | 69 +++++++++++++++++++ .../resources/data/typicalDataBundle.json | 2 +- 6 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 src/main/java/teammates/ui/webapi/GetAccountRequestAction.java create mode 100644 src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java diff --git a/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java b/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java index 51e00ad74b8..c3d2188074f 100644 --- a/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java +++ b/src/e2e/java/teammates/e2e/cases/BaseE2ETestCase.java @@ -301,9 +301,7 @@ String getKeyForStudent(StudentAttributes student) { @Override protected AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest) { - assert false : "This method has no implementation"; - - return null; + return BACKDOOR.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); } @Override diff --git a/src/main/java/teammates/ui/webapi/ActionFactory.java b/src/main/java/teammates/ui/webapi/ActionFactory.java index bb5f29ed7b1..f23a2f6291a 100644 --- a/src/main/java/teammates/ui/webapi/ActionFactory.java +++ b/src/main/java/teammates/ui/webapi/ActionFactory.java @@ -46,6 +46,7 @@ public final class ActionFactory { map(ResourceURIs.ACCOUNT, DELETE, DeleteAccountAction.class); map(ResourceURIs.ACCOUNT_DOWNGRADE, PUT, DowngradeAccountAction.class); map(ResourceURIs.ACCOUNT_RESET, PUT, ResetAccountAction.class); + map(ResourceURIs.ACCOUNT_REQUEST, GET, GetAccountRequestAction.class); map(ResourceURIs.ACCOUNT_REQUEST, POST, CreateAccountRequestAction.class); map(ResourceURIs.ACCOUNT_REQUEST, DELETE, DeleteAccountRequestAction.class); map(ResourceURIs.COURSE, GET, GetCourseAction.class); diff --git a/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java b/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java new file mode 100644 index 00000000000..00a90be0bd7 --- /dev/null +++ b/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java @@ -0,0 +1,26 @@ +package teammates.ui.webapi; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.util.Const; +import teammates.ui.output.AccountRequestData; + +/** + * Gets account request information. + */ +class GetAccountRequestAction extends AdminOnlyAction { + + @Override + public JsonResult execute() { + String email = getNonNullRequestParamValue(Const.ParamsNames.EMAIL); + String institute = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_INSTITUTION); + + AccountRequestAttributes accountRequestInfo = logic.getAccountRequest(email, institute); + if (accountRequestInfo == null) { + throw new EntityNotFoundException("Account request does not exist."); + } + + AccountRequestData output = new AccountRequestData(accountRequestInfo); + return new JsonResult(output); + } + +} diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 774ff9dd713..2a5bcacd33d 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -34,6 +34,7 @@ import teammates.common.datatransfer.DataBundle; import teammates.common.datatransfer.FeedbackParticipantType; import teammates.common.datatransfer.attributes.AccountAttributes; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.CourseAttributes; import teammates.common.datatransfer.attributes.FeedbackQuestionAttributes; import teammates.common.datatransfer.attributes.FeedbackResponseAttributes; @@ -45,6 +46,7 @@ import teammates.common.util.Const; import teammates.common.util.JsonUtils; import teammates.ui.output.AccountData; +import teammates.ui.output.AccountRequestData; import teammates.ui.output.CourseData; import teammates.ui.output.CoursesData; import teammates.ui.output.FeedbackQuestionData; @@ -728,6 +730,26 @@ public void deleteCourse(String courseId) { executeDeleteRequest(Const.ResourceURIs.COURSE, params); } + /** + * Gets an account request from the database. + */ + public AccountRequestAttributes getAccountRequest(String email, String institute) { + Map params = new HashMap<>(); + params.put(Const.ParamsNames.EMAIL, email); + params.put(Const.ParamsNames.INSTRUCTOR_INSTITUTION, institute); + + ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.ACCOUNT_REQUEST, params); + if (response.responseCode == HttpStatus.SC_NOT_FOUND) { + return null; + } + + AccountRequestData accountRequestData = JsonUtils.fromJson(response.responseBody, AccountRequestData.class); + return AccountRequestAttributes.builder(accountRequestData.getEmail(), accountRequestData.getInstitute()) + .withName(accountRequestData.getName()) + .withRegistrationKey(accountRequestData.getRegistrationKey()) + .build(); + } + /** * Deletes an account request from the database. */ diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java new file mode 100644 index 00000000000..74f1b580f0c --- /dev/null +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -0,0 +1,69 @@ +package teammates.ui.webapi; + +import org.testng.annotations.Test; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.util.Const; +import teammates.ui.output.AccountRequestData; + +/** + * SUT: {@link GetAccountRequestAction}. + */ +public class GetAccountRequestActionTest extends BaseActionTest { + + @Override + protected String getActionUri() { + return Const.ResourceURIs.ACCOUNT_REQUEST; + } + + @Override + protected String getRequestMethod() { + return GET; + } + + @Override + @Test + protected void testExecute() { + AccountRequestAttributes accountRequest = typicalBundle.accountRequests.get("accountRequest1"); + + loginAsAdmin(); + + ______TS("Not enough parameters"); + + verifyHttpParameterFailure(); + + ______TS("account request does not exist"); + + String[] nonExistParams = { + Const.ParamsNames.EMAIL, "non-existent@email", + Const.ParamsNames.INSTRUCTOR_INSTITUTION, "non existent institute", + }; + + EntityNotFoundException enfe = verifyEntityNotFound(nonExistParams); + assertEquals("Account request does not exist.", enfe.getMessage()); + + ______TS("typical success case"); + + String[] params = new String[] { + Const.ParamsNames.EMAIL, accountRequest.getEmail(), + Const.ParamsNames.INSTRUCTOR_INSTITUTION, accountRequest.getInstitute(), + }; + + GetAccountRequestAction a = getAction(params); + JsonResult r = getJsonResult(a); + + AccountRequestData response = (AccountRequestData) r.getOutput(); + + assertEquals(response.getName(), accountRequest.getName()); + assertEquals(response.getEmail(), accountRequest.getEmail()); + assertEquals(response.getRegistrationKey(), accountRequest.getRegistrationKey()); + assertEquals(response.getInstitute(), accountRequest.getInstitute()); + } + + @Override + @Test + protected void testAccessControl() { + verifyOnlyAdminCanAccess(); + } + +} diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index 5e8c30a23a6..cefa527f92c 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1709,7 +1709,7 @@ "accountRequests": { "accountRequest1": { "email": "typical@gmail.tmt", - "registrationKey": "typical@gmail.tmt%TEAMMATES Test Institute 1123", + "registrationKey": "123456", "institute": "TEAMMATES Test Institute 1", "name": "Typical Instructor Name" } From c1a3f06b762c10d2d00acb28546e726be841c824 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 8 Sep 2021 23:24:22 +0800 Subject: [PATCH 24/79] Remove unnecessary calls to getAccountRequest --- .../java/teammates/e2e/cases/AdminHomePageE2ETest.java | 1 + .../datatransfer/attributes/AccountRequestAttributes.java | 8 +++++++- .../teammates/logic/core/AccountRequestsLogicTest.java | 2 -- .../java/teammates/storage/api/AccountRequestsDbTest.java | 6 +++--- .../teammates/ui/webapi/GetAccountRequestActionTest.java | 8 ++++---- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java b/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java index 0b1528a6703..9d694c5d962 100644 --- a/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/AdminHomePageE2ETest.java @@ -42,6 +42,7 @@ public void testAll() { assertTrue(failureMessage.contains( "\"invalidemail\" is not acceptable to TEAMMATES as a/an email because it is not in the correct format.")); + assertNotNull(BACKDOOR.getAccountRequest(email, institute)); BACKDOOR.deleteAccountRequest(email, institute); } diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 5dcd6aa3ae3..2d9e5d84bcf 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -76,7 +76,13 @@ public List getInvalidityInfo() { @Override public AccountRequest toEntity() { - return new AccountRequest(getEmail(), getName(), getInstitute()); + AccountRequest accountRequest = new AccountRequest(getEmail(), getName(), getInstitute()); + + if (this.getRegistrationKey() != null) { + accountRequest.setRegistrationKey(getRegistrationKey()); + } + + return accountRequest; } @Override diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 9e7349edbf3..9b883aa0dff 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -80,7 +80,6 @@ public void testCreateOrUpdateAccountRequest() throws Exception { @Test public void testDeleteAccountRequest() throws Exception { AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); - a = getAccountRequest(a); ______TS("silent deletion of non-existent account"); @@ -107,7 +106,6 @@ public void testDeleteAccountRequest() throws Exception { @Test public void testGetAccountRequestForRegistrationKey() throws Exception { AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); - a = getAccountRequest(a); ______TS("typical success case"); diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 9463d287815..98cf6ff5cea 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -64,10 +64,10 @@ public void testDeleteAccountRequest() throws Exception { AccountRequestAttributes a = AccountRequestAttributes .builder("valid2@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") + .withRegistrationKey("123456") .build(); accountRequestsDb.saveEntity(a.toEntity()); - a = accountRequestsDb.getAccountRequest("valid2@test.com", "TEAMMATES Test Institute 1"); ______TS("silent deletion of non-existent account"); @@ -93,10 +93,10 @@ public void testDeleteAccountRequest() throws Exception { public void testGetAccountRequestForRegistrationKey() throws Exception { AccountRequestAttributes a = AccountRequestAttributes.builder("valid3@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") + .withRegistrationKey("123456") .build(); accountRequestsDb.saveEntity(a.toEntity()); - a = accountRequestsDb.getAccountRequest("valid3@test.com", "TEAMMATES Test Institute 1"); ______TS("typical success case"); @@ -120,10 +120,10 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { public void testGetAccountRequest() throws Exception { AccountRequestAttributes a = AccountRequestAttributes.builder("valid4@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") + .withRegistrationKey("123456") .build(); accountRequestsDb.saveEntity(a.toEntity()); - a = accountRequestsDb.getAccountRequest("valid4@test.com", "TEAMMATES Test Institute 1"); ______TS("typical success case"); diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index 74f1b580f0c..bf89dce206b 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -35,8 +35,8 @@ protected void testExecute() { ______TS("account request does not exist"); String[] nonExistParams = { - Const.ParamsNames.EMAIL, "non-existent@email", - Const.ParamsNames.INSTRUCTOR_INSTITUTION, "non existent institute", + Const.ParamsNames.EMAIL, "non-existent@email", + Const.ParamsNames.INSTRUCTOR_INSTITUTION, "non existent institute", }; EntityNotFoundException enfe = verifyEntityNotFound(nonExistParams); @@ -45,8 +45,8 @@ protected void testExecute() { ______TS("typical success case"); String[] params = new String[] { - Const.ParamsNames.EMAIL, accountRequest.getEmail(), - Const.ParamsNames.INSTRUCTOR_INSTITUTION, accountRequest.getInstitute(), + Const.ParamsNames.EMAIL, accountRequest.getEmail(), + Const.ParamsNames.INSTRUCTOR_INSTITUTION, accountRequest.getInstitute(), }; GetAccountRequestAction a = getAction(params); From 68a1faee124be65b12b1129ad6902c7fce17d8f7 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 9 Sep 2021 17:40:47 +0800 Subject: [PATCH 25/79] Fix failing component tests --- .../java/teammates/ui/webapi/GetActionClassesActionTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java b/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java index a05124ab49a..1823935b9fd 100644 --- a/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java @@ -90,6 +90,8 @@ protected void testExecute() { DeleteAccountAction.class, CreateAccountAction.class, CreateAccountRequestAction.class, + GetAccountRequestAction.class, + DeleteAccountRequestAction.class, GetAccountAction.class, FeedbackSessionPublishedRemindersAction.class, QueryLogsAction.class, From c55d1eb6abe1d58bfa591c7b9ba30fa2f239873f Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 9 Sep 2021 18:01:29 +0800 Subject: [PATCH 26/79] Use instructorEmail instead of Email const --- .../attributes/AccountRequestAttributes.java | 9 +++++---- src/main/java/teammates/common/util/Const.java | 1 - .../teammates/ui/webapi/DeleteAccountRequestAction.java | 2 +- .../teammates/ui/webapi/GetAccountRequestAction.java | 2 +- src/test/java/teammates/test/AbstractBackDoor.java | 4 ++-- .../ui/webapi/DeleteAccountRequestActionTest.java | 2 +- .../teammates/ui/webapi/GetAccountRequestActionTest.java | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 2d9e5d84bcf..772fae32136 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -93,7 +93,7 @@ public String toString() { @Override public int hashCode() { - return (this.email + this.name + this.institute).hashCode(); + return (this.email + this.name + this.institute + this.registrationKey).hashCode(); } @Override @@ -106,7 +106,8 @@ public boolean equals(Object other) { AccountRequestAttributes otherCourse = (AccountRequestAttributes) other; return Objects.equals(this.email, otherCourse.email) && Objects.equals(this.institute, otherCourse.institute) - && Objects.equals(this.name, otherCourse.name); + && Objects.equals(this.name, otherCourse.name) + && Objects.equals(this.registrationKey, otherCourse.registrationKey); } else { return false; } @@ -128,7 +129,7 @@ public void update(UpdateOptions updateOptions) { } /** - * Returns a {@link UpdateOptions.Builder} to build {@link UpdateOptions} for a accountRequest. + * Returns a {@link UpdateOptions.Builder} to build {@link UpdateOptions} for an accountRequest. */ public static UpdateOptions.Builder updateOptionsBuilder(String email, String institute) { return new UpdateOptions.Builder(email, institute); @@ -157,7 +158,7 @@ public AccountRequestAttributes build() { } /** - * Helper class to specific the fields to update in {@link AccountAttributes}. + * Helper class to specific the fields to update in {@link AccountRequestAttributes}. */ public static class UpdateOptions { private String email; diff --git a/src/main/java/teammates/common/util/Const.java b/src/main/java/teammates/common/util/Const.java index f26939bfcaa..e97ff474e5b 100644 --- a/src/main/java/teammates/common/util/Const.java +++ b/src/main/java/teammates/common/util/Const.java @@ -148,7 +148,6 @@ public static class ParamsNames { public static final String USER_CAPTCHA_RESPONSE = "captcharesponse"; - public static final String EMAIL = "email"; public static final String EMAIL_TYPE = "emailtype"; public static final String ENTITY_TYPE = "entitytype"; diff --git a/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java b/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java index ba32cc927bb..95221fb3868 100644 --- a/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java @@ -9,7 +9,7 @@ class DeleteAccountRequestAction extends AdminOnlyAction { @Override public JsonResult execute() { - String email = getNonNullRequestParamValue(Const.ParamsNames.EMAIL); + String email = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_EMAIL); String institute = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_INSTITUTION); logic.deleteAccountRequest(email, institute); return new JsonResult("Account request is successfully deleted."); diff --git a/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java b/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java index 00a90be0bd7..fecf80f9c84 100644 --- a/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java @@ -11,7 +11,7 @@ class GetAccountRequestAction extends AdminOnlyAction { @Override public JsonResult execute() { - String email = getNonNullRequestParamValue(Const.ParamsNames.EMAIL); + String email = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_EMAIL); String institute = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_INSTITUTION); AccountRequestAttributes accountRequestInfo = logic.getAccountRequest(email, institute); diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 2a5bcacd33d..a71b69ff90c 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -735,7 +735,7 @@ public void deleteCourse(String courseId) { */ public AccountRequestAttributes getAccountRequest(String email, String institute) { Map params = new HashMap<>(); - params.put(Const.ParamsNames.EMAIL, email); + params.put(Const.ParamsNames.INSTRUCTOR_EMAIL, email); params.put(Const.ParamsNames.INSTRUCTOR_INSTITUTION, institute); ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.ACCOUNT_REQUEST, params); @@ -755,7 +755,7 @@ public AccountRequestAttributes getAccountRequest(String email, String institute */ public void deleteAccountRequest(String email, String institute) { Map params = new HashMap<>(); - params.put(Const.ParamsNames.EMAIL, email); + params.put(Const.ParamsNames.INSTRUCTOR_EMAIL, email); params.put(Const.ParamsNames.INSTRUCTOR_INSTITUTION, institute); executeDeleteRequest(Const.ResourceURIs.ACCOUNT_REQUEST, params); } diff --git a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java index f0261e7dee6..57e49ddb1a1 100644 --- a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java @@ -34,7 +34,7 @@ protected void testExecute() { ______TS("Typical case, delete an existing account request"); String[] submissionParams = new String[] { - Const.ParamsNames.EMAIL, accountRequest.getEmail(), + Const.ParamsNames.INSTRUCTOR_EMAIL, accountRequest.getEmail(), Const.ParamsNames.INSTRUCTOR_INSTITUTION, accountRequest.getInstitute(), }; diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index bf89dce206b..cf90b175ae0 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -35,7 +35,7 @@ protected void testExecute() { ______TS("account request does not exist"); String[] nonExistParams = { - Const.ParamsNames.EMAIL, "non-existent@email", + Const.ParamsNames.INSTRUCTOR_EMAIL, "non-existent@email", Const.ParamsNames.INSTRUCTOR_INSTITUTION, "non existent institute", }; @@ -45,7 +45,7 @@ protected void testExecute() { ______TS("typical success case"); String[] params = new String[] { - Const.ParamsNames.EMAIL, accountRequest.getEmail(), + Const.ParamsNames.INSTRUCTOR_EMAIL, accountRequest.getEmail(), Const.ParamsNames.INSTRUCTOR_INSTITUTION, accountRequest.getInstitute(), }; From 688d9d84827312999bc1a0e20b910e8f3826ac70 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 9 Sep 2021 18:41:54 +0800 Subject: [PATCH 27/79] Fix minor bugs and cleanup --- .../java/teammates/storage/api/AccountRequestsDb.java | 3 ++- .../java/teammates/storage/entity/AccountRequest.java | 4 ++-- .../teammates/ui/webapi/DeleteAccountRequestAction.java | 2 +- .../teammates/logic/core/AccountRequestsLogicTest.java | 6 +++--- .../java/teammates/storage/api/AccountRequestsDbTest.java | 6 +++--- .../java/teammates/ui/webapi/CreateAccountActionTest.java | 2 +- .../ui/webapi/CreateAccountRequestActionTest.java | 1 - .../ui/webapi/DeleteAccountRequestActionTest.java | 4 ++-- .../__snapshots__/admin-home-page.component.spec.ts.snap | 2 +- .../admin-home-page/admin-home-page.component.spec.ts | 8 ++++---- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/teammates/storage/api/AccountRequestsDb.java b/src/main/java/teammates/storage/api/AccountRequestsDb.java index e8a5b3d7c82..3083bc5ad28 100644 --- a/src/main/java/teammates/storage/api/AccountRequestsDb.java +++ b/src/main/java/teammates/storage/api/AccountRequestsDb.java @@ -104,7 +104,8 @@ LoadType load() { @Override boolean hasExistingEntities(AccountRequestAttributes entityToCreate) { - Key keyToFind = Key.create(AccountRequest.class, entityToCreate.getRegistrationKey()); + Key keyToFind = Key.create(AccountRequest.class, + AccountRequest.generateId(entityToCreate.getEmail(), entityToCreate.getInstitute())); return !load().filterKey(keyToFind).keys().list().isEmpty(); } diff --git a/src/main/java/teammates/storage/entity/AccountRequest.java b/src/main/java/teammates/storage/entity/AccountRequest.java index bb703e4885e..34a814afa34 100644 --- a/src/main/java/teammates/storage/entity/AccountRequest.java +++ b/src/main/java/teammates/storage/entity/AccountRequest.java @@ -74,7 +74,7 @@ public String getEmail() { } public void setEmail(String email) { - this.email = email; + this.email = email.trim(); } public String getInstitute() { @@ -82,7 +82,7 @@ public String getInstitute() { } public void setInstitute(String institute) { - this.institute = institute; + this.institute = institute.trim(); } public Instant getCreatedAt() { diff --git a/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java b/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java index 95221fb3868..c234f6cfbf4 100644 --- a/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/DeleteAccountRequestAction.java @@ -12,7 +12,7 @@ public JsonResult execute() { String email = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_EMAIL); String institute = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_INSTITUTION); logic.deleteAccountRequest(email, institute); - return new JsonResult("Account request is successfully deleted."); + return new JsonResult("Account request successfully deleted."); } } diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 9b883aa0dff..54579c2fd40 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -46,7 +46,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { assertEquals(accountRequest.getInstitute(), createdAccountRequest.getInstitute()); assertNotNull(createdAccountRequest.getRegistrationKey()); - ______TS("duplicate account, account request updated"); + ______TS("duplicate account request, account request updated"); AccountRequestAttributes duplicateAccount = AccountRequestAttributes .builder("valid@test.com", "TEAMMATES Test Institute 2") @@ -81,7 +81,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { public void testDeleteAccountRequest() throws Exception { AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); - ______TS("silent deletion of non-existent account"); + ______TS("silent deletion of non-existent account request"); accountRequestsLogic.deleteAccountRequest("not_exist", "not_exist"); @@ -93,7 +93,7 @@ public void testDeleteAccountRequest() throws Exception { verifyAbsentInDatabase(a); - ______TS("silent deletion of same account"); + ______TS("silent deletion of same account request"); accountRequestsLogic.deleteAccountRequest(a.getEmail(), a.getInstitute()); diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 98cf6ff5cea..80d3e12ded3 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -28,7 +28,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { accountRequest = accountRequestsDb.createOrUpdateAccountRequest(accountRequest); verifyPresentInDatabase(accountRequest); - ______TS("duplicate account, account request updated"); + ______TS("duplicate account request, account request updated"); AccountRequestAttributes duplicateAccount = AccountRequestAttributes .builder("valid@test.com", "TEAMMATES Test Institute 1") @@ -69,7 +69,7 @@ public void testDeleteAccountRequest() throws Exception { accountRequestsDb.saveEntity(a.toEntity()); - ______TS("silent deletion of non-existent account"); + ______TS("silent deletion of non-existent account request"); accountRequestsDb.deleteAccountRequest("not_exist", "not_exist"); @@ -79,7 +79,7 @@ public void testDeleteAccountRequest() throws Exception { accountRequestsDb.deleteAccountRequest(a.getEmail(), a.getInstitute()); verifyAbsentInDatabase(a); - ______TS("silent deletion of same account"); + ______TS("silent deletion of same account request"); accountRequestsDb.deleteAccountRequest(a.getEmail(), a.getInstitute()); diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index 0792bc4950b..334bd849a99 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -75,7 +75,7 @@ protected void testExecute() throws Exception { verifySpecifiedTasksAdded(Const.TaskQueue.SEARCH_INDEXING_QUEUE_NAME, studentList.size() + instructorList.size()); - ______TS("Error: reg key not found"); + ______TS("Error: account request not found"); verifyEntityNotFound(params); verifyNoTasksAdded(); diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index c2adf8355ea..cb57117728e 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -96,7 +96,6 @@ protected void testExecute() throws Exception { verifyNoEmailsSent(); verifyNoTasksAdded(); - } @Override diff --git a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java index 57e49ddb1a1..46db65be439 100644 --- a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java @@ -43,7 +43,7 @@ protected void testExecute() { MessageOutput msg = (MessageOutput) result.getOutput(); - assertEquals(msg.getMessage(), "Account request is successfully deleted."); + assertEquals(msg.getMessage(), "Account request successfully deleted."); assertNull(logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); @@ -54,7 +54,7 @@ protected void testExecute() { msg = (MessageOutput) result.getOutput(); // should fail silently. - assertEquals(msg.getMessage(), "Account request is successfully deleted."); + assertEquals(msg.getMessage(), "Account request successfully deleted."); } diff --git a/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap b/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap index ba03a0b004c..0eeb8e3fc96 100644 --- a/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap +++ b/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap @@ -554,7 +554,7 @@ exports[`AdminHomePageComponent should snap with some instructors details 1`] = > Instructor "Instructor B" has been successfully created. [ join link diff --git a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts index 641ad627d65..03dce0ee274 100644 --- a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts +++ b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts @@ -157,7 +157,7 @@ describe('AdminHomePageComponent', () => { }, ]; spyOn(service, 'createAccountRequest').and.returnValue(of({ - joinLink: 'http://localhost:4200/web/createaccount', + joinLink: 'http://localhost:4200/web/join', })); fixture.detectChanges(); @@ -165,7 +165,7 @@ describe('AdminHomePageComponent', () => { button.click(); expect(component.instructorsConsolidated[0].status).toEqual('SUCCESS'); - expect(component.instructorsConsolidated[0].joinLink).toEqual('http://localhost:4200/web/createaccount'); + expect(component.instructorsConsolidated[0].joinLink).toEqual('http://localhost:4200/web/join'); expect(component.activeRequests).toEqual(0); }); @@ -210,7 +210,7 @@ describe('AdminHomePageComponent', () => { email: 'instructorb@example.com', institution: 'Sample Institution B', status: 'SUCCESS', - joinLink: 'http://localhost:4200/web/createaccount', + joinLink: 'http://localhost:4200/web/join', message: 'This should not be displayed', }, { @@ -254,7 +254,7 @@ describe('AdminHomePageComponent', () => { email: 'instructorb@example.com', institution: 'Sample Institution B', status: 'SUCCESS', - joinLink: 'http://localhost:4200/web/createaccount', + joinLink: 'http://localhost:4200/web/join', message: 'This should not be displayed', }, { From bedcaabc6b4f9e63f1ce7a229c2f0c13318939d5 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 9 Sep 2021 18:51:07 +0800 Subject: [PATCH 28/79] Remove trailing whitespace --- src/main/java/teammates/storage/api/AccountRequestsDb.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/teammates/storage/api/AccountRequestsDb.java b/src/main/java/teammates/storage/api/AccountRequestsDb.java index 3083bc5ad28..849da9f546e 100644 --- a/src/main/java/teammates/storage/api/AccountRequestsDb.java +++ b/src/main/java/teammates/storage/api/AccountRequestsDb.java @@ -104,7 +104,7 @@ LoadType load() { @Override boolean hasExistingEntities(AccountRequestAttributes entityToCreate) { - Key keyToFind = Key.create(AccountRequest.class, + Key keyToFind = Key.create(AccountRequest.class, AccountRequest.generateId(entityToCreate.getEmail(), entityToCreate.getInstitute())); return !load().filterKey(keyToFind).keys().list().isEmpty(); } From e042bce2e33a0a5f290d5bde02616592473c494b Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Mon, 15 Nov 2021 23:14:30 +0800 Subject: [PATCH 29/79] Add placeholder skeleton to frontend --- src/web/app/user-join-page.component.ts | 46 ++++++++++++++----------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/web/app/user-join-page.component.ts b/src/web/app/user-join-page.component.ts index ff9c43ea98e..8931bdd4b05 100644 --- a/src/web/app/user-join-page.component.ts +++ b/src/web/app/user-join-page.component.ts @@ -43,6 +43,7 @@ export class UserJoinPageComponent implements OnInit { this.route.queryParams.subscribe((queryParams: any) => { this.entityType = queryParams.entitytype; this.key = queryParams.key; + this.isCreatingAccount = queryParams.iscreatingaccount === 'true'; const nextUrl: string = `${window.location.pathname}${window.location.search.replace(/&/g, '%26')}`; this.authService.getAuthUser(undefined, nextUrl).subscribe((auth: AuthInfo) => { @@ -56,25 +57,30 @@ export class UserJoinPageComponent implements OnInit { return; } this.userId = auth.user.id; - this.courseService.getJoinCourseStatus(this.key, this.entityType).subscribe((resp: JoinStatus) => { - this.hasJoined = resp.hasJoined; - if (this.hasJoined) { - // The regkey has been used; simply redirect the user to their home page, - // regardless of whether the regkey matches or not. - this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); - } else { - this.isLoading = false; - } - }, (resp: ErrorMessageOutput) => { - if (resp.status === 404) { - this.validUrl = false; - this.isLoading = false; - return; - } - const modalRef: any = this.ngbModal.open(ErrorReportComponent); - modalRef.componentInstance.requestId = resp.error.requestId; - modalRef.componentInstance.errorMessage = resp.error.message; - }); + + if (this.isCreatingAccount) { + // TODO Handle creation of account + } else { + this.courseService.getJoinCourseStatus(this.key, this.entityType).subscribe((resp: JoinStatus) => { + this.hasJoined = resp.hasJoined; + if (this.hasJoined) { + // The regkey has been used; simply redirect the user to their home page, + // regardless of whether the regkey matches or not. + this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); + } else { + this.isLoading = false; + } + }, (resp: ErrorMessageOutput) => { + if (resp.status === 404) { + this.validUrl = false; + this.isLoading = false; + return; + } + const modalRef: any = this.ngbModal.open(ErrorReportComponent); + modalRef.componentInstance.requestId = resp.error.requestId; + modalRef.componentInstance.errorMessage = resp.error.message; + }); + } }); }); } @@ -101,7 +107,7 @@ export class UserJoinPageComponent implements OnInit { .createAccount(this.key) .pipe(finalize(() => (this.isLoading = false))) .subscribe( - (_resp: MessageOutput) => { + () => { this.navigationService.navigateByURL(this.router, '/web/instructor'); }, (resp: ErrorMessageOutput) => { From aa92dee90df23a53a85c7c6f68fcdfde7c709794 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 16 Nov 2021 00:05:11 +0800 Subject: [PATCH 30/79] Update AccountRequest and DTO --- .../attributes/AccountRequestAttributes.java | 48 ++++++++++++++----- .../storage/entity/AccountRequest.java | 11 +++++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 772fae32136..9e11bf2245e 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -1,5 +1,6 @@ package teammates.common.datatransfer.attributes; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -16,7 +17,9 @@ public class AccountRequestAttributes extends EntityAttributes { private String email; private String name; private String institute; - private String registrationKey; + private Instant registeredAt; + private transient String registrationKey; + private transient Instant createdAt; private AccountRequestAttributes(String email, String institute) { this.email = email; @@ -24,6 +27,8 @@ private AccountRequestAttributes(String email, String institute) { this.name = null; this.registrationKey = null; + this.registeredAt = null; + this.createdAt = null; } /** @@ -35,7 +40,9 @@ public static AccountRequestAttributes valueOf(AccountRequest accountRequest) { accountRequestAttributes.registrationKey = accountRequest.getRegistrationKey(); accountRequestAttributes.name = accountRequest.getName(); - + accountRequestAttributes.registeredAt = accountRequest.getRegisteredAt(); + accountRequestAttributes.createdAt = accountRequestAttributes.getCreatedAt(); + return accountRequestAttributes; } @@ -62,6 +69,18 @@ public String getInstitute() { return institute; } + public Instant getRegisteredAt() { + return registeredAt; + } + + public void setRegisteredAt(Instant registeredAt) { + this.registeredAt = registeredAt; + } + + public Instant getCreatedAt() { + return createdAt; + } + @Override public List getInvalidityInfo() { @@ -79,7 +98,15 @@ public AccountRequest toEntity() { AccountRequest accountRequest = new AccountRequest(getEmail(), getName(), getInstitute()); if (this.getRegistrationKey() != null) { - accountRequest.setRegistrationKey(getRegistrationKey()); + accountRequest.setRegistrationKey(this.getRegistrationKey()); + } + + if (this.getCreatedAt() != null) { + accountRequest.setCreatedAt(this.getCreatedAt()); + } + + if (this.getRegisteredAt() != null) { + accountRequest.setRegisteredAt(this.getRegisteredAt()); } return accountRequest; @@ -87,13 +114,13 @@ public AccountRequest toEntity() { @Override public String toString() { - return "[" + AccountRequestAttributes.class.getSimpleName() + "] registrationKey: " + getRegistrationKey() - + " email: " + getEmail() + " name: " + getName() + " institute: " + getInstitute(); + return "[" + AccountRequestAttributes.class.getSimpleName() + "] email: " + + getEmail() + " name: " + getName() + " institute: " + getInstitute(); } @Override public int hashCode() { - return (this.email + this.name + this.institute + this.registrationKey).hashCode(); + return (this.email + this.name + this.institute).hashCode(); } @Override @@ -103,11 +130,10 @@ public boolean equals(Object other) { } else if (this == other) { return true; } else if (this.getClass() == other.getClass()) { - AccountRequestAttributes otherCourse = (AccountRequestAttributes) other; - return Objects.equals(this.email, otherCourse.email) - && Objects.equals(this.institute, otherCourse.institute) - && Objects.equals(this.name, otherCourse.name) - && Objects.equals(this.registrationKey, otherCourse.registrationKey); + AccountRequestAttributes otherAccountRequest = (AccountRequestAttributes) other; + return Objects.equals(this.email, otherAccountRequest.email) + && Objects.equals(this.institute, otherAccountRequest.institute) + && Objects.equals(this.name, otherAccountRequest.name); } else { return false; } diff --git a/src/main/java/teammates/storage/entity/AccountRequest.java b/src/main/java/teammates/storage/entity/AccountRequest.java index 34a814afa34..d67b1fc419b 100644 --- a/src/main/java/teammates/storage/entity/AccountRequest.java +++ b/src/main/java/teammates/storage/entity/AccountRequest.java @@ -28,6 +28,9 @@ public class AccountRequest extends BaseEntity { private String institute; + @Translate(InstantTranslatorFactory.class) + private Instant registeredAt; + @Translate(InstantTranslatorFactory.class) private Instant createdAt; @@ -85,6 +88,14 @@ public void setInstitute(String institute) { this.institute = institute.trim(); } + public Instant getRegisteredAt() { + return registeredAt; + } + + public void setRegisteredAt(Instant registeredAt) { + this.registeredAt = registeredAt; + } + public Instant getCreatedAt() { return createdAt; } From 6abc921a5cf1761c39ee1dd2626d190b953781dc Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 16 Nov 2021 00:13:43 +0800 Subject: [PATCH 31/79] Move registration url creation to accountRequest dto --- .../attributes/AccountRequestAttributes.java | 10 ++++++++++ .../ui/webapi/CreateAccountRequestAction.java | 7 +------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 9e11bf2245e..d203cd0a10e 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -5,6 +5,8 @@ import java.util.List; import java.util.Objects; +import teammates.common.util.Config; +import teammates.common.util.Const; import teammates.common.util.FieldValidator; import teammates.common.util.SanitizationHelper; import teammates.storage.entity.AccountRequest; @@ -81,6 +83,14 @@ public Instant getCreatedAt() { return createdAt; } + public String getRegistrationUrl() { + return Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) + .withIsCreatingAccount(String.valueOf(true)) + .withRegistrationKey(this.getRegistrationKey()) + .withEntityType(Const.EntityType.INSTRUCTOR) + .toAbsoluteString(); + } + @Override public List getInvalidityInfo() { diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index ec031eec19e..746aea7ae12 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -2,8 +2,6 @@ import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.exception.InvalidParametersException; -import teammates.common.util.Config; -import teammates.common.util.Const; import teammates.common.util.EmailWrapper; import teammates.ui.output.JoinLinkData; import teammates.ui.request.AccountCreateRequest; @@ -33,10 +31,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera throw new InvalidHttpRequestBodyException(ipe); } - String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) - .withIsCreatingAccount(String.valueOf(true)) - .withRegistrationKey(accountRequestAttributes.getRegistrationKey()) - .toAbsoluteString(); + String joinLink = accountRequestAttributes.getRegistrationUrl(); EmailWrapper email = emailGenerator.generateNewInstructorAccountJoinEmail( instructorEmail, instructorName, joinLink); From 92b9db03ae80d1bdad5121a96f998a2744986368 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 16 Nov 2021 00:22:51 +0800 Subject: [PATCH 32/79] Update createAccountAction to soft delete --- .../java/teammates/ui/webapi/CreateAccountAction.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 7f7456e0d19..579a1c209ae 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -1,5 +1,6 @@ package teammates.ui.webapi; +import java.time.Instant; import java.util.List; import org.apache.http.HttpStatus; @@ -48,6 +49,10 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera throw new EntityNotFoundException(ednee); } + if (accountRequestAttributes.getRegisteredAt() != null) { + throw new InvalidOperationException("The registration key " + registrationKey + " has already been used."); + } + String instructorEmail = accountRequestAttributes.getEmail(); String instructorName = accountRequestAttributes.getName(); String instructorInstitution = accountRequestAttributes.getInstitute(); @@ -66,6 +71,8 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera try { logic.joinCourseForInstructor(instructorList.get(0).getKey(), userInfo.id); + accountRequestAttributes.setRegisteredAt(Instant.now()); + logic.createOrUpdateAccountRequest(accountRequestAttributes); } catch (EntityDoesNotExistException ednee) { throw new EntityNotFoundException(ednee); } catch (EntityAlreadyExistsException eaee) { @@ -74,9 +81,6 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera throw new InvalidHttpRequestBodyException(ipe); } - // Delete account request as it is no longer needed - logic.deleteAccountRequest(instructorEmail, instructorInstitution); - return new JsonResult("Account successfully created", HttpStatus.SC_OK); } From 33a2f2cce8f65b187ef161aaad83c7b0124e46d4 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 16 Nov 2021 01:37:16 +0800 Subject: [PATCH 33/79] Update component tests --- .../attributes/AccountRequestAttributes.java | 14 ++++++++++---- .../teammates/ui/output/AccountRequestData.java | 8 ++++++++ .../teammates/ui/webapi/CreateAccountAction.java | 5 ++++- .../attributes/AccountRequestAttributesTest.java | 16 +++++++++++++++- .../ui/webapi/CreateAccountActionTest.java | 5 +++++ .../webapi/CreateAccountRequestActionTest.java | 3 +-- .../ui/webapi/GetAccountRequestActionTest.java | 2 ++ src/test/resources/data/typicalDataBundle.json | 1 - 8 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index d203cd0a10e..27d20d7a9f6 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -75,10 +75,6 @@ public Instant getRegisteredAt() { return registeredAt; } - public void setRegisteredAt(Instant registeredAt) { - this.registeredAt = registeredAt; - } - public Instant getCreatedAt() { return createdAt; } @@ -162,6 +158,7 @@ public void sanitizeForSaving() { public void update(UpdateOptions updateOptions) { updateOptions.registrationKeyOption.ifPresent(s -> registrationKey = s); updateOptions.nameOption.ifPresent(s -> name = s); + updateOptions.registeredAtOption.ifPresent(s -> registeredAt = s); } /** @@ -202,6 +199,7 @@ public static class UpdateOptions { private UpdateOption nameOption = UpdateOption.empty(); private UpdateOption registrationKeyOption = UpdateOption.empty(); + private UpdateOption registeredAtOption = UpdateOption.empty(); private UpdateOptions(String email, String institute) { assert email != null; @@ -218,6 +216,7 @@ public String toString() { + ", institute = " + institute + ", name = " + nameOption + ", registrationKey = " + registrationKeyOption + + ", registeredAt = " + registeredAtOption + "]"; } @@ -269,6 +268,13 @@ public B withRegistrationKey(String registationKey) { return thisBuilder; } + public B withRegisteredAt(Instant registeredAt) { + assert registeredAt != null; + + updateOptions.registeredAtOption = UpdateOption.of(registeredAt); + return thisBuilder; + } + public abstract T build(); } diff --git a/src/main/java/teammates/ui/output/AccountRequestData.java b/src/main/java/teammates/ui/output/AccountRequestData.java index d6ea3d6fed1..1ca3bd7c1d7 100644 --- a/src/main/java/teammates/ui/output/AccountRequestData.java +++ b/src/main/java/teammates/ui/output/AccountRequestData.java @@ -1,5 +1,7 @@ package teammates.ui.output; +import java.time.Instant; + import teammates.common.datatransfer.attributes.AccountRequestAttributes; /** @@ -11,12 +13,14 @@ public class AccountRequestData extends ApiOutput { private final String name; private final String institute; private final String registrationKey; + private final Instant registeredAt; public AccountRequestData(AccountRequestAttributes accountRequestInfo) { this.name = accountRequestInfo.getName(); this.email = accountRequestInfo.getEmail(); this.institute = accountRequestInfo.getInstitute(); this.registrationKey = accountRequestInfo.getRegistrationKey(); + this.registeredAt = accountRequestInfo.getRegisteredAt(); } public String getInstitute() { @@ -35,4 +39,8 @@ public String getRegistrationKey() { return registrationKey; } + public Instant getRegisteredAt() { + return registeredAt; + } + } diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 579a1c209ae..feb70c0d1eb 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -71,7 +71,10 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera try { logic.joinCourseForInstructor(instructorList.get(0).getKey(), userInfo.id); - accountRequestAttributes.setRegisteredAt(Instant.now()); + accountRequestAttributes.update(AccountRequestAttributes + .updateOptionsBuilder(instructorEmail, instructorInstitution) + .withRegisteredAt(Instant.now()) + .build()); logic.createOrUpdateAccountRequest(accountRequestAttributes); } catch (EntityDoesNotExistException ednee) { throw new EntityNotFoundException(ednee); diff --git a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java index f7a0cca0663..0a95a69c435 100644 --- a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java +++ b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java @@ -2,6 +2,7 @@ import org.testng.annotations.Test; +import teammates.common.util.Const; import teammates.common.util.FieldValidator; import teammates.common.util.StringHelper; import teammates.storage.entity.AccountRequest; @@ -23,6 +24,18 @@ public void testValueOf_withTypicalData_shouldGenerateAttributesCorrectly() { assertEquals("Valid Name", accountRequestAttributes.getName()); assertEquals("valid@test.com", accountRequestAttributes.getEmail()); assertEquals("Valid Institute", accountRequestAttributes.getInstitute()); + assertNull(accountRequestAttributes.getRegisteredAt()); + } + + @Test + public void testValueOf_registeredWithTypicalData_shouldGenerateAttributesCorrectly() { + AccountRequest accountRequest = + new AccountRequest("valid@test.com", "Valid Name", "Valid Institute"); + accountRequest.setRegisteredAt(Const.TIME_REPRESENTS_NOW); + + AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes.valueOf(accountRequest); + + assertEquals(Const.TIME_REPRESENTS_NOW, accountRequestAttributes.getRegisteredAt()); } @Test @@ -53,6 +66,7 @@ public void testBuilder_buildNothing_shouldUseDefaultValues() { assertEquals("valid institute", accountRequestAttributes.getInstitute()); assertNull(accountRequestAttributes.getName()); assertNull(accountRequestAttributes.getRegistrationKey()); + assertNull(accountRequestAttributes.getRegisteredAt()); } @Test @@ -117,7 +131,7 @@ public void testIsValid() { @Test public void testToString() { AccountRequestAttributes a = getValidAccountRequestAttributesObject(); - assertEquals("[AccountRequestAttributes] registrationKey: valid123 email: valid@test.com " + assertEquals("[AccountRequestAttributes] email: valid@test.com " + "name: valid-name institute: valid-institute", a.toString()); } diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index 334bd849a99..8b49d0e3dd4 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -75,8 +75,13 @@ protected void testExecute() throws Exception { verifySpecifiedTasksAdded(Const.TaskQueue.SEARCH_INDEXING_QUEUE_NAME, studentList.size() + instructorList.size()); + ______TS("Error: registration key already used"); + verifyInvalidOperation(params); + verifyNoTasksAdded(); + ______TS("Error: account request not found"); + params = new String[] { Const.ParamsNames.REGKEY, "unknownregkey", }; verifyEntityNotFound(params); verifyNoTasksAdded(); } diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index cb57117728e..6c2c7829258 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -58,8 +58,6 @@ protected void testExecute() throws Exception { CreateAccountRequestAction a = getAction(req); JsonResult r = getJsonResult(a); - assertEquals(HttpStatus.SC_OK, r.getStatusCode()); - AccountRequestAttributes accountRequestAttributes = logic.getAccountRequest(email, institute); assertEquals(name, accountRequestAttributes.getName()); @@ -70,6 +68,7 @@ protected void testExecute() throws Exception { String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) .withIsCreatingAccount(String.valueOf(true)) .withRegistrationKey(accountRequestAttributes.getRegistrationKey()) + .withEntityType(Const.EntityType.INSTRUCTOR) .toAbsoluteString(); JoinLinkData output = (JoinLinkData) r.getOutput(); diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index cf90b175ae0..acad01e57e0 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -25,6 +25,7 @@ protected String getRequestMethod() { @Test protected void testExecute() { AccountRequestAttributes accountRequest = typicalBundle.accountRequests.get("accountRequest1"); + accountRequest = logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); loginAsAdmin(); @@ -58,6 +59,7 @@ protected void testExecute() { assertEquals(response.getEmail(), accountRequest.getEmail()); assertEquals(response.getRegistrationKey(), accountRequest.getRegistrationKey()); assertEquals(response.getInstitute(), accountRequest.getInstitute()); + assertEquals(response.getRegisteredAt(), accountRequest.getRegisteredAt()); } @Override diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index 01d055df55e..8a1cbd5ffa2 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1719,7 +1719,6 @@ "accountRequests": { "accountRequest1": { "email": "typical@gmail.tmt", - "registrationKey": "123456", "institute": "TEAMMATES Test Institute 1", "name": "Typical Instructor Name" } From e3048ef7eb7fe638a254eac9006cd9ec212f88ec Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 16 Nov 2021 02:42:52 +0800 Subject: [PATCH 34/79] Add endpoint to get account request status --- .../attributes/AccountRequestAttributes.java | 4 +- .../java/teammates/common/util/Const.java | 1 + .../java/teammates/ui/output/JoinStatus.java | 2 +- .../teammates/ui/webapi/ActionFactory.java | 1 + .../webapi/GetAccountRequestStatusAction.java | 40 ++++++++++ .../CreateAccountRequestActionTest.java | 1 - .../webapi/GetAccountRequestActionTest.java | 4 +- .../GetAccountRequestStatusActionTest.java | 73 +++++++++++++++++++ .../resources/data/typicalDataBundle.json | 10 ++- 9 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 src/main/java/teammates/ui/webapi/GetAccountRequestStatusAction.java create mode 100644 src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 27d20d7a9f6..0c926186748 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -44,7 +44,7 @@ public static AccountRequestAttributes valueOf(AccountRequest accountRequest) { accountRequestAttributes.name = accountRequest.getName(); accountRequestAttributes.registeredAt = accountRequest.getRegisteredAt(); accountRequestAttributes.createdAt = accountRequestAttributes.getCreatedAt(); - + return accountRequestAttributes; } @@ -120,7 +120,7 @@ public AccountRequest toEntity() { @Override public String toString() { - return "[" + AccountRequestAttributes.class.getSimpleName() + "] email: " + return "[" + AccountRequestAttributes.class.getSimpleName() + "] email: " + getEmail() + " name: " + getName() + " institute: " + getInstitute(); } diff --git a/src/main/java/teammates/common/util/Const.java b/src/main/java/teammates/common/util/Const.java index ac1a480475d..865c18d0032 100644 --- a/src/main/java/teammates/common/util/Const.java +++ b/src/main/java/teammates/common/util/Const.java @@ -301,6 +301,7 @@ public static class ResourceURIs { public static final String ACCOUNT_RESET = URI_PREFIX + "/account/reset"; public static final String ACCOUNT_DOWNGRADE = URI_PREFIX + "/account/downgrade"; public static final String ACCOUNT_REQUEST = URI_PREFIX + "/account/request"; + public static final String ACCOUNT_REQUEST_STATUS = URI_PREFIX + "/account/request/status"; public static final String RESPONSE_COMMENT = URI_PREFIX + "/responsecomment"; public static final String COURSE = URI_PREFIX + "/course"; public static final String COURSE_ARCHIVE = URI_PREFIX + "/course/archive"; diff --git a/src/main/java/teammates/ui/output/JoinStatus.java b/src/main/java/teammates/ui/output/JoinStatus.java index aa3a04a126e..ecac63612e9 100644 --- a/src/main/java/teammates/ui/output/JoinStatus.java +++ b/src/main/java/teammates/ui/output/JoinStatus.java @@ -1,7 +1,7 @@ package teammates.ui.output; /** - * The join status of a course. + * The join status of a course or account request. */ public class JoinStatus extends ApiOutput { diff --git a/src/main/java/teammates/ui/webapi/ActionFactory.java b/src/main/java/teammates/ui/webapi/ActionFactory.java index 3c45fb165a6..5dfcde9df12 100644 --- a/src/main/java/teammates/ui/webapi/ActionFactory.java +++ b/src/main/java/teammates/ui/webapi/ActionFactory.java @@ -49,6 +49,7 @@ public final class ActionFactory { map(ResourceURIs.ACCOUNT_REQUEST, GET, GetAccountRequestAction.class); map(ResourceURIs.ACCOUNT_REQUEST, POST, CreateAccountRequestAction.class); map(ResourceURIs.ACCOUNT_REQUEST, DELETE, DeleteAccountRequestAction.class); + map(ResourceURIs.ACCOUNT_REQUEST_STATUS, GET, GetAccountRequestStatusAction.class); map(ResourceURIs.COURSE, GET, GetCourseAction.class); map(ResourceURIs.COURSE, DELETE, DeleteCourseAction.class); map(ResourceURIs.COURSE, POST, CreateCourseAction.class); diff --git a/src/main/java/teammates/ui/webapi/GetAccountRequestStatusAction.java b/src/main/java/teammates/ui/webapi/GetAccountRequestStatusAction.java new file mode 100644 index 00000000000..d071b568e2f --- /dev/null +++ b/src/main/java/teammates/ui/webapi/GetAccountRequestStatusAction.java @@ -0,0 +1,40 @@ +package teammates.ui.webapi; + +import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityDoesNotExistException; +import teammates.common.util.Const; +import teammates.ui.output.JoinStatus; + +/** + * Get the join status of account request. + */ +class GetAccountRequestStatusAction extends Action { + + @Override + AuthType getMinAuthLevel() { + return AuthType.LOGGED_IN; + } + + @Override + void checkSpecificAccessControl() { + // Any user can use a join link as long as its parameters are valid + } + + @Override + public JsonResult execute() { + String regkey = getNonNullRequestParamValue(Const.ParamsNames.REGKEY); + + try { + AccountRequestAttributes accountRequest = logic.getAccountRequestForRegistrationKey(regkey); + boolean hasJoined = accountRequest.getRegisteredAt() != null; + return getJoinStatusResult(hasJoined); + } catch (EntityDoesNotExistException ednee) { + throw new EntityNotFoundException(ednee); + } + } + + private JsonResult getJoinStatusResult(boolean hasJoined) { + JoinStatus result = new JoinStatus(hasJoined); + return new JsonResult(result); + } +} diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index 6c2c7829258..5e8ece81308 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -1,6 +1,5 @@ package teammates.ui.webapi; -import org.apache.http.HttpStatus; import org.testng.annotations.Test; import teammates.common.datatransfer.attributes.AccountRequestAttributes; diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index acad01e57e0..f6df437263b 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -24,8 +24,8 @@ protected String getRequestMethod() { @Override @Test protected void testExecute() { - AccountRequestAttributes accountRequest = typicalBundle.accountRequests.get("accountRequest1"); - accountRequest = logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); + AccountRequestAttributes accountRequest = + logic.getAccountRequest("typical@gmail.tmt", "TEAMMATES Test Institute 1"); loginAsAdmin(); diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java new file mode 100644 index 00000000000..454251b6f6b --- /dev/null +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java @@ -0,0 +1,73 @@ +package teammates.ui.webapi; + +import org.testng.annotations.Test; + +import teammates.common.util.Const; +import teammates.ui.output.JoinStatus; + +/** + * SUT: {@link GetAccountRequestStatusAction}. + */ +public class GetAccountRequestStatusActionTest extends BaseActionTest { + + @Override + protected String getActionUri() { + return Const.ResourceURIs.ACCOUNT_REQUEST_STATUS; + } + + @Override + protected String getRequestMethod() { + return GET; + } + + @Override + @Test + protected void testExecute() { + + loginAsUnregistered("unreg.user"); + + ______TS("Not enough parameters"); + + verifyHttpParameterFailure(); + + ______TS("Normal case: account request not used"); + + String registrationKey = + logic.getAccountRequest("typical@gmail.tmt", "TEAMMATES Test Institute 1").getRegistrationKey(); + + String[] params = new String[] { Const.ParamsNames.REGKEY, registrationKey, }; + + GetAccountRequestStatusAction getAccountRequestStatusAction = getAction(params); + JsonResult result = getJsonResult(getAccountRequestStatusAction); + + JoinStatus output = (JoinStatus) result.getOutput(); + assertFalse(output.getHasJoined()); + + ______TS("Normal case: account request already used"); + + String usedRegistrationKey = + logic.getAccountRequest("typical2@gmail.tmt", "TEAMMATES Test Institute 2").getRegistrationKey(); + + params = new String[] { Const.ParamsNames.REGKEY, usedRegistrationKey, }; + + getAccountRequestStatusAction = getAction(params); + result = getJsonResult(getAccountRequestStatusAction); + + output = (JoinStatus) result.getOutput(); + assertTrue(output.getHasJoined()); + + ______TS("Failure case: regkey is not valid"); + + params = new String[] { Const.ParamsNames.REGKEY, "invalid-registration-key", }; + + verifyEntityNotFound(params); + + } + + @Override + @Test + protected void testAccessControl() { + verifyAnyLoggedInUserCanAccess(); + } + +} diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index 8a1cbd5ffa2..a743509c0ae 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1720,7 +1720,15 @@ "accountRequest1": { "email": "typical@gmail.tmt", "institute": "TEAMMATES Test Institute 1", - "name": "Typical Instructor Name" + "name": "Typical Instructor Name", + "createdAt": "2011-01-01T00:00:00Z" + }, + "accountRequest2": { + "email": "typical2@gmail.tmt", + "institute": "TEAMMATES Test Institute 2", + "name": "Typical Instructor Name", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" } } } From 94492bce8a37897c1fc22fcc6ae957614f4d6699 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 16 Nov 2021 03:00:11 +0800 Subject: [PATCH 35/79] Update user join page --- .../ui/constants/ResourceEndpoints.java | 1 + src/web/app/user-join-page.component.ts | 22 +++++++++++++++++-- src/web/services/account.service.ts | 10 ++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/java/teammates/ui/constants/ResourceEndpoints.java b/src/main/java/teammates/ui/constants/ResourceEndpoints.java index 4eb38fc7d8a..09548ac7366 100644 --- a/src/main/java/teammates/ui/constants/ResourceEndpoints.java +++ b/src/main/java/teammates/ui/constants/ResourceEndpoints.java @@ -16,6 +16,7 @@ public enum ResourceEndpoints { ACCOUNT_RESET(ResourceURIs.ACCOUNT_RESET), ACCOUNT_DOWNGRADE(ResourceURIs.ACCOUNT_DOWNGRADE), ACCOUNT_REQUEST(ResourceURIs.ACCOUNT_REQUEST), + ACCOUNT_REQUEST_STATUS(ResourceURIs.ACCOUNT_REQUEST_STATUS), RESPONSE_COMMENT(ResourceURIs.RESPONSE_COMMENT), COURSE(ResourceURIs.COURSE), COURSE_ARCHIVE(ResourceURIs.COURSE_ARCHIVE), diff --git a/src/web/app/user-join-page.component.ts b/src/web/app/user-join-page.component.ts index 8931bdd4b05..dc9ff5044c1 100644 --- a/src/web/app/user-join-page.component.ts +++ b/src/web/app/user-join-page.component.ts @@ -7,7 +7,7 @@ import { AccountService } from '../services/account.service'; import { AuthService } from '../services/auth.service'; import { CourseService } from '../services/course.service'; import { NavigationService } from '../services/navigation.service'; -import { AuthInfo, JoinStatus, MessageOutput } from '../types/api-output'; +import { AuthInfo, JoinStatus } from '../types/api-output'; import { ErrorReportComponent } from './components/error-report/error-report.component'; import { ErrorMessageOutput } from './error-message-output'; @@ -59,7 +59,25 @@ export class UserJoinPageComponent implements OnInit { this.userId = auth.user.id; if (this.isCreatingAccount) { - // TODO Handle creation of account + this.accountService.getRegisteredStatus(this.key).subscribe((resp: JoinStatus) => { + this.hasJoined = resp.hasJoined; + if (this.hasJoined) { + // The regkey has been used; simply redirect the user to their home page, + // regardless of whether the regkey matches or not. + this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); + } else { + this.isLoading = false; + } + }, (resp: ErrorMessageOutput) => { + if (resp.status === 404) { + this.validUrl = false; + this.isLoading = false; + return; + } + const modalRef: any = this.ngbModal.open(ErrorReportComponent); + modalRef.componentInstance.requestId = resp.error.requestId; + modalRef.componentInstance.errorMessage = resp.error.message; + }); } else { this.courseService.getJoinCourseStatus(this.key, this.entityType).subscribe((resp: JoinStatus) => { this.hasJoined = resp.hasJoined; diff --git a/src/web/services/account.service.ts b/src/web/services/account.service.ts index e65362a3516..f1a13117e0d 100644 --- a/src/web/services/account.service.ts +++ b/src/web/services/account.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { ResourceEndpoints } from '../types/api-const'; -import { Account, JoinLink, MessageOutput } from '../types/api-output'; +import { Account, JoinLink, JoinStatus, MessageOutput } from '../types/api-output'; import { AccountCreateRequest } from '../types/api-request'; import { HttpRequestService } from './http-request.service'; @@ -82,4 +82,12 @@ export class AccountService { return this.httpRequestService.get(ResourceEndpoints.ACCOUNT, paramMap); } + /** + * Get the status of whether the registration key has been used by calling API. + */ + getRegisteredStatus(regKey: string): Observable { + const paramMap: Record = { key: regKey, }; + return this.httpRequestService.get(ResourceEndpoints.ACCOUNT_REQUEST_STATUS, paramMap); + } + } From 32b98264ae0fce3fd162d72e259a9b6ee6a2a893 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 17 Nov 2021 21:28:09 +0800 Subject: [PATCH 36/79] Fix indentation --- src/web/services/account.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/services/account.service.ts b/src/web/services/account.service.ts index f1a13117e0d..594d2407354 100644 --- a/src/web/services/account.service.ts +++ b/src/web/services/account.service.ts @@ -85,8 +85,8 @@ export class AccountService { /** * Get the status of whether the registration key has been used by calling API. */ - getRegisteredStatus(regKey: string): Observable { - const paramMap: Record = { key: regKey, }; + getRegisteredStatus(regKey: string): Observable { + const paramMap: Record = { key: regKey }; return this.httpRequestService.get(ResourceEndpoints.ACCOUNT_REQUEST_STATUS, paramMap); } From cb527e4fc3e0dcb6f4f8b19511296ab4ec18b2b6 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 17 Nov 2021 23:09:17 +0800 Subject: [PATCH 37/79] Update tests for user-join-page --- .../user-join-page.component.spec.ts.snap | 112 -------------- src/web/app/user-join-page.component.spec.ts | 139 +++++++++++++++--- 2 files changed, 116 insertions(+), 135 deletions(-) diff --git a/src/web/app/__snapshots__/user-join-page.component.spec.ts.snap b/src/web/app/__snapshots__/user-join-page.component.spec.ts.snap index c115c4f45a6..c0f9c9625be 100644 --- a/src/web/app/__snapshots__/user-join-page.component.spec.ts.snap +++ b/src/web/app/__snapshots__/user-join-page.component.spec.ts.snap @@ -1,50 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UserJoinPageComponent should snap if user is not logged in and has a valid create account url 1`] = ` - -
- -
-
-

- Not logged in -

-
-
- You are not logged in. Please log in first. -
-
- - -
-
-`; - exports[`UserJoinPageComponent should snap if user is not logged in and has a valid url 1`] = ` -
-

- Confirm your Google account -

-
-
- You are currently logged in as - - user - - . -
- If this is not you, please log out and log in using your own Google account. -
- If this is you, please confirm below to complete your registration. -
-
- -
-
-
-
- -`; - -exports[`UserJoinPageComponent should snap with valid create account link 1`] = ` - -
- - -
{ expect(fixture).toMatchSnapshot(); }); - it('should snap if user is not logged in and has a valid create account url', () => { - component.hasJoined = false; - component.userId = ''; - component.validUrl = true; - component.isLoading = false; - component.isCreatingAccount = true; - - fixture.detectChanges(); - - expect(fixture).toMatchSnapshot(); - }); - it('should snap with invalid course join link', () => { component.userId = 'user'; component.validUrl = false; @@ -117,17 +105,6 @@ describe('UserJoinPageComponent', () => { expect(fixture).toMatchSnapshot(); }); - it('should snap with valid create account link', () => { - component.validUrl = true; - component.userId = 'user'; - component.isLoading = false; - component.isCreatingAccount = true; - - fixture.detectChanges(); - - expect(fixture).toMatchSnapshot(); - }); - it('should join course when join course button is clicked on', () => { const params: string[] = ['key', 'student']; component.isLoading = false; @@ -209,3 +186,119 @@ describe('UserJoinPageComponent', () => { expect(component.isLoading).toBeFalsy(); }); }); + +describe('UserJoinPageComponent creating account', () => { + let component: UserJoinPageComponent; + let fixture: ComponentFixture; + let navService: NavigationService; + let authService: AuthService; + let accountService: AccountService; + + beforeEach((() => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + declarations: [UserJoinPageComponent], + imports: [ + HttpClientTestingModule, + RouterTestingModule, + LoadingSpinnerModule, + ], + providers: [ + NavigationService, + CourseService, + AuthService, + AccountService, + { + provide: ActivatedRoute, + useValue: { + queryParams: of({ + iscreatingaccount: 'true', + entitytype: 'instructor', + key: 'key', + }), + }, + }, + ], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserJoinPageComponent); + component = fixture.componentInstance; + navService = TestBed.inject(NavigationService); + authService = TestBed.inject(AuthService); + accountService = TestBed.inject(AccountService); + fixture.detectChanges(); + }); + + it('should create account and join course when join course button is clicked on', () => { + const params: string[] = ['key']; + component.isLoading = false; + component.hasJoined = false; + component.userId = 'user'; + component.isCreatingAccount = true; + component.key = 'key'; + component.entityType = 'instructor'; + component.validUrl = true; + + const accountSpy: Spy = spyOn(accountService, 'createAccount').and.returnValue(of({})); + const navSpy: Spy = spyOn(navService, 'navigateByURL'); + + fixture.detectChanges(); + + const btn: any = fixture.debugElement.nativeElement.querySelector('#btn-confirm'); + btn.click(); + + expect(accountSpy.calls.count()).toEqual(1); + expect(accountSpy.calls.mostRecent().args).toEqual(params); + expect(navSpy.calls.count()).toEqual(1); + expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor'); + }); + + it('should redirect user to home page if user is logged in and URL has been used', () => { + spyOn(authService, 'getAuthUser').and.returnValue(of({ + user: { + id: 'user', + isAdmin: false, + isInstructor: false, + isStudent: false, + isMaintainer: false, + }, + masquerade: false, + })); + spyOn(accountService, 'getRegisteredStatus').and.returnValue(of({ + hasJoined: true, + })); + const navSpy: Spy = spyOn(navService, 'navigateByURL'); + + component.ngOnInit(); + + expect(component.hasJoined).toBeTruthy(); + expect(component.userId).toEqual('user'); + expect(navSpy.calls.count()).toEqual(1); + expect(navSpy.calls.mostRecent().args[1]).toEqual('/web/instructor/home'); + }); + + it('should stop loading and show error message if 404 is returned when creating new account', () => { + spyOn(authService, 'getAuthUser').and.returnValue(of({ + user: { + id: 'user', + isAdmin: false, + isInstructor: false, + isStudent: false, + isMaintainer: false, + }, + masquerade: false, + })); + spyOn(accountService, 'getRegisteredStatus').and.returnValue(throwError({ + status: 404, + })); + + component.ngOnInit(); + + expect(component.entityType).toBe('instructor'); + expect(component.isCreatingAccount).toBeTruthy(); + expect(component.isLoading).toBeFalsy(); + expect(component.validUrl).toBeFalsy(); + }); +}); From 8d451855fc245cf572c9f8dfa68dfc7e9c5d55e1 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 17 Nov 2021 23:39:41 +0800 Subject: [PATCH 38/79] Fix failing component tests --- .../java/teammates/logic/core/AccountRequestsLogicTest.java | 3 ++- .../java/teammates/ui/webapi/GetActionClassesActionTest.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 54579c2fd40..46bd3778306 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -105,7 +105,8 @@ public void testDeleteAccountRequest() throws Exception { @Test public void testGetAccountRequestForRegistrationKey() throws Exception { - AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); + AccountRequestAttributes a = + accountRequestsLogic.getAccountRequest("typical@gmail.tmt", "TEAMMATES Test Institute 1"); ______TS("typical success case"); diff --git a/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java b/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java index 2aacb803c48..c1b1364b20b 100644 --- a/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java @@ -91,6 +91,7 @@ protected void testExecute() { CreateAccountAction.class, CreateAccountRequestAction.class, GetAccountRequestAction.class, + GetAccountRequestStatusAction.class, DeleteAccountRequestAction.class, GetAccountAction.class, FeedbackSessionPublishedRemindersAction.class, From 0fb8280f644dcc596a45fced7079b3d46f945b40 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 20 Nov 2021 21:03:21 +0800 Subject: [PATCH 39/79] Fix typos --- .../datatransfer/attributes/AccountRequestAttributes.java | 8 ++------ .../java/teammates/logic/core/AccountRequestsLogic.java | 2 +- .../java/teammates/storage/entity/AccountRequest.java | 1 + src/main/java/teammates/ui/output/AccountRequestData.java | 6 ++++++ 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 0c926186748..bfd6fcfc063 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -20,13 +20,12 @@ public class AccountRequestAttributes extends EntityAttributes { private String name; private String institute; private Instant registeredAt; + private Instant createdAt; private transient String registrationKey; - private transient Instant createdAt; private AccountRequestAttributes(String email, String institute) { this.email = email; this.institute = institute; - this.name = null; this.registrationKey = null; this.registeredAt = null; @@ -89,7 +88,6 @@ public String getRegistrationUrl() { @Override public List getInvalidityInfo() { - List errors = new ArrayList<>(); addNonEmptyError(FieldValidator.getInvalidityInfoForEmail(getEmail()), errors); @@ -162,7 +160,7 @@ public void update(UpdateOptions updateOptions) { } /** - * Returns a {@link UpdateOptions.Builder} to build {@link UpdateOptions} for an accountRequest. + * Returns a {@link UpdateOptions.Builder} to build {@link UpdateOptions} for an account request. */ public static UpdateOptions.Builder updateOptionsBuilder(String email, String institute) { return new UpdateOptions.Builder(email, institute); @@ -172,7 +170,6 @@ public static UpdateOptions.Builder updateOptionsBuilder(String email, String in * A builder for {@link AccountRequestAttributes}. */ public static class Builder extends BasicBuilder { - private final AccountRequestAttributes accountRequestAttributes; private Builder(String email, String institute) { @@ -224,7 +221,6 @@ public String toString() { * Builder class to build {@link UpdateOptions}. */ public static class Builder extends BasicBuilder { - private Builder(String email, String institute) { super(new UpdateOptions(email, institute)); thisBuilder = this; diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index f9d3c0a4ed7..a97203d2b15 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -38,7 +38,7 @@ public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttri } /** - * Deletes the account request associated with the {@code id}. + * Deletes the account request associated with the email address and institute. * *

Fails silently if the account request doesn't exist.

*/ diff --git a/src/main/java/teammates/storage/entity/AccountRequest.java b/src/main/java/teammates/storage/entity/AccountRequest.java index d67b1fc419b..14e3b7fd34b 100644 --- a/src/main/java/teammates/storage/entity/AccountRequest.java +++ b/src/main/java/teammates/storage/entity/AccountRequest.java @@ -46,6 +46,7 @@ public AccountRequest(String email, String name, String institute) { this.setId(generateId(email, institute)); this.setRegistrationKey(generateRegistrationKey()); this.setCreatedAt(Instant.now()); + this.setRegisteredAt(null); } public String getId() { diff --git a/src/main/java/teammates/ui/output/AccountRequestData.java b/src/main/java/teammates/ui/output/AccountRequestData.java index 1ca3bd7c1d7..aed63694861 100644 --- a/src/main/java/teammates/ui/output/AccountRequestData.java +++ b/src/main/java/teammates/ui/output/AccountRequestData.java @@ -14,6 +14,7 @@ public class AccountRequestData extends ApiOutput { private final String institute; private final String registrationKey; private final Instant registeredAt; + private final Instant createdAt; public AccountRequestData(AccountRequestAttributes accountRequestInfo) { this.name = accountRequestInfo.getName(); @@ -21,6 +22,7 @@ public AccountRequestData(AccountRequestAttributes accountRequestInfo) { this.institute = accountRequestInfo.getInstitute(); this.registrationKey = accountRequestInfo.getRegistrationKey(); this.registeredAt = accountRequestInfo.getRegisteredAt(); + this.createdAt = accountRequestInfo.getCreatedAt(); } public String getInstitute() { @@ -43,4 +45,8 @@ public Instant getRegisteredAt() { return registeredAt; } + public Instant getCreatedAt() { + return createdAt; + } + } From 12af21f92cd11d4c4192dab567c4a0b584852c8e Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 20 Nov 2021 21:34:12 +0800 Subject: [PATCH 40/79] Fix typo which caused createdAt to be null --- .../datatransfer/attributes/AccountRequestAttributes.java | 2 +- .../java/teammates/logic/core/AccountRequestsLogicTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index bfd6fcfc063..dfe9fb8bde8 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -42,7 +42,7 @@ public static AccountRequestAttributes valueOf(AccountRequest accountRequest) { accountRequestAttributes.registrationKey = accountRequest.getRegistrationKey(); accountRequestAttributes.name = accountRequest.getName(); accountRequestAttributes.registeredAt = accountRequest.getRegisteredAt(); - accountRequestAttributes.createdAt = accountRequestAttributes.getCreatedAt(); + accountRequestAttributes.createdAt = accountRequest.getCreatedAt(); return accountRequestAttributes; } diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 46bd3778306..aea092d8381 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -45,6 +45,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { assertEquals(accountRequest.getName(), createdAccountRequest.getName()); assertEquals(accountRequest.getInstitute(), createdAccountRequest.getInstitute()); assertNotNull(createdAccountRequest.getRegistrationKey()); + assertNotNull(createdAccountRequest.getCreatedAt()); ______TS("duplicate account request, account request updated"); From ff6c48311bb8fefb4365bb23c28364a2bf739ed0 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 20 Nov 2021 21:55:22 +0800 Subject: [PATCH 41/79] Fix broken component tests --- .../java/teammates/storage/api/AccountRequestsDbTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 80d3e12ded3..52a35c02c2c 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -64,10 +64,11 @@ public void testDeleteAccountRequest() throws Exception { AccountRequestAttributes a = AccountRequestAttributes .builder("valid2@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") - .withRegistrationKey("123456") + .withRegistrationKey("2-123456") .build(); accountRequestsDb.saveEntity(a.toEntity()); + a = accountRequestsDb.getAccountRequest("valid2@test.com", "TEAMMATES Test Institute 1"); ______TS("silent deletion of non-existent account request"); @@ -93,7 +94,7 @@ public void testDeleteAccountRequest() throws Exception { public void testGetAccountRequestForRegistrationKey() throws Exception { AccountRequestAttributes a = AccountRequestAttributes.builder("valid3@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") - .withRegistrationKey("123456") + .withRegistrationKey("3-123456") .build(); accountRequestsDb.saveEntity(a.toEntity()); @@ -120,7 +121,7 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { public void testGetAccountRequest() throws Exception { AccountRequestAttributes a = AccountRequestAttributes.builder("valid4@test.com", "TEAMMATES Test Institute 1") .withName("Test account Name") - .withRegistrationKey("123456") + .withRegistrationKey("4-123456") .build(); accountRequestsDb.saveEntity(a.toEntity()); From cbb5adf7b9c0979f19447e2153beb80bab11a9c8 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 8 Dec 2021 00:51:01 +0800 Subject: [PATCH 42/79] Update exception to use specific exception type --- src/test/java/teammates/ui/webapi/CreateAccountActionTest.java | 2 +- .../teammates/ui/webapi/CreateAccountRequestActionTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index 8b49d0e3dd4..6ce3498976b 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -48,7 +48,7 @@ protected void testExecute() throws Exception { ______TS("Null parameters"); String[] nullParams = new String[] { Const.ParamsNames.REGKEY, null, }; - Exception ex = verifyHttpParameterFailure(nullParams); + InvalidHttpParameterException ex = verifyHttpParameterFailure(nullParams); assertEquals("The [key] HTTP parameter is null.", ex.getMessage()); verifyNoTasksAdded(); diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index 5e8ece81308..cca2e3885c9 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -9,6 +9,7 @@ import teammates.common.util.EmailWrapper; import teammates.ui.output.JoinLinkData; import teammates.ui.request.AccountCreateRequest; +import teammates.ui.request.InvalidHttpRequestBodyException; /** * SUT: {@link CreateAccountRequestAction}. @@ -35,7 +36,7 @@ protected void testExecute() throws Exception { ______TS("Null parameters"); - Exception ex = verifyHttpRequestBodyFailure(buildCreateRequest(null, institute, email)); + InvalidHttpRequestBodyException ex = verifyHttpRequestBodyFailure(buildCreateRequest(null, institute, email)); assertEquals("name cannot be null", ex.getMessage()); ex = verifyHttpRequestBodyFailure(buildCreateRequest(name, null, email)); From 9958e090e6d5c429231700dd010880c3245493c9 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 8 Dec 2021 00:54:06 +0800 Subject: [PATCH 43/79] Change Instant to Long in AccountRequestData.java --- .../java/teammates/ui/output/AccountRequestData.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/teammates/ui/output/AccountRequestData.java b/src/main/java/teammates/ui/output/AccountRequestData.java index aed63694861..f09ca0ab51d 100644 --- a/src/main/java/teammates/ui/output/AccountRequestData.java +++ b/src/main/java/teammates/ui/output/AccountRequestData.java @@ -13,16 +13,16 @@ public class AccountRequestData extends ApiOutput { private final String name; private final String institute; private final String registrationKey; - private final Instant registeredAt; - private final Instant createdAt; + private final long registeredAt; + private final long createdAt; public AccountRequestData(AccountRequestAttributes accountRequestInfo) { this.name = accountRequestInfo.getName(); this.email = accountRequestInfo.getEmail(); this.institute = accountRequestInfo.getInstitute(); this.registrationKey = accountRequestInfo.getRegistrationKey(); - this.registeredAt = accountRequestInfo.getRegisteredAt(); - this.createdAt = accountRequestInfo.getCreatedAt(); + this.registeredAt = accountRequestInfo.getRegisteredAt().toEpochMilli(); + this.createdAt = accountRequestInfo.getCreatedAt().toEpochMilli(); } public String getInstitute() { @@ -41,11 +41,11 @@ public String getRegistrationKey() { return registrationKey; } - public Instant getRegisteredAt() { + public long getRegisteredAt() { return registeredAt; } - public Instant getCreatedAt() { + public long getCreatedAt() { return createdAt; } From b54289d972939ef6834871f94523936ba061bdaa Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 8 Dec 2021 01:01:18 +0800 Subject: [PATCH 44/79] Refactor CreateAccountRequestActionTest to use getJoinLink method --- src/main/java/teammates/ui/output/AccountRequestData.java | 2 -- .../ui/webapi/CreateAccountRequestActionTest.java | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/teammates/ui/output/AccountRequestData.java b/src/main/java/teammates/ui/output/AccountRequestData.java index f09ca0ab51d..3b30d69ea90 100644 --- a/src/main/java/teammates/ui/output/AccountRequestData.java +++ b/src/main/java/teammates/ui/output/AccountRequestData.java @@ -1,7 +1,5 @@ package teammates.ui.output; -import java.time.Instant; - import teammates.common.datatransfer.attributes.AccountRequestAttributes; /** diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index cca2e3885c9..6a0aa14fa02 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -3,7 +3,6 @@ import org.testng.annotations.Test; import teammates.common.datatransfer.attributes.AccountRequestAttributes; -import teammates.common.util.Config; import teammates.common.util.Const; import teammates.common.util.EmailType; import teammates.common.util.EmailWrapper; @@ -65,12 +64,7 @@ protected void testExecute() throws Exception { assertEquals(institute, accountRequestAttributes.getInstitute()); assertNotNull(accountRequestAttributes.getRegistrationKey()); - String joinLink = Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) - .withIsCreatingAccount(String.valueOf(true)) - .withRegistrationKey(accountRequestAttributes.getRegistrationKey()) - .withEntityType(Const.EntityType.INSTRUCTOR) - .toAbsoluteString(); - + String joinLink = accountRequestAttributes.getRegistrationUrl(); JoinLinkData output = (JoinLinkData) r.getOutput(); assertEquals(joinLink, output.getJoinLink()); From 0536be1d67d9173b6699692bffbe324c0161acab Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 8 Dec 2021 01:28:06 +0800 Subject: [PATCH 45/79] Fix lint issues --- .../java/teammates/ui/output/AccountRequestData.java | 10 +++++++--- .../ui/webapi/GetAccountRequestActionTest.java | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/teammates/ui/output/AccountRequestData.java b/src/main/java/teammates/ui/output/AccountRequestData.java index 3b30d69ea90..6fde8d69a85 100644 --- a/src/main/java/teammates/ui/output/AccountRequestData.java +++ b/src/main/java/teammates/ui/output/AccountRequestData.java @@ -11,7 +11,7 @@ public class AccountRequestData extends ApiOutput { private final String name; private final String institute; private final String registrationKey; - private final long registeredAt; + private final Long registeredAt; private final long createdAt; public AccountRequestData(AccountRequestAttributes accountRequestInfo) { @@ -19,8 +19,12 @@ public AccountRequestData(AccountRequestAttributes accountRequestInfo) { this.email = accountRequestInfo.getEmail(); this.institute = accountRequestInfo.getInstitute(); this.registrationKey = accountRequestInfo.getRegistrationKey(); - this.registeredAt = accountRequestInfo.getRegisteredAt().toEpochMilli(); this.createdAt = accountRequestInfo.getCreatedAt().toEpochMilli(); + if (accountRequestInfo.getRegisteredAt() == null) { + this.registeredAt = null; + } else { + this.registeredAt = accountRequestInfo.getRegisteredAt().toEpochMilli(); + } } public String getInstitute() { @@ -39,7 +43,7 @@ public String getRegistrationKey() { return registrationKey; } - public long getRegisteredAt() { + public Long getRegisteredAt() { return registeredAt; } diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index f6df437263b..b3819d159b9 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -23,7 +23,7 @@ protected String getRequestMethod() { @Override @Test - protected void testExecute() { + protected void testExecute() throws Exception { AccountRequestAttributes accountRequest = logic.getAccountRequest("typical@gmail.tmt", "TEAMMATES Test Institute 1"); @@ -59,7 +59,7 @@ protected void testExecute() { assertEquals(response.getEmail(), accountRequest.getEmail()); assertEquals(response.getRegistrationKey(), accountRequest.getRegistrationKey()); assertEquals(response.getInstitute(), accountRequest.getInstitute()); - assertEquals(response.getRegisteredAt(), accountRequest.getRegisteredAt()); + assertNull(accountRequest.getRegisteredAt()); } @Override From 9e67ba64718f6b60c4d094e6b953bae06e98b7ac Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 11 Dec 2021 21:08:05 +0800 Subject: [PATCH 46/79] Change getAccountRequest to throw exception --- src/main/java/teammates/logic/api/Logic.java | 8 +++++--- .../logic/core/AccountRequestsLogic.java | 15 ++++++++++++--- .../ui/webapi/GetAccountRequestAction.java | 9 ++++++--- .../logic/core/AccountRequestsLogicTest.java | 5 ++--- .../test/BaseTestCaseWithLocalDatabaseAccess.java | 7 ++++++- .../ui/webapi/DeleteAccountRequestActionTest.java | 5 +++-- .../ui/webapi/GetAccountRequestActionTest.java | 3 ++- .../webapi/GetAccountRequestStatusActionTest.java | 2 +- 8 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 4c7ff87643b..0aa37489ba7 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -1427,7 +1427,7 @@ public void deleteAccountRequest(String email, String institute) { *

Preconditions:

* * All parameters are non-null. * - * @return the account request or null if no match found + * @return the account request * @throws EntityDoesNotExistException if the account request does not exist */ public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) @@ -1443,9 +1443,11 @@ public AccountRequestAttributes getAccountRequestForRegistrationKey(String regis *

Preconditions:

* * All parameters are non-null. * - * @return the account request or null if no match found + * @return the account request + * @throws EntityDoesNotExistException if the account request does not exist */ - public AccountRequestAttributes getAccountRequest(String email, String institute) { + public AccountRequestAttributes getAccountRequest(String email, String institute) + throws EntityDoesNotExistException { assert email != null; assert institute != null; diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index a97203d2b15..cf4a2e4aec5 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -49,10 +49,19 @@ public void deleteAccountRequest(String email, String institute) { /** * Gets an account request by email address and institute. * - * @return the account request or null if no match found + * @return the account request + * @throws EntityDoesNotExistException if account request does not exist */ - public AccountRequestAttributes getAccountRequest(String email, String institute) { - return accountRequestsDb.getAccountRequest(email, institute); + public AccountRequestAttributes getAccountRequest(String email, String institute) + throws EntityDoesNotExistException { + AccountRequestAttributes accountRequest = accountRequestsDb.getAccountRequest(email, institute); + + if (accountRequest == null) { + throw new EntityDoesNotExistException( + "Account request with email " + email + " and institute " + institute + " does not exist"); + } + + return accountRequest; } /** diff --git a/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java b/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java index fecf80f9c84..f77e0320424 100644 --- a/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java @@ -1,6 +1,7 @@ package teammates.ui.webapi; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityDoesNotExistException; import teammates.common.util.Const; import teammates.ui.output.AccountRequestData; @@ -14,9 +15,11 @@ public JsonResult execute() { String email = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_EMAIL); String institute = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_INSTITUTION); - AccountRequestAttributes accountRequestInfo = logic.getAccountRequest(email, institute); - if (accountRequestInfo == null) { - throw new EntityNotFoundException("Account request does not exist."); + AccountRequestAttributes accountRequestInfo; + try { + accountRequestInfo = logic.getAccountRequest(email, institute); + } catch (EntityDoesNotExistException ednee) { + throw new EntityNotFoundException(ednee); } AccountRequestData output = new AccountRequestData(accountRequestInfo); diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index aea092d8381..1daa447c2f1 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -138,9 +138,8 @@ public void testGetAccountRequest() throws Exception { ______TS("account request not found"); - AccountRequestAttributes notFoundRequestAttributes = - accountRequestsLogic.getAccountRequest("not-found@test.com", "not-found"); - assertNull(notFoundRequestAttributes); + assertThrows(EntityDoesNotExistException.class, + () -> accountRequestsLogic.getAccountRequest("not-found@test.com", "not-found")); ______TS("failure null parameter"); diff --git a/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java b/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java index e021b5ff15a..65d4afc9ba8 100644 --- a/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java +++ b/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java @@ -23,6 +23,7 @@ import teammates.common.datatransfer.attributes.InstructorAttributes; import teammates.common.datatransfer.attributes.StudentAttributes; import teammates.common.datatransfer.attributes.StudentProfileAttributes; +import teammates.common.exception.EntityDoesNotExistException; import teammates.logic.api.LogicExtension; import teammates.logic.core.LogicStarter; import teammates.storage.api.OfyHelper; @@ -135,7 +136,11 @@ protected StudentAttributes getStudent(StudentAttributes student) { @Override protected AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest) { - return logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); + try { + return logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); + } catch (EntityDoesNotExistException ednee) { + return null; + } } protected void removeAndRestoreTypicalDataBundle() { diff --git a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java index 46db65be439..21b52de11ca 100644 --- a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java @@ -3,6 +3,7 @@ import org.testng.annotations.Test; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityDoesNotExistException; import teammates.common.util.Const; import teammates.ui.output.MessageOutput; @@ -25,7 +26,6 @@ protected String getRequestMethod() { @Test protected void testExecute() { AccountRequestAttributes accountRequest = typicalBundle.accountRequests.get("accountRequest1"); - assertNotNull(logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); ______TS("Not enough parameters"); @@ -45,7 +45,8 @@ protected void testExecute() { assertEquals(msg.getMessage(), "Account request successfully deleted."); - assertNull(logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); + assertThrows(EntityDoesNotExistException.class, + () -> logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); ______TS("Typical case, delete non-existing account request"); diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index b3819d159b9..741d460c374 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -41,7 +41,8 @@ protected void testExecute() throws Exception { }; EntityNotFoundException enfe = verifyEntityNotFound(nonExistParams); - assertEquals("Account request does not exist.", enfe.getMessage()); + assertEquals("Account request with email non-existent@email and institute non existent institute does not exist", + enfe.getMessage()); ______TS("typical success case"); diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java index 454251b6f6b..16f23dc847c 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java @@ -22,7 +22,7 @@ protected String getRequestMethod() { @Override @Test - protected void testExecute() { + protected void testExecute() throws Exception { loginAsUnregistered("unreg.user"); From c8a22bb6c5757c8b4831563b2c0694d0e5ff3372 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 11 Dec 2021 21:15:26 +0800 Subject: [PATCH 47/79] Remove unnecessary null check --- .../datatransfer/attributes/AccountRequestAttributes.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index dfe9fb8bde8..abb11a7473e 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -109,9 +109,7 @@ public AccountRequest toEntity() { accountRequest.setCreatedAt(this.getCreatedAt()); } - if (this.getRegisteredAt() != null) { - accountRequest.setRegisteredAt(this.getRegisteredAt()); - } + accountRequest.setRegisteredAt(this.getRegisteredAt()); return accountRequest; } From 381da0d064c422009470fa3be1b470c970714cdd Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 11 Dec 2021 21:34:50 +0800 Subject: [PATCH 48/79] Refactor user join page --- src/web/app/user-join-page.component.ts | 71 +++++++++++-------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/src/web/app/user-join-page.component.ts b/src/web/app/user-join-page.component.ts index dc9ff5044c1..3abfa7d9f10 100644 --- a/src/web/app/user-join-page.component.ts +++ b/src/web/app/user-join-page.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Observable } from 'rxjs'; import { finalize } from 'rxjs/operators'; import { environment } from '../environments/environment'; import { AccountService } from '../services/account.service'; @@ -45,6 +46,11 @@ export class UserJoinPageComponent implements OnInit { this.key = queryParams.key; this.isCreatingAccount = queryParams.iscreatingaccount === 'true'; + // Create cccount request can only come from instructor. + if (this.isCreatingAccount) { + this.entityType = 'instructor'; + } + const nextUrl: string = `${window.location.pathname}${window.location.search.replace(/&/g, '%26')}`; this.authService.getAuthUser(undefined, nextUrl).subscribe((auth: AuthInfo) => { if (!auth.user) { @@ -58,47 +64,29 @@ export class UserJoinPageComponent implements OnInit { } this.userId = auth.user.id; - if (this.isCreatingAccount) { - this.accountService.getRegisteredStatus(this.key).subscribe((resp: JoinStatus) => { - this.hasJoined = resp.hasJoined; - if (this.hasJoined) { - // The regkey has been used; simply redirect the user to their home page, - // regardless of whether the regkey matches or not. - this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); - } else { - this.isLoading = false; - } - }, (resp: ErrorMessageOutput) => { - if (resp.status === 404) { - this.validUrl = false; - this.isLoading = false; - return; - } - const modalRef: any = this.ngbModal.open(ErrorReportComponent); - modalRef.componentInstance.requestId = resp.error.requestId; - modalRef.componentInstance.errorMessage = resp.error.message; - }); - } else { - this.courseService.getJoinCourseStatus(this.key, this.entityType).subscribe((resp: JoinStatus) => { - this.hasJoined = resp.hasJoined; - if (this.hasJoined) { - // The regkey has been used; simply redirect the user to their home page, - // regardless of whether the regkey matches or not. - this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); - } else { - this.isLoading = false; - } - }, (resp: ErrorMessageOutput) => { - if (resp.status === 404) { - this.validUrl = false; - this.isLoading = false; - return; - } - const modalRef: any = this.ngbModal.open(ErrorReportComponent); - modalRef.componentInstance.requestId = resp.error.requestId; - modalRef.componentInstance.errorMessage = resp.error.message; - }); - } + const request: Observable = this.isCreatingAccount + ? this.accountService.getRegisteredStatus(this.key) + : this.courseService.getJoinCourseStatus(this.key, this.entityType); + + request.subscribe((resp: JoinStatus) => { + this.hasJoined = resp.hasJoined; + if (this.hasJoined) { + // The regkey has been used; simply redirect the user to their home page, + // regardless of whether the regkey matches or not. + this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); + } else { + this.isLoading = false; + } + }, (resp: ErrorMessageOutput) => { + if (resp.status === 404) { + this.validUrl = false; + this.isLoading = false; + return; + } + const modalRef: any = this.ngbModal.open(ErrorReportComponent); + modalRef.componentInstance.requestId = resp.error.requestId; + modalRef.componentInstance.errorMessage = resp.error.message; + }); }); }); } @@ -118,6 +106,7 @@ export class UserJoinPageComponent implements OnInit { /** * Creates an account. + * Account is only created after instructor joins for the first time. */ createAccount(): void { this.isLoading = true; From 3c41600b87dd281f8c6c794458eed988fcb539f3 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 11 Dec 2021 21:42:03 +0800 Subject: [PATCH 49/79] Remove instructor from registration url --- .../common/datatransfer/attributes/AccountRequestAttributes.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index abb11a7473e..272ca632226 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -82,7 +82,6 @@ public String getRegistrationUrl() { return Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) .withIsCreatingAccount(String.valueOf(true)) .withRegistrationKey(this.getRegistrationKey()) - .withEntityType(Const.EntityType.INSTRUCTOR) .toAbsoluteString(); } From 2a49efec27810f1424d9d416fc7fe32aca8b46a7 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 11 Dec 2021 21:44:33 +0800 Subject: [PATCH 50/79] Remove instructor entity type from tests --- src/web/app/user-join-page.component.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/web/app/user-join-page.component.spec.ts b/src/web/app/user-join-page.component.spec.ts index 453daf78089..6b4588a0936 100644 --- a/src/web/app/user-join-page.component.spec.ts +++ b/src/web/app/user-join-page.component.spec.ts @@ -213,7 +213,6 @@ describe('UserJoinPageComponent creating account', () => { useValue: { queryParams: of({ iscreatingaccount: 'true', - entitytype: 'instructor', key: 'key', }), }, From 38b2941a8623182ecf7f53f656ccaf4231ba0177 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sat, 11 Dec 2021 22:58:04 +0800 Subject: [PATCH 51/79] Update sample data to be more realistic --- .../logic/core/AccountRequestsLogicTest.java | 6 +- .../DeleteAccountRequestActionTest.java | 2 +- .../webapi/GetAccountRequestActionTest.java | 2 +- .../GetAccountRequestStatusActionTest.java | 6 +- .../resources/data/typicalDataBundle.json | 107 ++++++++++++++++-- 5 files changed, 107 insertions(+), 16 deletions(-) diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 1daa447c2f1..e2065e4a117 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -80,7 +80,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { @Test public void testDeleteAccountRequest() throws Exception { - AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); + AccountRequestAttributes a = dataBundle.accountRequests.get("unregisteredInstructor1"); ______TS("silent deletion of non-existent account request"); @@ -107,7 +107,7 @@ public void testDeleteAccountRequest() throws Exception { @Test public void testGetAccountRequestForRegistrationKey() throws Exception { AccountRequestAttributes a = - accountRequestsLogic.getAccountRequest("typical@gmail.tmt", "TEAMMATES Test Institute 1"); + accountRequestsLogic.getAccountRequest("unregisteredinstructor1@gmail.tmt", "TEAMMATES Test Institute 1"); ______TS("typical success case"); @@ -128,7 +128,7 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { @Test public void testGetAccountRequest() throws Exception { - AccountRequestAttributes a = dataBundle.accountRequests.get("accountRequest1"); + AccountRequestAttributes a = dataBundle.accountRequests.get("unregisteredInstructor1"); ______TS("typical success case"); diff --git a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java index 21b52de11ca..7e7205bf704 100644 --- a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java @@ -25,7 +25,7 @@ protected String getRequestMethod() { @Override @Test protected void testExecute() { - AccountRequestAttributes accountRequest = typicalBundle.accountRequests.get("accountRequest1"); + AccountRequestAttributes accountRequest = typicalBundle.accountRequests.get("unregisteredInstructor1"); ______TS("Not enough parameters"); diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index 741d460c374..47c736e65eb 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -25,7 +25,7 @@ protected String getRequestMethod() { @Test protected void testExecute() throws Exception { AccountRequestAttributes accountRequest = - logic.getAccountRequest("typical@gmail.tmt", "TEAMMATES Test Institute 1"); + logic.getAccountRequest("unregisteredinstructor1@gmail.tmt", "TEAMMATES Test Institute 1"); loginAsAdmin(); diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java index 16f23dc847c..338a74193ac 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java @@ -32,8 +32,8 @@ protected void testExecute() throws Exception { ______TS("Normal case: account request not used"); - String registrationKey = - logic.getAccountRequest("typical@gmail.tmt", "TEAMMATES Test Institute 1").getRegistrationKey(); + String registrationKey = logic.getAccountRequest("unregisteredinstructor1@gmail.tmt", + "TEAMMATES Test Institute 1").getRegistrationKey(); String[] params = new String[] { Const.ParamsNames.REGKEY, registrationKey, }; @@ -46,7 +46,7 @@ protected void testExecute() throws Exception { ______TS("Normal case: account request already used"); String usedRegistrationKey = - logic.getAccountRequest("typical2@gmail.tmt", "TEAMMATES Test Institute 2").getRegistrationKey(); + logic.getAccountRequest("instr1@course1.tmt", "TEAMMATES Test Institute 1").getRegistrationKey(); params = new String[] { Const.ParamsNames.REGKEY, usedRegistrationKey, }; diff --git a/src/test/resources/data/typicalDataBundle.json b/src/test/resources/data/typicalDataBundle.json index 2c19d72fdf3..b91c7da594e 100644 --- a/src/test/resources/data/typicalDataBundle.json +++ b/src/test/resources/data/typicalDataBundle.json @@ -1717,18 +1717,109 @@ } }, "accountRequests": { - "accountRequest1": { - "email": "typical@gmail.tmt", + "instructor1OfCourse1": { + "name": "Instructor 1 of Course 1", + "email": "instr1@course1.tmt", "institute": "TEAMMATES Test Institute 1", - "name": "Typical Instructor Name", - "createdAt": "2011-01-01T00:00:00Z" + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" }, - "accountRequest2": { - "email": "typical2@gmail.tmt", - "institute": "TEAMMATES Test Institute 2", - "name": "Typical Instructor Name", + "instructor2OfCourse1": { + "name": "Instructor 2 of Course 1", + "email": "instr2@course1.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "helperOfCourse1": { + "name": "Helper of Course 1", + "email": "helper@course1.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructor1OfCourse2": { + "name": "Instructor 1 of Course 2", + "email": "instr1@course2.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructor2OfCourse2": { + "name": "Instructor 2 of Course 2", + "email": "instr2@course2.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructor1OfCourse3": { + "name": "Instructor 1 of Course 3", + "email": "instr1@course3.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructor2OfCourse3": { + "name": "Instructor 2 of Course 3", + "email": "instr2@course3.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructor3": { + "name": "Instructor 3 of Course 1 and 2", + "email": "instr3@course1n2.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructor4": { + "name": "Instructor 4 of CourseNoEvals", + "email": "instr4@coursenoevals.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructor5": { + "name": "Instructor 5 of CourseNoRegister", + "email": "instructor5@courseNoRegister.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructorWithoutCourses": { + "name": "Instructor Without Courses", + "email": "iwc@yahoo.tmt", + "institute": "TEAMMATES Test Institute 7", "createdAt": "2011-01-01T00:00:00Z", "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructorWithOnlyOneSampleCourse": { + "name": "Instructor With Only One Sample Course", + "email": "iwosc@yahoo.tmt", + "institute": "TEAMMATES Test Institute 7", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructorOfArchivedCourse": { + "name": "InstructorOfArchiveCourse name", + "email": "instructorOfArchiveCourse@archiveCourse.tmt", + "institute": "TEAMMATES Test Institute 5", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "instructor1OfTestingSanitizationCourse": { + "name": "Instructor", + "email": "instructor1@sanitization.tmt", + "institute": "inst", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "unregisteredInstructor1": { + "email": "unregisteredinstructor1@gmail.tmt", + "institute": "TEAMMATES Test Institute 1", + "name": "Typical Instructor Name", + "createdAt": "2011-01-01T00:00:00Z" } } } From f49d7cc2f8f137e53591c4d2fd7021a64fa17957 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sun, 12 Dec 2021 00:22:12 +0800 Subject: [PATCH 52/79] Remove name and registration key update options --- .../attributes/AccountRequestAttributes.java | 51 ++++++----------- .../ui/webapi/CreateAccountAction.java | 2 +- .../ui/webapi/CreateAccountRequestAction.java | 3 +- .../AccountRequestAttributesTest.java | 43 ++++++--------- .../logic/core/AccountRequestsLogicTest.java | 9 +-- .../storage/api/AccountRequestsDbTest.java | 55 ++++++++----------- .../java/teammates/test/AbstractBackDoor.java | 13 +++-- .../ui/webapi/CreateAccountActionTest.java | 12 +--- 8 files changed, 74 insertions(+), 114 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 272ca632226..f78d4cb6210 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -23,10 +23,10 @@ public class AccountRequestAttributes extends EntityAttributes { private Instant createdAt; private transient String registrationKey; - private AccountRequestAttributes(String email, String institute) { + private AccountRequestAttributes(String email, String institute, String name) { this.email = email; this.institute = institute; - this.name = null; + this.name = name; this.registrationKey = null; this.registeredAt = null; this.createdAt = null; @@ -37,10 +37,9 @@ private AccountRequestAttributes(String email, String institute) { */ public static AccountRequestAttributes valueOf(AccountRequest accountRequest) { AccountRequestAttributes accountRequestAttributes = new AccountRequestAttributes(accountRequest.getEmail(), - accountRequest.getInstitute()); + accountRequest.getInstitute(), accountRequest.getName()); accountRequestAttributes.registrationKey = accountRequest.getRegistrationKey(); - accountRequestAttributes.name = accountRequest.getName(); accountRequestAttributes.registeredAt = accountRequest.getRegisteredAt(); accountRequestAttributes.createdAt = accountRequest.getCreatedAt(); @@ -50,8 +49,8 @@ public static AccountRequestAttributes valueOf(AccountRequest accountRequest) { /** * Returns a builder for {@link AccountRequestAttributes}. */ - public static Builder builder(String email, String institute) { - return new Builder(email, institute); + public static Builder builder(String email, String institute, String name) { + return new Builder(email, institute, name); } public String getRegistrationKey() { @@ -151,16 +150,14 @@ public void sanitizeForSaving() { * Updates with {@link UpdateOptions}. */ public void update(UpdateOptions updateOptions) { - updateOptions.registrationKeyOption.ifPresent(s -> registrationKey = s); - updateOptions.nameOption.ifPresent(s -> name = s); updateOptions.registeredAtOption.ifPresent(s -> registeredAt = s); } /** * Returns a {@link UpdateOptions.Builder} to build {@link UpdateOptions} for an account request. */ - public static UpdateOptions.Builder updateOptionsBuilder(String email, String institute) { - return new UpdateOptions.Builder(email, institute); + public static UpdateOptions.Builder updateOptionsBuilder(String email, String institute, String name) { + return new UpdateOptions.Builder(email, institute, name); } /** @@ -169,11 +166,11 @@ public static UpdateOptions.Builder updateOptionsBuilder(String email, String in public static class Builder extends BasicBuilder { private final AccountRequestAttributes accountRequestAttributes; - private Builder(String email, String institute) { - super(new UpdateOptions(email, institute)); + private Builder(String email, String institute, String name) { + super(new UpdateOptions(email, institute, name)); thisBuilder = this; - accountRequestAttributes = new AccountRequestAttributes(email, institute); + accountRequestAttributes = new AccountRequestAttributes(email, institute, name); } @Override @@ -190,17 +187,18 @@ public AccountRequestAttributes build() { public static class UpdateOptions { private String email; private String institute; + private String name; - private UpdateOption nameOption = UpdateOption.empty(); - private UpdateOption registrationKeyOption = UpdateOption.empty(); private UpdateOption registeredAtOption = UpdateOption.empty(); - private UpdateOptions(String email, String institute) { + private UpdateOptions(String email, String institute, String name) { assert email != null; assert institute != null; + assert name != null; this.email = email; this.institute = institute; + this.name = name; } @Override @@ -208,8 +206,7 @@ public String toString() { return "AccountRequestAttributes.UpdateOptions [" + ", email = " + email + ", institute = " + institute - + ", name = " + nameOption - + ", registrationKey = " + registrationKeyOption + + ", name = " + name + ", registeredAt = " + registeredAtOption + "]"; } @@ -218,8 +215,8 @@ public String toString() { * Builder class to build {@link UpdateOptions}. */ public static class Builder extends BasicBuilder { - private Builder(String email, String institute) { - super(new UpdateOptions(email, institute)); + private Builder(String email, String institute, String name) { + super(new UpdateOptions(email, institute, name)); thisBuilder = this; } @@ -247,20 +244,6 @@ private abstract static class BasicBuilder> { this.updateOptions = updateOptions; } - public B withName(String name) { - assert name != null; - - updateOptions.nameOption = UpdateOption.of(name); - return thisBuilder; - } - - public B withRegistrationKey(String registationKey) { - assert registationKey != null; - - updateOptions.registrationKeyOption = UpdateOption.of(registationKey); - return thisBuilder; - } - public B withRegisteredAt(Instant registeredAt) { assert registeredAt != null; diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index feb70c0d1eb..7abe5b58677 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -72,7 +72,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera try { logic.joinCourseForInstructor(instructorList.get(0).getKey(), userInfo.id); accountRequestAttributes.update(AccountRequestAttributes - .updateOptionsBuilder(instructorEmail, instructorInstitution) + .updateOptionsBuilder(instructorEmail, instructorInstitution, instructorName) .withRegisteredAt(Instant.now()) .build()); logic.createOrUpdateAccountRequest(accountRequestAttributes); diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index 746aea7ae12..d3b7712340b 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -21,8 +21,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera String instructorInstitution = createRequest.getInstructorInstitution().trim(); AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes - .builder(instructorEmail, instructorInstitution) - .withName(instructorName) + .builder(instructorEmail, instructorInstitution, instructorName) .build(); try { diff --git a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java index 0a95a69c435..45e35224e74 100644 --- a/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java +++ b/src/test/java/teammates/common/datatransfer/attributes/AccountRequestAttributesTest.java @@ -40,31 +40,29 @@ public void testValueOf_registeredWithTypicalData_shouldGenerateAttributesCorrec @Test public void testBuilder_withTypicalData_shouldBuildCorrectAttributes() { - String validName = "validName"; String validEmail = "valid@test.com"; String validInstitute = "validInstitute"; - String validRegKey = "validRegKey123"; + String validName = "valid name"; AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes - .builder(validEmail, validInstitute) - .withName(validName) - .withRegistrationKey(validRegKey) + .builder(validEmail, validInstitute, validName) + .withRegisteredAt(Const.TIME_REPRESENTS_NOW) .build(); + assertEquals(Const.TIME_REPRESENTS_NOW, accountRequestAttributes.getRegisteredAt()); assertEquals(validEmail, accountRequestAttributes.getEmail()); - assertEquals(validName, accountRequestAttributes.getName()); - assertEquals(validRegKey, accountRequestAttributes.getRegistrationKey()); assertEquals(validInstitute, accountRequestAttributes.getInstitute()); + assertEquals(validName, accountRequestAttributes.getName()); } @Test public void testBuilder_buildNothing_shouldUseDefaultValues() { AccountRequestAttributes accountRequestAttributes = - AccountRequestAttributes.builder("valid@test.com", "valid institute").build(); + AccountRequestAttributes.builder("valid@test.com", "valid institute", "valid name").build(); assertEquals("valid@test.com", accountRequestAttributes.getEmail()); assertEquals("valid institute", accountRequestAttributes.getInstitute()); - assertNull(accountRequestAttributes.getName()); + assertEquals("valid name", accountRequestAttributes.getName()); assertNull(accountRequestAttributes.getRegistrationKey()); assertNull(accountRequestAttributes.getRegisteredAt()); } @@ -73,21 +71,14 @@ public void testBuilder_buildNothing_shouldUseDefaultValues() { public void testBuilder_withNullArguments_shouldThrowException() { assertThrows(AssertionError.class, () -> { AccountRequestAttributes - .builder(null, null) + .builder(null, null, null) .build(); }); assertThrows(AssertionError.class, () -> { AccountRequestAttributes - .builder("valid@test.com", "valid institute") - .withName(null) - .build(); - }); - - assertThrows(AssertionError.class, () -> { - AccountRequestAttributes - .builder("valid@test.com", "valid institute") - .withRegistrationKey(null) + .builder("valid@test.com", "valid institute", "valid name") + .withRegisteredAt(null) .build(); }); @@ -102,8 +93,7 @@ public void testValidate() throws Exception { String invalidEmail = "invalid-email"; String emptyName = ""; AccountRequestAttributes invalidAccountRequest = AccountRequestAttributes - .builder(invalidEmail, "institute") - .withName(emptyName) + .builder(invalidEmail, "institute", emptyName) .build(); assertFalse("invalid value", invalidAccountRequest.isValid()); @@ -146,7 +136,7 @@ public void testEquals() { // When the two account requests are different AccountRequestAttributes differentAccountRequest = - AccountRequestAttributes.builder("test@test.com", "test-institute").withName("Another Name").build(); + AccountRequestAttributes.builder("test@test.com", "test-institute", "Another Name").build(); assertFalse(accountRequest.equals(differentAccountRequest)); @@ -165,16 +155,15 @@ public void testHashCode() { // When the two account requests are different, they should have different hash code AccountRequestAttributes accountRequestDifferent = - AccountRequestAttributes.builder("test@test.com", "test-institute").withName("Another Name").build(); + AccountRequestAttributes.builder("test@test.com", "test-institute", "Another Name").build(); assertFalse(accountRequest.hashCode() == accountRequestDifferent.hashCode()); } private static AccountRequestAttributes getValidAccountRequestAttributesObject() { - return AccountRequestAttributes.builder("valid@test.com", "valid-institute") - .withName("valid-name") - .withRegistrationKey("valid123") - .build(); + AccountRequest accountRequest = new AccountRequest("valid@test.com", "valid-name", "valid-institute"); + accountRequest.setRegistrationKey("valid123"); + return AccountRequestAttributes.valueOf(accountRequest); } } diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index e2065e4a117..b6464101def 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -33,8 +33,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ______TS("typical success case"); AccountRequestAttributes accountRequest = AccountRequestAttributes - .builder("valid@test.com", "TEAMMATES Test Institute 1") - .withName("Test account Name") + .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") .build(); AccountRequestAttributes createdAccountRequest = @@ -50,8 +49,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ______TS("duplicate account request, account request updated"); AccountRequestAttributes duplicateAccount = AccountRequestAttributes - .builder("valid@test.com", "TEAMMATES Test Institute 2") - .withName("Test account Name 2") + .builder("valid@test.com", "TEAMMATES Test Institute 2", "Test account Name 2") .build(); duplicateAccount = accountRequestsLogic.createOrUpdateAccountRequest(duplicateAccount); @@ -60,8 +58,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ______TS("failure case: invalid parameter"); AccountRequestAttributes invalidAccountRequest = AccountRequestAttributes - .builder("invalid email", "TEAMMATES Test Institute 1") - .withName("Test account Name") + .builder("invalid email", "TEAMMATES Test Institute 1", "Test account Name") .build(); InvalidParametersException ipe = assertThrows(InvalidParametersException.class, diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 52a35c02c2c..8c63e76300d 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -5,6 +5,7 @@ import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.exception.InvalidParametersException; import teammates.common.util.FieldValidator; +import teammates.storage.entity.AccountRequest; import teammates.test.AssertHelper; import teammates.test.BaseTestCaseWithLocalDatabaseAccess; @@ -21,8 +22,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ______TS("typical success case"); AccountRequestAttributes accountRequest = AccountRequestAttributes - .builder("valid@test.com", "TEAMMATES Test Institute 1") - .withName("Test account Name") + .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") .build(); accountRequest = accountRequestsDb.createOrUpdateAccountRequest(accountRequest); @@ -31,8 +31,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ______TS("duplicate account request, account request updated"); AccountRequestAttributes duplicateAccount = AccountRequestAttributes - .builder("valid@test.com", "TEAMMATES Test Institute 1") - .withName("Test account Name 2") + .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name 2") .build(); duplicateAccount = accountRequestsDb.createOrUpdateAccountRequest(duplicateAccount); @@ -41,8 +40,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ______TS("failure case: invalid parameter"); AccountRequestAttributes invalidAccountRequest = AccountRequestAttributes - .builder("invalid email", "TEAMMATES Test Institute 1") - .withName("Test account Name") + .builder("invalid email", "TEAMMATES Test Institute 1", "Test account Name") .build(); InvalidParametersException ipe = assertThrows(InvalidParametersException.class, @@ -61,14 +59,11 @@ public void testCreateOrUpdateAccountRequest() throws Exception { @Test public void testDeleteAccountRequest() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes - .builder("valid2@test.com", "TEAMMATES Test Institute 1") - .withName("Test account Name") - .withRegistrationKey("2-123456") - .build(); + AccountRequest accountRequest = new AccountRequest("valid2@test.com", + "Test account Name", "TEAMMATES Test Institute 1"); + accountRequest.setRegistrationKey("2-123456"); - accountRequestsDb.saveEntity(a.toEntity()); - a = accountRequestsDb.getAccountRequest("valid2@test.com", "TEAMMATES Test Institute 1"); + accountRequestsDb.saveEntity(accountRequest); ______TS("silent deletion of non-existent account request"); @@ -76,13 +71,13 @@ public void testDeleteAccountRequest() throws Exception { ______TS("typical success case"); - verifyPresentInDatabase(a); - accountRequestsDb.deleteAccountRequest(a.getEmail(), a.getInstitute()); - verifyAbsentInDatabase(a); + verifyPresentInDatabase(AccountRequestAttributes.valueOf(accountRequest)); + accountRequestsDb.deleteAccountRequest("valid2@test.com", "TEAMMATES Test Institute 1"); + verifyAbsentInDatabase(AccountRequestAttributes.valueOf(accountRequest)); ______TS("silent deletion of same account request"); - accountRequestsDb.deleteAccountRequest(a.getEmail(), a.getInstitute()); + accountRequestsDb.deleteAccountRequest("valid2@test.com", "TEAMMATES Test Institute 1"); ______TS("failure null parameter"); @@ -92,18 +87,17 @@ public void testDeleteAccountRequest() throws Exception { @Test public void testGetAccountRequestForRegistrationKey() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes.builder("valid3@test.com", "TEAMMATES Test Institute 1") - .withName("Test account Name") - .withRegistrationKey("3-123456") - .build(); + AccountRequest accountRequest = new AccountRequest("valid3@test.com", + "Test account Name", "TEAMMATES Test Institute 1"); + accountRequest.setRegistrationKey("3-123456"); - accountRequestsDb.saveEntity(a.toEntity()); + accountRequestsDb.saveEntity(accountRequest); ______TS("typical success case"); AccountRequestAttributes accountRequestAttributes = - accountRequestsDb.getAccountRequestForRegistrationKey(a.getRegistrationKey()); - assertEquals(a, accountRequestAttributes); + accountRequestsDb.getAccountRequestForRegistrationKey("3-123456"); + assertEquals(AccountRequestAttributes.valueOf(accountRequest), accountRequestAttributes); ______TS("account request not found"); @@ -119,18 +113,17 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { @Test public void testGetAccountRequest() throws Exception { - AccountRequestAttributes a = AccountRequestAttributes.builder("valid4@test.com", "TEAMMATES Test Institute 1") - .withName("Test account Name") - .withRegistrationKey("4-123456") - .build(); + AccountRequest accountRequest = new AccountRequest("valid4@test.com", + "Test account Name", "TEAMMATES Test Institute 1"); + accountRequest.setRegistrationKey("4-123456"); - accountRequestsDb.saveEntity(a.toEntity()); + accountRequestsDb.saveEntity(accountRequest); ______TS("typical success case"); AccountRequestAttributes accountRequestAttributes = - accountRequestsDb.getAccountRequest(a.getEmail(), a.getInstitute()); - assertEquals(a, accountRequestAttributes); + accountRequestsDb.getAccountRequest("valid4@test.com", "TEAMMATES Test Institute 1"); + assertEquals(AccountRequestAttributes.valueOf(accountRequest), accountRequestAttributes); ______TS("account request not found"); diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 2c26d812b42..8cde017ba29 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -44,6 +44,7 @@ import teammates.common.exception.HttpRequestFailedException; import teammates.common.util.Const; import teammates.common.util.JsonUtils; +import teammates.storage.entity.AccountRequest; import teammates.ui.output.AccountData; import teammates.ui.output.AccountRequestData; import teammates.ui.output.CourseData; @@ -742,10 +743,14 @@ public AccountRequestAttributes getAccountRequest(String email, String institute } AccountRequestData accountRequestData = JsonUtils.fromJson(response.responseBody, AccountRequestData.class); - return AccountRequestAttributes.builder(accountRequestData.getEmail(), accountRequestData.getInstitute()) - .withName(accountRequestData.getName()) - .withRegistrationKey(accountRequestData.getRegistrationKey()) - .build(); + AccountRequest accountRequest = new AccountRequest(accountRequestData.getEmail(), + accountRequestData.getName(), accountRequestData.getInstitute()); + accountRequest.setCreatedAt(Instant.ofEpochMilli(accountRequestData.getCreatedAt())); + accountRequest.setRegistrationKey(accountRequestData.getRegistrationKey()); + if (accountRequestData.getRegisteredAt() != null) { + accountRequest.setRegisteredAt(Instant.ofEpochMilli(accountRequestData.getRegisteredAt())); + } + return AccountRequestAttributes.valueOf(accountRequest); } /** diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index 6ce3498976b..1c751e446d4 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -30,17 +30,10 @@ protected String getRequestMethod() { @Override @Test protected void testExecute() throws Exception { - String name = "JamesBond"; - String email = "jamesbond89@gmail.tmt"; + String name = "Typical Instructor Name"; + String email = "unregisteredinstructor1@gmail.tmt"; String institute = "TEAMMATES Test Institute 1"; - AccountRequestAttributes accountRequest = AccountRequestAttributes - .builder(email, institute) - .withName(name) - .build(); - - accountRequest = logic.createOrUpdateAccountRequest(accountRequest); - ______TS("Not enough parameters"); verifyHttpParameterFailure(); @@ -54,6 +47,7 @@ protected void testExecute() throws Exception { verifyNoTasksAdded(); ______TS("Normal case"); + AccountRequestAttributes accountRequest = logic.getAccountRequest(email, institute); String[] params = new String[] { Const.ParamsNames.REGKEY, accountRequest.getRegistrationKey(), }; CreateAccountAction a = getAction(params); From 926c3dd082a94863f17fa446863cfcdedaf74cae Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Sun, 12 Dec 2021 00:29:41 +0800 Subject: [PATCH 53/79] Fix architecture test violations --- src/test/java/teammates/test/AbstractBackDoor.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 8cde017ba29..278b904c6f4 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -44,7 +44,6 @@ import teammates.common.exception.HttpRequestFailedException; import teammates.common.util.Const; import teammates.common.util.JsonUtils; -import teammates.storage.entity.AccountRequest; import teammates.ui.output.AccountData; import teammates.ui.output.AccountRequestData; import teammates.ui.output.CourseData; @@ -743,14 +742,10 @@ public AccountRequestAttributes getAccountRequest(String email, String institute } AccountRequestData accountRequestData = JsonUtils.fromJson(response.responseBody, AccountRequestData.class); - AccountRequest accountRequest = new AccountRequest(accountRequestData.getEmail(), - accountRequestData.getName(), accountRequestData.getInstitute()); - accountRequest.setCreatedAt(Instant.ofEpochMilli(accountRequestData.getCreatedAt())); - accountRequest.setRegistrationKey(accountRequestData.getRegistrationKey()); - if (accountRequestData.getRegisteredAt() != null) { - accountRequest.setRegisteredAt(Instant.ofEpochMilli(accountRequestData.getRegisteredAt())); - } - return AccountRequestAttributes.valueOf(accountRequest); + + return AccountRequestAttributes + .builder(accountRequestData.getEmail(), accountRequestData.getInstitute(), accountRequestData.getName()) + .build(); } /** From c0f4cb8db8f734210a4d795fa28ad7110db41a89 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 18 Jan 2022 23:33:12 +0800 Subject: [PATCH 54/79] Seperate craete and update account request --- .../attributes/AccountRequestAttributes.java | 10 +++- src/main/java/teammates/logic/api/Logic.java | 28 ++++++++-- .../logic/core/AccountRequestsLogic.java | 22 ++++++-- .../storage/api/AccountRequestsDb.java | 30 +++++++---- .../ui/webapi/CreateAccountAction.java | 3 +- .../ui/webapi/CreateAccountRequestAction.java | 24 ++++++++- .../logic/core/AccountRequestsLogicTest.java | 53 ++++++++++++++---- .../storage/api/AccountRequestsDbTest.java | 54 +++++++++++++++---- 8 files changed, 177 insertions(+), 47 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index f78d4cb6210..dd3ac0375c8 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -182,7 +182,7 @@ public AccountRequestAttributes build() { } /** - * Helper class to specific the fields to update in {@link AccountRequestAttributes}. + * Helper class to specify the fields to update in {@link AccountRequestAttributes}. */ public static class UpdateOptions { private String email; @@ -201,6 +201,14 @@ private UpdateOptions(String email, String institute, String name) { this.name = name; } + public String getEmail() { + return email; + } + + public String getInstitute() { + return institute; + } + @Override public String toString() { return "AccountRequestAttributes.UpdateOptions [" diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 0aa37489ba7..e0ed3132e75 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -1394,19 +1394,37 @@ public boolean isStudentsInSameTeam(String courseId, String student1Email, Strin } /** - * Creates or updates an account request. + * Creates an account request. * *

Preconditions:

* * All parameters are non-null. * * @return the created account request * @throws InvalidParametersException if the account request is not valid + * @throws EntityAlreadyExistsException if the account request already exists */ - public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) - throws InvalidParametersException { - assert accountRequestToAdd != null; + public AccountRequestAttributes createAccountRequest(AccountRequestAttributes accountRequest) + throws InvalidParametersException, EntityAlreadyExistsException { + assert accountRequest != null; + + return accountRequestsLogic.createAccountRequest(accountRequest); + } + + /** + * Updates an account request. + * + *

Preconditions:

+ * * All parameters are non-null. + * + * @return the updated account request + * @throws InvalidParametersException if the account request is not valid + * @throws EntityDoesNotExistException if the account request does not exist + */ + public AccountRequestAttributes updateAccountRequest(AccountRequestAttributes.UpdateOptions updateOptions) + throws InvalidParametersException, EntityDoesNotExistException { + assert updateOptions != null; - return accountRequestsLogic.createOrUpdateAccountRequest(accountRequestToAdd); + return accountRequestsLogic.updateAccountRequest(updateOptions); } /** diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index cf4a2e4aec5..00e69e7b0bd 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -1,6 +1,7 @@ package teammates.logic.core; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityAlreadyExistsException; import teammates.common.exception.EntityDoesNotExistException; import teammates.common.exception.InvalidParametersException; import teammates.storage.api.AccountRequestsDb; @@ -27,14 +28,27 @@ void initLogicDependencies() { } /** - * Creates or updates an account request. + * Updates an account request. + * + * @return the updated account request + * @throws InvalidParametersException if the account request is not valid + * @throws EntityDoesNotExistException if the account request to create does not exist + */ + public AccountRequestAttributes updateAccountRequest(AccountRequestAttributes.UpdateOptions updateOptions) + throws InvalidParametersException, EntityDoesNotExistException { + return accountRequestsDb.updateAccountRequest(updateOptions); + } + + /** + * Creates an account request. * * @return the created account request * @throws InvalidParametersException if the account request is not valid + * @throws EntityAlreadyExistsException if the account request to create already exists */ - public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) - throws InvalidParametersException { - return accountRequestsDb.createOrUpdateAccountRequest(accountRequestToAdd); + public AccountRequestAttributes createAccountRequest(AccountRequestAttributes accountRequest) + throws InvalidParametersException, EntityAlreadyExistsException { + return accountRequestsDb.createEntity(accountRequest); } /** diff --git a/src/main/java/teammates/storage/api/AccountRequestsDb.java b/src/main/java/teammates/storage/api/AccountRequestsDb.java index 849da9f546e..2158883b18f 100644 --- a/src/main/java/teammates/storage/api/AccountRequestsDb.java +++ b/src/main/java/teammates/storage/api/AccountRequestsDb.java @@ -9,6 +9,7 @@ import com.googlecode.objectify.cmd.LoadType; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityDoesNotExistException; import teammates.common.exception.InvalidParametersException; import teammates.storage.entity.AccountRequest; @@ -41,24 +42,31 @@ public AccountRequestAttributes getAccountRequest(String email, String institute } /** - * Creates or updates an account request. + * Updates an account request. * - * @return the created account request + * @return the updated account request * @throws InvalidParametersException if the account request is not valid + * @throws EntityDoesNotExistException if the account request cannot be found */ - public AccountRequestAttributes createOrUpdateAccountRequest(AccountRequestAttributes accountRequestToAdd) - throws InvalidParametersException { - assert accountRequestToAdd != null; - accountRequestToAdd.sanitizeForSaving(); + public AccountRequestAttributes updateAccountRequest(AccountRequestAttributes.UpdateOptions updateOptions) + throws InvalidParametersException, EntityDoesNotExistException { + assert updateOptions != null; - if (!accountRequestToAdd.isValid()) { - throw new InvalidParametersException(accountRequestToAdd.getInvalidityInfo()); + AccountRequestAttributes accountRequest = getAccountRequest(updateOptions.getEmail(), updateOptions.getInstitute()); + if (accountRequest == null) { + throw new EntityDoesNotExistException(ERROR_UPDATE_NON_EXISTENT + updateOptions); } - AccountRequest accountRequest = accountRequestToAdd.toEntity(); - saveEntity(accountRequest); + accountRequest.update(updateOptions); + accountRequest.sanitizeForSaving(); - return makeAttributes(accountRequest); + if (!accountRequest.isValid()) { + throw new InvalidParametersException(accountRequest.getInvalidityInfo()); + } + + saveEntity(accountRequest.toEntity()); + + return accountRequest; } /** diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 7abe5b58677..55d61c44e53 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -71,11 +71,10 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera try { logic.joinCourseForInstructor(instructorList.get(0).getKey(), userInfo.id); - accountRequestAttributes.update(AccountRequestAttributes + logic.updateAccountRequest(AccountRequestAttributes .updateOptionsBuilder(instructorEmail, instructorInstitution, instructorName) .withRegisteredAt(Instant.now()) .build()); - logic.createOrUpdateAccountRequest(accountRequestAttributes); } catch (EntityDoesNotExistException ednee) { throw new EntityNotFoundException(ednee); } catch (EntityAlreadyExistsException eaee) { diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index d3b7712340b..c2949e71a09 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -1,8 +1,13 @@ package teammates.ui.webapi; +import org.apache.http.HttpStatus; + import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityAlreadyExistsException; +import teammates.common.exception.EntityDoesNotExistException; import teammates.common.exception.InvalidParametersException; import teammates.common.util.EmailWrapper; +import teammates.common.util.Logger; import teammates.ui.output.JoinLinkData; import teammates.ui.request.AccountCreateRequest; import teammates.ui.request.InvalidHttpRequestBodyException; @@ -12,6 +17,8 @@ */ class CreateAccountRequestAction extends AdminOnlyAction { + private static final Logger log = Logger.getLogger(); + @Override public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOperationException { AccountCreateRequest createRequest = getAndValidateRequestBody(AccountCreateRequest.class); @@ -20,14 +27,27 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera String instructorEmail = createRequest.getInstructorEmail().trim(); String instructorInstitution = createRequest.getInstructorInstitution().trim(); - AccountRequestAttributes accountRequestAttributes = AccountRequestAttributes + AccountRequestAttributes accountRequestToCreate = AccountRequestAttributes .builder(instructorEmail, instructorInstitution, instructorName) .build(); + AccountRequestAttributes accountRequestAttributes = null; try { - accountRequestAttributes = logic.createOrUpdateAccountRequest(accountRequestAttributes); + accountRequestAttributes = logic.createAccountRequest(accountRequestToCreate); } catch (InvalidParametersException ipe) { throw new InvalidHttpRequestBodyException(ipe); + } catch (EntityAlreadyExistsException eaee) { + // Continue without creating a new account request + } + + if (accountRequestAttributes == null) { + try { + logic.getAccountRequest(instructorEmail, instructorInstitution); + } catch (EntityDoesNotExistException ednee) { + // Entity should exist + log.severe("Unexpected error", ednee); + return new JsonResult("Unexpected Error", HttpStatus.SC_INTERNAL_SERVER_ERROR); + } } String joinLink = accountRequestAttributes.getRegistrationUrl(); diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index b6464101def..c979b02f7fa 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -4,8 +4,10 @@ import org.testng.annotations.Test; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityAlreadyExistsException; import teammates.common.exception.EntityDoesNotExistException; import teammates.common.exception.InvalidParametersException; +import teammates.common.util.Const; import teammates.common.util.FieldValidator; import teammates.test.AssertHelper; @@ -28,8 +30,7 @@ public void refreshTestData() { } @Test - public void testCreateOrUpdateAccountRequest() throws Exception { - + public void testCreateAccountRequest() throws Exception { ______TS("typical success case"); AccountRequestAttributes accountRequest = AccountRequestAttributes @@ -37,7 +38,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { .build(); AccountRequestAttributes createdAccountRequest = - accountRequestsLogic.createOrUpdateAccountRequest(accountRequest); + accountRequestsLogic.createAccountRequest(accountRequest); verifyPresentInDatabase(createdAccountRequest); assertEquals(accountRequest.getEmail(), createdAccountRequest.getEmail()); @@ -46,14 +47,17 @@ public void testCreateOrUpdateAccountRequest() throws Exception { assertNotNull(createdAccountRequest.getRegistrationKey()); assertNotNull(createdAccountRequest.getCreatedAt()); - ______TS("duplicate account request, account request updated"); + ______TS("failure: duplicate account request"); - AccountRequestAttributes duplicateAccount = AccountRequestAttributes - .builder("valid@test.com", "TEAMMATES Test Institute 2", "Test account Name 2") + AccountRequestAttributes duplicateAccountRequest = AccountRequestAttributes + .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") .build(); - duplicateAccount = accountRequestsLogic.createOrUpdateAccountRequest(duplicateAccount); - verifyPresentInDatabase(duplicateAccount); + assertThrows(EntityAlreadyExistsException.class, () -> { + accountRequestsLogic.createAccountRequest(duplicateAccountRequest); + }); + + accountRequestsLogic.deleteAccountRequest("valid@test.com", "TEAMMATES Test Institute 1"); ______TS("failure case: invalid parameter"); @@ -62,7 +66,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { .build(); InvalidParametersException ipe = assertThrows(InvalidParametersException.class, - () -> accountRequestsLogic.createOrUpdateAccountRequest(invalidAccountRequest)); + () -> accountRequestsLogic.createAccountRequest(invalidAccountRequest)); AssertHelper.assertContains( getPopulatedErrorMessage( FieldValidator.EMAIL_ERROR_MESSAGE, "invalid email", @@ -71,8 +75,35 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ipe.getMessage()); ______TS("failure: null parameter"); - assertThrows(AssertionError.class, - () -> accountRequestsLogic.createOrUpdateAccountRequest(null)); + assertThrows(AssertionError.class, () -> accountRequestsLogic.createAccountRequest(null)); + } + + @Test + public void testUpdateAccountRequest() throws Exception { + accountRequestsLogic.createAccountRequest(AccountRequestAttributes + .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") + .build()); + + ______TS("typical success case"); + AccountRequestAttributes.UpdateOptions updateOptions = AccountRequestAttributes + .updateOptionsBuilder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") + .withRegisteredAt(Const.TIME_REPRESENTS_NOW) + .build(); + accountRequestsLogic.updateAccountRequest(updateOptions); + + AccountRequestAttributes accountRequest = accountRequestsLogic + .getAccountRequest("valid@test.com", "TEAMMATES Test Institute 1"); + + assertEquals(Const.TIME_REPRESENTS_NOW, accountRequest.getRegisteredAt()); + + ______TS("failure: account request not found"); + AccountRequestAttributes.UpdateOptions updateOptionsNotFound = AccountRequestAttributes + .updateOptionsBuilder("not_found@test.com", "Unknown Test Institute 1", "Unknown Name") + .withRegisteredAt(Const.TIME_REPRESENTS_NOW) + .build(); + + assertThrows(EntityDoesNotExistException.class, + () -> accountRequestsLogic.updateAccountRequest(updateOptionsNotFound)); } @Test diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 8c63e76300d..827835a2bbd 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -3,7 +3,10 @@ import org.testng.annotations.Test; import teammates.common.datatransfer.attributes.AccountRequestAttributes; +import teammates.common.exception.EntityAlreadyExistsException; +import teammates.common.exception.EntityDoesNotExistException; import teammates.common.exception.InvalidParametersException; +import teammates.common.util.Const; import teammates.common.util.FieldValidator; import teammates.storage.entity.AccountRequest; import teammates.test.AssertHelper; @@ -17,25 +20,27 @@ public class AccountRequestsDbTest extends BaseTestCaseWithLocalDatabaseAccess { private final AccountRequestsDb accountRequestsDb = AccountRequestsDb.inst(); @Test - public void testCreateOrUpdateAccountRequest() throws Exception { - + public void testCreateAccountRequest() throws Exception { ______TS("typical success case"); AccountRequestAttributes accountRequest = AccountRequestAttributes .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") .build(); - accountRequest = accountRequestsDb.createOrUpdateAccountRequest(accountRequest); + accountRequest = accountRequestsDb.createEntity(accountRequest); verifyPresentInDatabase(accountRequest); - ______TS("duplicate account request, account request updated"); + ______TS("failure: duplicate account request"); - AccountRequestAttributes duplicateAccount = AccountRequestAttributes - .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name 2") + AccountRequestAttributes duplicateAccountRequest = AccountRequestAttributes + .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") .build(); - duplicateAccount = accountRequestsDb.createOrUpdateAccountRequest(duplicateAccount); - verifyPresentInDatabase(duplicateAccount); + assertThrows(EntityAlreadyExistsException.class, () -> { + accountRequestsDb.createEntity(duplicateAccountRequest); + }); + + accountRequestsDb.deleteAccountRequest("valid@test.com", "TEAMMATES Test Institute 1"); ______TS("failure case: invalid parameter"); @@ -44,7 +49,7 @@ public void testCreateOrUpdateAccountRequest() throws Exception { .build(); InvalidParametersException ipe = assertThrows(InvalidParametersException.class, - () -> accountRequestsDb.createOrUpdateAccountRequest(invalidAccountRequest)); + () -> accountRequestsDb.createEntity(invalidAccountRequest)); AssertHelper.assertContains( getPopulatedErrorMessage( FieldValidator.EMAIL_ERROR_MESSAGE, "invalid email", @@ -53,8 +58,35 @@ public void testCreateOrUpdateAccountRequest() throws Exception { ipe.getMessage()); ______TS("failure: null parameter"); - assertThrows(AssertionError.class, - () -> accountRequestsDb.createOrUpdateAccountRequest(null)); + assertThrows(AssertionError.class, () -> accountRequestsDb.createEntity(null)); + } + + @Test + public void testUpdateAccountRequest() throws Exception { + accountRequestsDb.createEntity(AccountRequestAttributes + .builder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") + .build()); + + ______TS("typical success case"); + AccountRequestAttributes.UpdateOptions updateOptions = AccountRequestAttributes + .updateOptionsBuilder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") + .withRegisteredAt(Const.TIME_REPRESENTS_NOW) + .build(); + accountRequestsDb.updateAccountRequest(updateOptions); + + AccountRequestAttributes accountRequest = accountRequestsDb + .getAccountRequest("valid@test.com", "TEAMMATES Test Institute 1"); + + assertEquals(Const.TIME_REPRESENTS_NOW, accountRequest.getRegisteredAt()); + + ______TS("failure: account request not found"); + AccountRequestAttributes.UpdateOptions updateOptionsNotFound = AccountRequestAttributes + .updateOptionsBuilder("not_found@test.com", "Unknown Test Institute 1", "Unknown Name") + .withRegisteredAt(Const.TIME_REPRESENTS_NOW) + .build(); + + assertThrows(EntityDoesNotExistException.class, + () -> accountRequestsDb.updateAccountRequest(updateOptionsNotFound)); } @Test From 6649b9c556ef3c5c1502101c4df994f27202b4f2 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Mon, 31 Jan 2022 16:05:36 +0800 Subject: [PATCH 55/79] Streamline API for join status --- .../ui/constants/ResourceEndpoints.java | 1 - .../java/teammates/ui/output/JoinStatus.java | 2 +- .../teammates/ui/webapi/ActionFactory.java | 1 - .../webapi/GetAccountRequestStatusAction.java | 40 ---------- .../ui/webapi/GetCourseJoinStatusAction.java | 25 +++++-- .../GetAccountRequestStatusActionTest.java | 73 ------------------- .../ui/webapi/GetActionClassesActionTest.java | 1 - .../webapi/GetCourseJoinStatusActionTest.java | 46 +++++++++++- src/web/app/user-join-page.component.spec.ts | 6 +- src/web/app/user-join-page.component.ts | 45 ++++++------ src/web/services/account.service.ts | 10 +-- src/web/services/course.service.spec.ts | 6 +- src/web/services/course.service.ts | 7 +- 13 files changed, 102 insertions(+), 161 deletions(-) delete mode 100644 src/main/java/teammates/ui/webapi/GetAccountRequestStatusAction.java delete mode 100644 src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java diff --git a/src/main/java/teammates/ui/constants/ResourceEndpoints.java b/src/main/java/teammates/ui/constants/ResourceEndpoints.java index 09548ac7366..4eb38fc7d8a 100644 --- a/src/main/java/teammates/ui/constants/ResourceEndpoints.java +++ b/src/main/java/teammates/ui/constants/ResourceEndpoints.java @@ -16,7 +16,6 @@ public enum ResourceEndpoints { ACCOUNT_RESET(ResourceURIs.ACCOUNT_RESET), ACCOUNT_DOWNGRADE(ResourceURIs.ACCOUNT_DOWNGRADE), ACCOUNT_REQUEST(ResourceURIs.ACCOUNT_REQUEST), - ACCOUNT_REQUEST_STATUS(ResourceURIs.ACCOUNT_REQUEST_STATUS), RESPONSE_COMMENT(ResourceURIs.RESPONSE_COMMENT), COURSE(ResourceURIs.COURSE), COURSE_ARCHIVE(ResourceURIs.COURSE_ARCHIVE), diff --git a/src/main/java/teammates/ui/output/JoinStatus.java b/src/main/java/teammates/ui/output/JoinStatus.java index ecac63612e9..aa3a04a126e 100644 --- a/src/main/java/teammates/ui/output/JoinStatus.java +++ b/src/main/java/teammates/ui/output/JoinStatus.java @@ -1,7 +1,7 @@ package teammates.ui.output; /** - * The join status of a course or account request. + * The join status of a course. */ public class JoinStatus extends ApiOutput { diff --git a/src/main/java/teammates/ui/webapi/ActionFactory.java b/src/main/java/teammates/ui/webapi/ActionFactory.java index 5dfcde9df12..3c45fb165a6 100644 --- a/src/main/java/teammates/ui/webapi/ActionFactory.java +++ b/src/main/java/teammates/ui/webapi/ActionFactory.java @@ -49,7 +49,6 @@ public final class ActionFactory { map(ResourceURIs.ACCOUNT_REQUEST, GET, GetAccountRequestAction.class); map(ResourceURIs.ACCOUNT_REQUEST, POST, CreateAccountRequestAction.class); map(ResourceURIs.ACCOUNT_REQUEST, DELETE, DeleteAccountRequestAction.class); - map(ResourceURIs.ACCOUNT_REQUEST_STATUS, GET, GetAccountRequestStatusAction.class); map(ResourceURIs.COURSE, GET, GetCourseAction.class); map(ResourceURIs.COURSE, DELETE, DeleteCourseAction.class); map(ResourceURIs.COURSE, POST, CreateCourseAction.class); diff --git a/src/main/java/teammates/ui/webapi/GetAccountRequestStatusAction.java b/src/main/java/teammates/ui/webapi/GetAccountRequestStatusAction.java deleted file mode 100644 index d071b568e2f..00000000000 --- a/src/main/java/teammates/ui/webapi/GetAccountRequestStatusAction.java +++ /dev/null @@ -1,40 +0,0 @@ -package teammates.ui.webapi; - -import teammates.common.datatransfer.attributes.AccountRequestAttributes; -import teammates.common.exception.EntityDoesNotExistException; -import teammates.common.util.Const; -import teammates.ui.output.JoinStatus; - -/** - * Get the join status of account request. - */ -class GetAccountRequestStatusAction extends Action { - - @Override - AuthType getMinAuthLevel() { - return AuthType.LOGGED_IN; - } - - @Override - void checkSpecificAccessControl() { - // Any user can use a join link as long as its parameters are valid - } - - @Override - public JsonResult execute() { - String regkey = getNonNullRequestParamValue(Const.ParamsNames.REGKEY); - - try { - AccountRequestAttributes accountRequest = logic.getAccountRequestForRegistrationKey(regkey); - boolean hasJoined = accountRequest.getRegisteredAt() != null; - return getJoinStatusResult(hasJoined); - } catch (EntityDoesNotExistException ednee) { - throw new EntityNotFoundException(ednee); - } - } - - private JsonResult getJoinStatusResult(boolean hasJoined) { - JoinStatus result = new JoinStatus(hasJoined); - return new JsonResult(result); - } -} diff --git a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java index 038f970a63b..d16ae569943 100644 --- a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java +++ b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java @@ -1,7 +1,9 @@ package teammates.ui.webapi; +import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.InstructorAttributes; import teammates.common.datatransfer.attributes.StudentAttributes; +import teammates.common.exception.EntityDoesNotExistException; import teammates.common.util.Const; import teammates.ui.output.JoinStatus; @@ -24,11 +26,13 @@ void checkSpecificAccessControl() { public JsonResult execute() { String regkey = getNonNullRequestParamValue(Const.ParamsNames.REGKEY); String entityType = getNonNullRequestParamValue(Const.ParamsNames.ENTITY_TYPE); + boolean isCreatingAccount = getRequestParamValue(Const.ParamsNames.IS_CREATING_ACCOUNT).equals("true"); + switch (entityType) { case Const.EntityType.STUDENT: return getStudentJoinStatus(regkey); case Const.EntityType.INSTRUCTOR: - return getInstructorJoinStatus(regkey); + return getInstructorJoinStatus(regkey, isCreatingAccount); default: throw new InvalidHttpParameterException("Error: invalid entity type"); } @@ -42,12 +46,21 @@ private JsonResult getStudentJoinStatus(String regkey) { return getJoinStatusResult(student.isRegistered()); } - private JsonResult getInstructorJoinStatus(String regkey) { - InstructorAttributes instructor = logic.getInstructorForRegistrationKey(regkey); - if (instructor == null) { - throw new EntityNotFoundException("No instructor with given registration key: " + regkey); + private JsonResult getInstructorJoinStatus(String regkey, boolean isCreatingAccount) { + if (isCreatingAccount) { + try { + AccountRequestAttributes accountRequest = logic.getAccountRequestForRegistrationKey(regkey); + return getJoinStatusResult(accountRequest.getRegisteredAt() != null); + } catch (EntityDoesNotExistException ednee) { + throw new EntityNotFoundException(ednee); + } + } else { + InstructorAttributes instructor = logic.getInstructorForRegistrationKey(regkey); + if (instructor == null) { + throw new EntityNotFoundException("No instructor with given registration key: " + regkey); + } + return getJoinStatusResult(instructor.isRegistered()); } - return getJoinStatusResult(instructor.isRegistered()); } private JsonResult getJoinStatusResult(boolean hasJoined) { diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java deleted file mode 100644 index 338a74193ac..00000000000 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestStatusActionTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package teammates.ui.webapi; - -import org.testng.annotations.Test; - -import teammates.common.util.Const; -import teammates.ui.output.JoinStatus; - -/** - * SUT: {@link GetAccountRequestStatusAction}. - */ -public class GetAccountRequestStatusActionTest extends BaseActionTest { - - @Override - protected String getActionUri() { - return Const.ResourceURIs.ACCOUNT_REQUEST_STATUS; - } - - @Override - protected String getRequestMethod() { - return GET; - } - - @Override - @Test - protected void testExecute() throws Exception { - - loginAsUnregistered("unreg.user"); - - ______TS("Not enough parameters"); - - verifyHttpParameterFailure(); - - ______TS("Normal case: account request not used"); - - String registrationKey = logic.getAccountRequest("unregisteredinstructor1@gmail.tmt", - "TEAMMATES Test Institute 1").getRegistrationKey(); - - String[] params = new String[] { Const.ParamsNames.REGKEY, registrationKey, }; - - GetAccountRequestStatusAction getAccountRequestStatusAction = getAction(params); - JsonResult result = getJsonResult(getAccountRequestStatusAction); - - JoinStatus output = (JoinStatus) result.getOutput(); - assertFalse(output.getHasJoined()); - - ______TS("Normal case: account request already used"); - - String usedRegistrationKey = - logic.getAccountRequest("instr1@course1.tmt", "TEAMMATES Test Institute 1").getRegistrationKey(); - - params = new String[] { Const.ParamsNames.REGKEY, usedRegistrationKey, }; - - getAccountRequestStatusAction = getAction(params); - result = getJsonResult(getAccountRequestStatusAction); - - output = (JoinStatus) result.getOutput(); - assertTrue(output.getHasJoined()); - - ______TS("Failure case: regkey is not valid"); - - params = new String[] { Const.ParamsNames.REGKEY, "invalid-registration-key", }; - - verifyEntityNotFound(params); - - } - - @Override - @Test - protected void testAccessControl() { - verifyAnyLoggedInUserCanAccess(); - } - -} diff --git a/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java b/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java index c1b1364b20b..2aacb803c48 100644 --- a/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetActionClassesActionTest.java @@ -91,7 +91,6 @@ protected void testExecute() { CreateAccountAction.class, CreateAccountRequestAction.class, GetAccountRequestAction.class, - GetAccountRequestStatusAction.class, DeleteAccountRequestAction.class, GetAccountAction.class, FeedbackSessionPublishedRemindersAction.class, diff --git a/src/test/java/teammates/ui/webapi/GetCourseJoinStatusActionTest.java b/src/test/java/teammates/ui/webapi/GetCourseJoinStatusActionTest.java index 577cab62e65..da87aff41ae 100644 --- a/src/test/java/teammates/ui/webapi/GetCourseJoinStatusActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetCourseJoinStatusActionTest.java @@ -22,7 +22,7 @@ protected String getRequestMethod() { @Override @Test - protected void testExecute() { + protected void testExecute() throws Exception { loginAsUnregistered("unreg.user"); @@ -118,6 +118,50 @@ protected void testExecute() { verifyEntityNotFound(params); + ______TS("Normal case: account request not used, instructor has not joined course"); + + String accountRequestNotUsedKey = logic.getAccountRequest("unregisteredinstructor1@gmail.tmt", + "TEAMMATES Test Institute 1").getRegistrationKey(); + + params = new String[] { + Const.ParamsNames.REGKEY, accountRequestNotUsedKey, + Const.ParamsNames.ENTITY_TYPE, Const.EntityType.INSTRUCTOR, + Const.ParamsNames.IS_CREATING_ACCOUNT, "true", + }; + + getCourseJoinStatusAction = getAction(params); + result = getJsonResult(getCourseJoinStatusAction); + + output = (JoinStatus) result.getOutput(); + assertFalse(output.getHasJoined()); + + ______TS("Normal case: account request already used, instructor has joined course"); + + String accountRequestUsedKey = + logic.getAccountRequest("instr1@course1.tmt", "TEAMMATES Test Institute 1").getRegistrationKey(); + + params = new String[] { + Const.ParamsNames.REGKEY, accountRequestUsedKey, + Const.ParamsNames.ENTITY_TYPE, Const.EntityType.INSTRUCTOR, + Const.ParamsNames.IS_CREATING_ACCOUNT, "true", + }; + + getCourseJoinStatusAction = getAction(params); + result = getJsonResult(getCourseJoinStatusAction); + + output = (JoinStatus) result.getOutput(); + assertTrue(output.getHasJoined()); + + ______TS("Failure case: account request regkey is not valid"); + + params = new String[] { + Const.ParamsNames.REGKEY, "invalid-registration-key", + Const.ParamsNames.ENTITY_TYPE, Const.EntityType.INSTRUCTOR, + Const.ParamsNames.IS_CREATING_ACCOUNT, "true", + }; + + verifyEntityNotFound(params); + ______TS("Failure case: invalid entity type"); params = new String[] { diff --git a/src/web/app/user-join-page.component.spec.ts b/src/web/app/user-join-page.component.spec.ts index 6b4588a0936..f2618413bfe 100644 --- a/src/web/app/user-join-page.component.spec.ts +++ b/src/web/app/user-join-page.component.spec.ts @@ -193,6 +193,7 @@ describe('UserJoinPageComponent creating account', () => { let navService: NavigationService; let authService: AuthService; let accountService: AccountService; + let courseService: CourseService; beforeEach((() => { TestBed.resetTestingModule(); @@ -227,6 +228,7 @@ describe('UserJoinPageComponent creating account', () => { navService = TestBed.inject(NavigationService); authService = TestBed.inject(AuthService); accountService = TestBed.inject(AccountService); + courseService = TestBed.inject(CourseService); fixture.detectChanges(); }); @@ -265,7 +267,7 @@ describe('UserJoinPageComponent creating account', () => { }, masquerade: false, })); - spyOn(accountService, 'getRegisteredStatus').and.returnValue(of({ + spyOn(courseService, 'getJoinCourseStatus').and.returnValue(of({ hasJoined: true, })); const navSpy: Spy = spyOn(navService, 'navigateByURL'); @@ -289,7 +291,7 @@ describe('UserJoinPageComponent creating account', () => { }, masquerade: false, })); - spyOn(accountService, 'getRegisteredStatus').and.returnValue(throwError({ + spyOn(courseService, 'getJoinCourseStatus').and.returnValue(throwError({ status: 404, })); diff --git a/src/web/app/user-join-page.component.ts b/src/web/app/user-join-page.component.ts index 3abfa7d9f10..2ff8484f4d9 100644 --- a/src/web/app/user-join-page.component.ts +++ b/src/web/app/user-join-page.component.ts @@ -1,7 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { Observable } from 'rxjs'; import { finalize } from 'rxjs/operators'; import { environment } from '../environments/environment'; import { AccountService } from '../services/account.service'; @@ -64,29 +63,27 @@ export class UserJoinPageComponent implements OnInit { } this.userId = auth.user.id; - const request: Observable = this.isCreatingAccount - ? this.accountService.getRegisteredStatus(this.key) - : this.courseService.getJoinCourseStatus(this.key, this.entityType); - - request.subscribe((resp: JoinStatus) => { - this.hasJoined = resp.hasJoined; - if (this.hasJoined) { - // The regkey has been used; simply redirect the user to their home page, - // regardless of whether the regkey matches or not. - this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); - } else { - this.isLoading = false; - } - }, (resp: ErrorMessageOutput) => { - if (resp.status === 404) { - this.validUrl = false; - this.isLoading = false; - return; - } - const modalRef: any = this.ngbModal.open(ErrorReportComponent); - modalRef.componentInstance.requestId = resp.error.requestId; - modalRef.componentInstance.errorMessage = resp.error.message; - }); + this.courseService + .getJoinCourseStatus(this.key, this.entityType, this.isCreatingAccount) + .subscribe((resp: JoinStatus) => { + this.hasJoined = resp.hasJoined; + if (this.hasJoined) { + // The regkey has been used; simply redirect the user to their home page, + // regardless of whether the regkey matches or not. + this.navigationService.navigateByURL(this.router, `/web/${this.entityType}/home`); + } else { + this.isLoading = false; + } + }, (resp: ErrorMessageOutput) => { + if (resp.status === 404) { + this.validUrl = false; + this.isLoading = false; + return; + } + const modalRef: any = this.ngbModal.open(ErrorReportComponent); + modalRef.componentInstance.requestId = resp.error.requestId; + modalRef.componentInstance.errorMessage = resp.error.message; + }); }); }); } diff --git a/src/web/services/account.service.ts b/src/web/services/account.service.ts index 594d2407354..e65362a3516 100644 --- a/src/web/services/account.service.ts +++ b/src/web/services/account.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { ResourceEndpoints } from '../types/api-const'; -import { Account, JoinLink, JoinStatus, MessageOutput } from '../types/api-output'; +import { Account, JoinLink, MessageOutput } from '../types/api-output'; import { AccountCreateRequest } from '../types/api-request'; import { HttpRequestService } from './http-request.service'; @@ -82,12 +82,4 @@ export class AccountService { return this.httpRequestService.get(ResourceEndpoints.ACCOUNT, paramMap); } - /** - * Get the status of whether the registration key has been used by calling API. - */ - getRegisteredStatus(regKey: string): Observable { - const paramMap: Record = { key: regKey }; - return this.httpRequestService.get(ResourceEndpoints.ACCOUNT_REQUEST_STATUS, paramMap); - } - } diff --git a/src/web/services/course.service.spec.ts b/src/web/services/course.service.spec.ts index 34d2fd5e269..be6f47b34a4 100644 --- a/src/web/services/course.service.spec.ts +++ b/src/web/services/course.service.spec.ts @@ -171,7 +171,11 @@ describe('CourseService', () => { key: regKey, entitytype: entityType, }; - service.getJoinCourseStatus(regKey, entityType); + service.getJoinCourseStatus(regKey, entityType, false); + expect(spyHttpRequestService.get).toHaveBeenCalledWith(ResourceEndpoints.JOIN, paramMap); + + paramMap.isCreatingAccount = 'true'; + service.getJoinCourseStatus(regKey, entityType, true); expect(spyHttpRequestService.get).toHaveBeenCalledWith(ResourceEndpoints.JOIN, paramMap); }); diff --git a/src/web/services/course.service.ts b/src/web/services/course.service.ts index 487174ac8b8..52104b8cba8 100644 --- a/src/web/services/course.service.ts +++ b/src/web/services/course.service.ts @@ -168,11 +168,16 @@ export class CourseService { /** * Get the status of whether the entity has joined the course by calling API. */ - getJoinCourseStatus(regKey: string, entityType: string): Observable { + getJoinCourseStatus(regKey: string, entityType: string, isCreatingAccount: boolean): Observable { const paramMap: Record = { key: regKey, entitytype: entityType, }; + + if (isCreatingAccount) { + paramMap.isCreatingAccount = 'true'; + } + return this.httpRequestService.get(ResourceEndpoints.JOIN, paramMap); } From 4a82bbe53f48f17e7b5c92e7f4509e65e6ac17e5 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Mon, 31 Jan 2022 16:11:44 +0800 Subject: [PATCH 56/79] Minor bug fix --- .../java/teammates/ui/webapi/GetCourseJoinStatusAction.java | 4 ++-- src/web/services/course.service.spec.ts | 2 +- src/web/services/course.service.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java index d16ae569943..97b826e1c85 100644 --- a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java +++ b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java @@ -26,13 +26,13 @@ void checkSpecificAccessControl() { public JsonResult execute() { String regkey = getNonNullRequestParamValue(Const.ParamsNames.REGKEY); String entityType = getNonNullRequestParamValue(Const.ParamsNames.ENTITY_TYPE); - boolean isCreatingAccount = getRequestParamValue(Const.ParamsNames.IS_CREATING_ACCOUNT).equals("true"); + String isCreatingAccount = getRequestParamValue(Const.ParamsNames.IS_CREATING_ACCOUNT); switch (entityType) { case Const.EntityType.STUDENT: return getStudentJoinStatus(regkey); case Const.EntityType.INSTRUCTOR: - return getInstructorJoinStatus(regkey, isCreatingAccount); + return getInstructorJoinStatus(regkey, isCreatingAccount != null && isCreatingAccount.equals("true")); default: throw new InvalidHttpParameterException("Error: invalid entity type"); } diff --git a/src/web/services/course.service.spec.ts b/src/web/services/course.service.spec.ts index be6f47b34a4..5a27d26b1ed 100644 --- a/src/web/services/course.service.spec.ts +++ b/src/web/services/course.service.spec.ts @@ -174,7 +174,7 @@ describe('CourseService', () => { service.getJoinCourseStatus(regKey, entityType, false); expect(spyHttpRequestService.get).toHaveBeenCalledWith(ResourceEndpoints.JOIN, paramMap); - paramMap.isCreatingAccount = 'true'; + paramMap.iscreatingaccount = 'true'; service.getJoinCourseStatus(regKey, entityType, true); expect(spyHttpRequestService.get).toHaveBeenCalledWith(ResourceEndpoints.JOIN, paramMap); }); diff --git a/src/web/services/course.service.ts b/src/web/services/course.service.ts index 52104b8cba8..59eaed764c9 100644 --- a/src/web/services/course.service.ts +++ b/src/web/services/course.service.ts @@ -175,7 +175,7 @@ export class CourseService { }; if (isCreatingAccount) { - paramMap.isCreatingAccount = 'true'; + paramMap.iscreatingaccount = 'true'; } return this.httpRequestService.get(ResourceEndpoints.JOIN, paramMap); From 38868564f120122ff35168ebea1e3704221ae9ef Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 00:12:57 +0800 Subject: [PATCH 57/79] Remove account request status constant --- src/main/java/teammates/common/util/Const.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/teammates/common/util/Const.java b/src/main/java/teammates/common/util/Const.java index dc24e26cd3e..0c6eca3f339 100644 --- a/src/main/java/teammates/common/util/Const.java +++ b/src/main/java/teammates/common/util/Const.java @@ -298,7 +298,6 @@ public static class ResourceURIs { public static final String ACCOUNT_RESET = URI_PREFIX + "/account/reset"; public static final String ACCOUNT_DOWNGRADE = URI_PREFIX + "/account/downgrade"; public static final String ACCOUNT_REQUEST = URI_PREFIX + "/account/request"; - public static final String ACCOUNT_REQUEST_STATUS = URI_PREFIX + "/account/request/status"; public static final String RESPONSE_COMMENT = URI_PREFIX + "/responsecomment"; public static final String COURSE = URI_PREFIX + "/course"; public static final String COURSE_ARCHIVE = URI_PREFIX + "/course/archive"; From d500cbaf5e3794c90610939ff5253bfaf9c99b93 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 00:36:26 +0800 Subject: [PATCH 58/79] Refactor get account request methods --- src/main/java/teammates/logic/api/Logic.java | 7 ++----- .../logic/core/AccountRequestsLogic.java | 17 ++--------------- .../ui/webapi/CreateAccountAction.java | 9 ++++----- .../ui/webapi/CreateAccountRequestAction.java | 17 +---------------- .../ui/webapi/GetAccountRequestAction.java | 11 +++++------ .../ui/webapi/GetCourseJoinStatusAction.java | 10 ++++------ .../logic/core/AccountRequestsLogicTest.java | 8 +++----- .../BaseTestCaseWithLocalDatabaseAccess.java | 7 +------ .../ui/webapi/CreateAccountActionTest.java | 2 +- .../webapi/CreateAccountRequestActionTest.java | 2 +- .../webapi/DeleteAccountRequestActionTest.java | 3 +-- .../ui/webapi/GetAccountRequestActionTest.java | 2 +- .../webapi/GetCourseJoinStatusActionTest.java | 2 +- 13 files changed, 27 insertions(+), 70 deletions(-) diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 94d04808be0..92c23a46223 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -1449,8 +1449,7 @@ public void deleteAccountRequest(String email, String institute) { * @return the account request * @throws EntityDoesNotExistException if the account request does not exist */ - public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) - throws EntityDoesNotExistException { + public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { assert registrationKey != null; return accountRequestsLogic.getAccountRequestForRegistrationKey(registrationKey); @@ -1463,10 +1462,8 @@ public AccountRequestAttributes getAccountRequestForRegistrationKey(String regis * * All parameters are non-null. * * @return the account request - * @throws EntityDoesNotExistException if the account request does not exist */ - public AccountRequestAttributes getAccountRequest(String email, String institute) - throws EntityDoesNotExistException { + public AccountRequestAttributes getAccountRequest(String email, String institute) { assert email != null; assert institute != null; diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index 00e69e7b0bd..b78a45d4377 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -64,17 +64,10 @@ public void deleteAccountRequest(String email, String institute) { * Gets an account request by email address and institute. * * @return the account request - * @throws EntityDoesNotExistException if account request does not exist */ - public AccountRequestAttributes getAccountRequest(String email, String institute) - throws EntityDoesNotExistException { + public AccountRequestAttributes getAccountRequest(String email, String institute) { AccountRequestAttributes accountRequest = accountRequestsDb.getAccountRequest(email, institute); - if (accountRequest == null) { - throw new EntityDoesNotExistException( - "Account request with email " + email + " and institute " + institute + " does not exist"); - } - return accountRequest; } @@ -84,16 +77,10 @@ public AccountRequestAttributes getAccountRequest(String email, String institute * @return the account request * @throws EntityDoesNotExistException if account request does not exist */ - public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) - throws EntityDoesNotExistException { + public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { AccountRequestAttributes accountRequest = accountRequestsDb .getAccountRequestForRegistrationKey(registrationKey); - if (accountRequest == null) { - throw new EntityDoesNotExistException( - "Account request with registration key " + registrationKey + " does not exist"); - } - return accountRequest; } diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 55d61c44e53..880f3973efd 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -41,12 +41,11 @@ void checkSpecificAccessControl() { public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOperationException { String registrationKey = getNonNullRequestParamValue(Const.ParamsNames.REGKEY); - AccountRequestAttributes accountRequestAttributes; + AccountRequestAttributes accountRequestAttributes = logic.getAccountRequestForRegistrationKey(registrationKey); - try { - accountRequestAttributes = logic.getAccountRequestForRegistrationKey(registrationKey); - } catch (EntityDoesNotExistException ednee) { - throw new EntityNotFoundException(ednee); + if (accountRequestAttributes == null) { + throw new EntityNotFoundException("Account request could with registration key " + + registrationKey + " could not be found"); } if (accountRequestAttributes.getRegisteredAt() != null) { diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index c2949e71a09..1798973759a 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -1,13 +1,9 @@ package teammates.ui.webapi; -import org.apache.http.HttpStatus; - import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.exception.EntityAlreadyExistsException; -import teammates.common.exception.EntityDoesNotExistException; import teammates.common.exception.InvalidParametersException; import teammates.common.util.EmailWrapper; -import teammates.common.util.Logger; import teammates.ui.output.JoinLinkData; import teammates.ui.request.AccountCreateRequest; import teammates.ui.request.InvalidHttpRequestBodyException; @@ -17,8 +13,6 @@ */ class CreateAccountRequestAction extends AdminOnlyAction { - private static final Logger log = Logger.getLogger(); - @Override public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOperationException { AccountCreateRequest createRequest = getAndValidateRequestBody(AccountCreateRequest.class); @@ -38,16 +32,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera throw new InvalidHttpRequestBodyException(ipe); } catch (EntityAlreadyExistsException eaee) { // Continue without creating a new account request - } - - if (accountRequestAttributes == null) { - try { - logic.getAccountRequest(instructorEmail, instructorInstitution); - } catch (EntityDoesNotExistException ednee) { - // Entity should exist - log.severe("Unexpected error", ednee); - return new JsonResult("Unexpected Error", HttpStatus.SC_INTERNAL_SERVER_ERROR); - } + accountRequestAttributes = logic.getAccountRequest(instructorEmail, instructorInstitution); } String joinLink = accountRequestAttributes.getRegistrationUrl(); diff --git a/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java b/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java index f77e0320424..4f6958a0ef6 100644 --- a/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/GetAccountRequestAction.java @@ -1,7 +1,6 @@ package teammates.ui.webapi; import teammates.common.datatransfer.attributes.AccountRequestAttributes; -import teammates.common.exception.EntityDoesNotExistException; import teammates.common.util.Const; import teammates.ui.output.AccountRequestData; @@ -15,11 +14,11 @@ public JsonResult execute() { String email = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_EMAIL); String institute = getNonNullRequestParamValue(Const.ParamsNames.INSTRUCTOR_INSTITUTION); - AccountRequestAttributes accountRequestInfo; - try { - accountRequestInfo = logic.getAccountRequest(email, institute); - } catch (EntityDoesNotExistException ednee) { - throw new EntityNotFoundException(ednee); + AccountRequestAttributes accountRequestInfo = logic.getAccountRequest(email, institute); + + if (accountRequestInfo == null) { + throw new EntityNotFoundException("Account request for email: " + + email + " and institute: " + institute + " not found."); } AccountRequestData output = new AccountRequestData(accountRequestInfo); diff --git a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java index 97b826e1c85..8a2d906c315 100644 --- a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java +++ b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java @@ -3,7 +3,6 @@ import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.datatransfer.attributes.InstructorAttributes; import teammates.common.datatransfer.attributes.StudentAttributes; -import teammates.common.exception.EntityDoesNotExistException; import teammates.common.util.Const; import teammates.ui.output.JoinStatus; @@ -48,12 +47,11 @@ private JsonResult getStudentJoinStatus(String regkey) { private JsonResult getInstructorJoinStatus(String regkey, boolean isCreatingAccount) { if (isCreatingAccount) { - try { - AccountRequestAttributes accountRequest = logic.getAccountRequestForRegistrationKey(regkey); - return getJoinStatusResult(accountRequest.getRegisteredAt() != null); - } catch (EntityDoesNotExistException ednee) { - throw new EntityNotFoundException(ednee); + AccountRequestAttributes accountRequest = logic.getAccountRequestForRegistrationKey(regkey); + if (accountRequest == null) { + throw new EntityNotFoundException("No account request with given registration key: " + regkey); } + return getJoinStatusResult(accountRequest.getRegisteredAt() != null); } else { InstructorAttributes instructor = logic.getInstructorForRegistrationKey(regkey); if (instructor == null) { diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index c979b02f7fa..630f504ad29 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -145,8 +145,7 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { ______TS("account request not found"); - assertThrows(EntityDoesNotExistException.class, - () -> accountRequestsLogic.getAccountRequestForRegistrationKey("not-found")); + assertNull(accountRequestsLogic.getAccountRequestForRegistrationKey("not-found")); ______TS("failure null parameter"); @@ -155,7 +154,7 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { } @Test - public void testGetAccountRequest() throws Exception { + public void testGetAccountRequest() { AccountRequestAttributes a = dataBundle.accountRequests.get("unregisteredInstructor1"); ______TS("typical success case"); @@ -166,8 +165,7 @@ public void testGetAccountRequest() throws Exception { ______TS("account request not found"); - assertThrows(EntityDoesNotExistException.class, - () -> accountRequestsLogic.getAccountRequest("not-found@test.com", "not-found")); + assertNull(accountRequestsLogic.getAccountRequest("not-found@test.com", "not-found")); ______TS("failure null parameter"); diff --git a/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java b/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java index 65d4afc9ba8..e021b5ff15a 100644 --- a/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java +++ b/src/test/java/teammates/test/BaseTestCaseWithLocalDatabaseAccess.java @@ -23,7 +23,6 @@ import teammates.common.datatransfer.attributes.InstructorAttributes; import teammates.common.datatransfer.attributes.StudentAttributes; import teammates.common.datatransfer.attributes.StudentProfileAttributes; -import teammates.common.exception.EntityDoesNotExistException; import teammates.logic.api.LogicExtension; import teammates.logic.core.LogicStarter; import teammates.storage.api.OfyHelper; @@ -136,11 +135,7 @@ protected StudentAttributes getStudent(StudentAttributes student) { @Override protected AccountRequestAttributes getAccountRequest(AccountRequestAttributes accountRequest) { - try { - return logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); - } catch (EntityDoesNotExistException ednee) { - return null; - } + return logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute()); } protected void removeAndRestoreTypicalDataBundle() { diff --git a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java index 1c751e446d4..4ad6d8c7ae8 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountActionTest.java @@ -29,7 +29,7 @@ protected String getRequestMethod() { @Override @Test - protected void testExecute() throws Exception { + protected void testExecute() { String name = "Typical Instructor Name"; String email = "unregisteredinstructor1@gmail.tmt"; String institute = "TEAMMATES Test Institute 1"; diff --git a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java index 6a0aa14fa02..da784c9ad36 100644 --- a/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/CreateAccountRequestActionTest.java @@ -27,7 +27,7 @@ protected String getRequestMethod() { @Override @Test - protected void testExecute() throws Exception { + protected void testExecute() { loginAsAdmin(); String name = "JamesBond"; String email = "jamesbond89@gmail.tmt"; diff --git a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java index 7e7205bf704..92a42b25e6d 100644 --- a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java @@ -45,8 +45,7 @@ protected void testExecute() { assertEquals(msg.getMessage(), "Account request successfully deleted."); - assertThrows(EntityDoesNotExistException.class, - () -> logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); + assertNull(logic.getAccountRequest(accountRequest.getEmail(), accountRequest.getInstitute())); ______TS("Typical case, delete non-existing account request"); diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index 47c736e65eb..eff9dc685b2 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -23,7 +23,7 @@ protected String getRequestMethod() { @Override @Test - protected void testExecute() throws Exception { + protected void testExecute() { AccountRequestAttributes accountRequest = logic.getAccountRequest("unregisteredinstructor1@gmail.tmt", "TEAMMATES Test Institute 1"); diff --git a/src/test/java/teammates/ui/webapi/GetCourseJoinStatusActionTest.java b/src/test/java/teammates/ui/webapi/GetCourseJoinStatusActionTest.java index da87aff41ae..c0a945b128d 100644 --- a/src/test/java/teammates/ui/webapi/GetCourseJoinStatusActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetCourseJoinStatusActionTest.java @@ -22,7 +22,7 @@ protected String getRequestMethod() { @Override @Test - protected void testExecute() throws Exception { + protected void testExecute() { loginAsUnregistered("unreg.user"); From a70285391ba26d402f06af0751de776524ee2344 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 00:37:51 +0800 Subject: [PATCH 59/79] Simplify condition --- .../java/teammates/ui/webapi/GetCourseJoinStatusAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java index 8a2d906c315..239a1e9467f 100644 --- a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java +++ b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java @@ -31,7 +31,7 @@ public JsonResult execute() { case Const.EntityType.STUDENT: return getStudentJoinStatus(regkey); case Const.EntityType.INSTRUCTOR: - return getInstructorJoinStatus(regkey, isCreatingAccount != null && isCreatingAccount.equals("true")); + return getInstructorJoinStatus(regkey, "true".equals(isCreatingAccount)); default: throw new InvalidHttpParameterException("Error: invalid entity type"); } From ce2f36a2a0909eadaeb4f54f890323b952ea2ae4 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 00:38:13 +0800 Subject: [PATCH 60/79] Fix typo --- src/web/app/user-join-page.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/app/user-join-page.component.ts b/src/web/app/user-join-page.component.ts index 2ff8484f4d9..04d7dd8e65c 100644 --- a/src/web/app/user-join-page.component.ts +++ b/src/web/app/user-join-page.component.ts @@ -45,7 +45,7 @@ export class UserJoinPageComponent implements OnInit { this.key = queryParams.key; this.isCreatingAccount = queryParams.iscreatingaccount === 'true'; - // Create cccount request can only come from instructor. + // Create account request can only come from instructor. if (this.isCreatingAccount) { this.entityType = 'instructor'; } From 8d4ad391b542f2afc911e02de158c2c9ca8492cf Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 00:39:13 +0800 Subject: [PATCH 61/79] Remove else block --- .../ui/webapi/GetCourseJoinStatusAction.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java index 239a1e9467f..9440844881d 100644 --- a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java +++ b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java @@ -52,13 +52,15 @@ private JsonResult getInstructorJoinStatus(String regkey, boolean isCreatingAcco throw new EntityNotFoundException("No account request with given registration key: " + regkey); } return getJoinStatusResult(accountRequest.getRegisteredAt() != null); - } else { - InstructorAttributes instructor = logic.getInstructorForRegistrationKey(regkey); - if (instructor == null) { - throw new EntityNotFoundException("No instructor with given registration key: " + regkey); - } - return getJoinStatusResult(instructor.isRegistered()); } + + InstructorAttributes instructor = logic.getInstructorForRegistrationKey(regkey); + + if (instructor == null) { + throw new EntityNotFoundException("No instructor with given registration key: " + regkey); + } + + return getJoinStatusResult(instructor.isRegistered()); } private JsonResult getJoinStatusResult(boolean hasJoined) { From b046861c26185f48017260efc4014c1aee9bcf6a Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 01:44:35 +0800 Subject: [PATCH 62/79] Create e2e test for account requests --- ...ctorCourseJoinConfirmationPageE2ETest.java | 29 +++++++++++++++++++ ...ctorCourseJoinConfirmationPageE2ETest.json | 17 ++++++++++- .../java/teammates/test/AbstractBackDoor.java | 16 ++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java index e5b10a4375f..2a61989b934 100644 --- a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java @@ -51,5 +51,34 @@ public void testAll() { ______TS("Already joined, no confirmation page"); getNewPageInstance(joinLink, InstructorHomePage.class); + + logout(); + + ______TS("Click join link: invalid key"); + joinLink = createFrontendUrl(Const.WebPageURIs.JOIN_PAGE) + .withIsCreatingAccount(String.valueOf(true)) + .withRegistrationKey("invalidKey") + .withEntityType(Const.EntityType.INSTRUCTOR); + confirmationPage = loginToPage(joinLink, CourseJoinConfirmationPage.class, "ICJoinConf.newinstr"); + + confirmationPage.verifyDisplayedMessage("The course join link is invalid. You may have " + + "entered the URL incorrectly or the URL may correspond to a/an instructor that does not exist."); + + ______TS("Click join link: valid account request key"); + + String regKey = BACKDOOR + .getRegKeyForAccountRequest("ICJoinConf.newinstr@gmail.tmt", "TEAMMATES Test Institute 1"); + + joinLink = createFrontendUrl(Const.WebPageURIs.JOIN_PAGE) + .withIsCreatingAccount(String.valueOf(true)) + .withRegistrationKey(regKey); + + confirmationPage = getNewPageInstance(joinLink, CourseJoinConfirmationPage.class); + confirmationPage.verifyJoiningUser("ICJoinConf.newinstr"); + confirmationPage.confirmJoinCourse(InstructorHomePage.class); + + ______TS("Regkey for account request used, no confirmation page"); + + getNewPageInstance(joinLink, InstructorHomePage.class); } } diff --git a/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json b/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json index fe528e39f6f..894187cc2cb 100644 --- a/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json +++ b/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json @@ -8,10 +8,25 @@ "institute": "TEAMMATES Test Institute 1" } }, + "accountRequests": { + "ICJoinConf.instr.CS1101": { + "name": "Teammates Test 2", + "email": "ICJoinConf.instr2@gmail.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z", + "registeredAt": "1970-02-14T00:00:00Z" + }, + "ICJoinConf.newinstr": { + "name": "Teammates Test 3", + "email": "ICJoinConf.newinstr@gmail.tmt", + "institute": "TEAMMATES Test Institute 1", + "createdAt": "2011-01-01T00:00:00Z" + } + }, "courses": { "ICJoinConf.CS1101": { "id": "tm.e2e.ICJoinConf.CS1101", - "name": "Programming Methodology", + "name": "CS1102", "institute": "TEAMMATES Test Institute 1", "timeZone": "UTC" } diff --git a/src/test/java/teammates/test/AbstractBackDoor.java b/src/test/java/teammates/test/AbstractBackDoor.java index 278b904c6f4..d8573e2806a 100644 --- a/src/test/java/teammates/test/AbstractBackDoor.java +++ b/src/test/java/teammates/test/AbstractBackDoor.java @@ -748,6 +748,22 @@ public AccountRequestAttributes getAccountRequest(String email, String institute .build(); } + /** + * Gets registration key of an account request from the database. + */ + public String getRegKeyForAccountRequest(String email, String institute) { + Map params = new HashMap<>(); + params.put(Const.ParamsNames.INSTRUCTOR_EMAIL, email); + params.put(Const.ParamsNames.INSTRUCTOR_INSTITUTION, institute); + + ResponseBodyAndCode response = executeGetRequest(Const.ResourceURIs.ACCOUNT_REQUEST, params); + if (response.responseCode == HttpStatus.SC_NOT_FOUND) { + return null; + } + + return JsonUtils.fromJson(response.responseBody, AccountRequestData.class).getRegistrationKey(); + } + /** * Deletes an account request from the database. */ From ce57ef34690ada91ab567ec441b433103a83b2ee Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 01:49:16 +0800 Subject: [PATCH 63/79] Fix lint issues --- .../InstructorCourseJoinConfirmationPageE2ETest.java | 2 +- .../java/teammates/logic/core/AccountRequestsLogic.java | 9 ++------- .../teammates/ui/webapi/GetCourseJoinStatusAction.java | 4 ++-- .../ui/webapi/DeleteAccountRequestActionTest.java | 1 - 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java index 2a61989b934..f9a7dfebe44 100644 --- a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java @@ -72,7 +72,7 @@ public void testAll() { joinLink = createFrontendUrl(Const.WebPageURIs.JOIN_PAGE) .withIsCreatingAccount(String.valueOf(true)) .withRegistrationKey(regKey); - + confirmationPage = getNewPageInstance(joinLink, CourseJoinConfirmationPage.class); confirmationPage.verifyJoiningUser("ICJoinConf.newinstr"); confirmationPage.confirmJoinCourse(InstructorHomePage.class); diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index b78a45d4377..d99efa8d99b 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -66,9 +66,7 @@ public void deleteAccountRequest(String email, String institute) { * @return the account request */ public AccountRequestAttributes getAccountRequest(String email, String institute) { - AccountRequestAttributes accountRequest = accountRequestsDb.getAccountRequest(email, institute); - - return accountRequest; + return accountRequestsDb.getAccountRequest(email, institute); } /** @@ -78,10 +76,7 @@ public AccountRequestAttributes getAccountRequest(String email, String institute * @throws EntityDoesNotExistException if account request does not exist */ public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { - AccountRequestAttributes accountRequest = accountRequestsDb - .getAccountRequestForRegistrationKey(registrationKey); - - return accountRequest; + return accountRequestsDb.getAccountRequestForRegistrationKey(registrationKey); } } diff --git a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java index 9440844881d..4b45ca24716 100644 --- a/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java +++ b/src/main/java/teammates/ui/webapi/GetCourseJoinStatusAction.java @@ -53,9 +53,9 @@ private JsonResult getInstructorJoinStatus(String regkey, boolean isCreatingAcco } return getJoinStatusResult(accountRequest.getRegisteredAt() != null); } - + InstructorAttributes instructor = logic.getInstructorForRegistrationKey(regkey); - + if (instructor == null) { throw new EntityNotFoundException("No instructor with given registration key: " + regkey); } diff --git a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java index 92a42b25e6d..351ad2cb552 100644 --- a/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/DeleteAccountRequestActionTest.java @@ -3,7 +3,6 @@ import org.testng.annotations.Test; import teammates.common.datatransfer.attributes.AccountRequestAttributes; -import teammates.common.exception.EntityDoesNotExistException; import teammates.common.util.Const; import teammates.ui.output.MessageOutput; From a27c7acee06eac38152c07636c2e82243ac141a6 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 01:53:48 +0800 Subject: [PATCH 64/79] Fix failing test --- .../java/teammates/ui/webapi/GetAccountRequestActionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java index eff9dc685b2..f87daad58f5 100644 --- a/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java +++ b/src/test/java/teammates/ui/webapi/GetAccountRequestActionTest.java @@ -41,7 +41,7 @@ protected void testExecute() { }; EntityNotFoundException enfe = verifyEntityNotFound(nonExistParams); - assertEquals("Account request with email non-existent@email and institute non existent institute does not exist", + assertEquals("Account request for email: non-existent@email and institute: non existent institute not found.", enfe.getMessage()); ______TS("typical success case"); From 868d81cce1e417fc911b44b3f838c5cd3620d4d3 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 01:55:18 +0800 Subject: [PATCH 65/79] Remove now unnecessary comment --- src/main/java/teammates/logic/api/Logic.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/teammates/logic/api/Logic.java b/src/main/java/teammates/logic/api/Logic.java index 92c23a46223..360361fdab9 100644 --- a/src/main/java/teammates/logic/api/Logic.java +++ b/src/main/java/teammates/logic/api/Logic.java @@ -1447,7 +1447,6 @@ public void deleteAccountRequest(String email, String institute) { * * All parameters are non-null. * * @return the account request - * @throws EntityDoesNotExistException if the account request does not exist */ public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { assert registrationKey != null; From 00ec3475848682d52bf0f5be59590552a2f3da59 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 01:59:24 +0800 Subject: [PATCH 66/79] Revert unnecessary change --- .../data/InstructorCourseJoinConfirmationPageE2ETest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json b/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json index 894187cc2cb..bb025ecf35a 100644 --- a/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json +++ b/src/e2e/resources/data/InstructorCourseJoinConfirmationPageE2ETest.json @@ -26,7 +26,7 @@ "courses": { "ICJoinConf.CS1101": { "id": "tm.e2e.ICJoinConf.CS1101", - "name": "CS1102", + "name": "Programming Methodology", "institute": "TEAMMATES Test Institute 1", "timeZone": "UTC" } From 19fee860060a568243c547052ad8cf250482d059 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 02:06:05 +0800 Subject: [PATCH 67/79] Remove outdated comment --- src/main/java/teammates/logic/core/AccountRequestsLogic.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/teammates/logic/core/AccountRequestsLogic.java b/src/main/java/teammates/logic/core/AccountRequestsLogic.java index d99efa8d99b..c97d2f12eec 100644 --- a/src/main/java/teammates/logic/core/AccountRequestsLogic.java +++ b/src/main/java/teammates/logic/core/AccountRequestsLogic.java @@ -73,7 +73,6 @@ public AccountRequestAttributes getAccountRequest(String email, String institute * Gets an account request by unique constraint {@code registrationKey}. * * @return the account request - * @throws EntityDoesNotExistException if account request does not exist */ public AccountRequestAttributes getAccountRequestForRegistrationKey(String registrationKey) { return accountRequestsDb.getAccountRequestForRegistrationKey(registrationKey); From 3d9aebe948623a0ad32f9db6f98b5a670e5a293a Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 02:21:11 +0800 Subject: [PATCH 68/79] Fix minor issues --- .../teammates/ui/webapi/CreateAccountRequestAction.java | 4 ++-- .../java/teammates/storage/api/AccountRequestsDbTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index 1798973759a..322c7569218 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -24,14 +24,14 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera AccountRequestAttributes accountRequestToCreate = AccountRequestAttributes .builder(instructorEmail, instructorInstitution, instructorName) .build(); - AccountRequestAttributes accountRequestAttributes = null; + AccountRequestAttributes accountRequestAttributes; try { accountRequestAttributes = logic.createAccountRequest(accountRequestToCreate); } catch (InvalidParametersException ipe) { throw new InvalidHttpRequestBodyException(ipe); } catch (EntityAlreadyExistsException eaee) { - // Continue without creating a new account request + // Use existing account request accountRequestAttributes = logic.getAccountRequest(instructorEmail, instructorInstitution); } diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 827835a2bbd..1efc1ac4614 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -90,7 +90,7 @@ public void testUpdateAccountRequest() throws Exception { } @Test - public void testDeleteAccountRequest() throws Exception { + public void testDeleteAccountRequest() { AccountRequest accountRequest = new AccountRequest("valid2@test.com", "Test account Name", "TEAMMATES Test Institute 1"); accountRequest.setRegistrationKey("2-123456"); @@ -118,7 +118,7 @@ public void testDeleteAccountRequest() throws Exception { } @Test - public void testGetAccountRequestForRegistrationKey() throws Exception { + public void testGetAccountRequestForRegistrationKey() { AccountRequest accountRequest = new AccountRequest("valid3@test.com", "Test account Name", "TEAMMATES Test Institute 1"); accountRequest.setRegistrationKey("3-123456"); @@ -144,7 +144,7 @@ public void testGetAccountRequestForRegistrationKey() throws Exception { } @Test - public void testGetAccountRequest() throws Exception { + public void testGetAccountRequest() { AccountRequest accountRequest = new AccountRequest("valid4@test.com", "Test account Name", "TEAMMATES Test Institute 1"); accountRequest.setRegistrationKey("4-123456"); From 7f00adc6cabf7966b6d9a6d01b378374a7b5d3ec Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 23:56:42 +0800 Subject: [PATCH 69/79] Remove additional word --- src/main/java/teammates/ui/webapi/CreateAccountAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 880f3973efd..16c500e585a 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -44,7 +44,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera AccountRequestAttributes accountRequestAttributes = logic.getAccountRequestForRegistrationKey(registrationKey); if (accountRequestAttributes == null) { - throw new EntityNotFoundException("Account request could with registration key " + throw new EntityNotFoundException("Account request with registration key " + registrationKey + " could not be found"); } From 690a406d7c7097a4071b17b265e182cafbfb1b3b Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Tue, 1 Feb 2022 23:59:30 +0800 Subject: [PATCH 70/79] Replace boolean with string --- .../cases/InstructorCourseJoinConfirmationPageE2ETest.java | 4 ++-- .../datatransfer/attributes/AccountRequestAttributes.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java index f9a7dfebe44..bba7f340223 100644 --- a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java @@ -56,7 +56,7 @@ public void testAll() { ______TS("Click join link: invalid key"); joinLink = createFrontendUrl(Const.WebPageURIs.JOIN_PAGE) - .withIsCreatingAccount(String.valueOf(true)) + .withIsCreatingAccount("true") .withRegistrationKey("invalidKey") .withEntityType(Const.EntityType.INSTRUCTOR); confirmationPage = loginToPage(joinLink, CourseJoinConfirmationPage.class, "ICJoinConf.newinstr"); @@ -70,7 +70,7 @@ public void testAll() { .getRegKeyForAccountRequest("ICJoinConf.newinstr@gmail.tmt", "TEAMMATES Test Institute 1"); joinLink = createFrontendUrl(Const.WebPageURIs.JOIN_PAGE) - .withIsCreatingAccount(String.valueOf(true)) + .withIsCreatingAccount("true") .withRegistrationKey(regKey); confirmationPage = getNewPageInstance(joinLink, CourseJoinConfirmationPage.class); diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index dd3ac0375c8..125bc0e0de2 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -79,7 +79,7 @@ public Instant getCreatedAt() { public String getRegistrationUrl() { return Config.getFrontEndAppUrl(Const.WebPageURIs.JOIN_PAGE) - .withIsCreatingAccount(String.valueOf(true)) + .withIsCreatingAccount("true") .withRegistrationKey(this.getRegistrationKey()) .toAbsoluteString(); } From 99782c63f4c068ffc30873a516affc89c7c0bbf8 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 2 Feb 2022 00:12:32 +0800 Subject: [PATCH 71/79] Catch error when accountRequestAttributes is null --- .../ui/webapi/CreateAccountRequestAction.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index 322c7569218..b2f58c19720 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -1,9 +1,12 @@ package teammates.ui.webapi; +import org.apache.http.HttpStatus; + import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.exception.EntityAlreadyExistsException; import teammates.common.exception.InvalidParametersException; import teammates.common.util.EmailWrapper; +import teammates.common.util.Logger; import teammates.ui.output.JoinLinkData; import teammates.ui.request.AccountCreateRequest; import teammates.ui.request.InvalidHttpRequestBodyException; @@ -13,6 +16,8 @@ */ class CreateAccountRequestAction extends AdminOnlyAction { + private static final Logger log = Logger.getLogger(); + @Override public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOperationException { AccountCreateRequest createRequest = getAndValidateRequestBody(AccountCreateRequest.class); @@ -35,6 +40,13 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera accountRequestAttributes = logic.getAccountRequest(instructorEmail, instructorInstitution); } + if (accountRequestAttributes == null) { + String errorMessage = "Account Request for instructor with email " + instructorEmail + + " and institute " + instructorInstitution + " could not be found"; + log.severe("Unexpected error: " + errorMessage); + return new JsonResult(errorMessage, HttpStatus.SC_INTERNAL_SERVER_ERROR); + } + String joinLink = accountRequestAttributes.getRegistrationUrl(); EmailWrapper email = emailGenerator.generateNewInstructorAccountJoinEmail( From a4aea9e12100c93b933e074a4c28585c84b2c5c4 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 2 Feb 2022 00:15:33 +0800 Subject: [PATCH 72/79] Remove entityType from join link --- .../e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java index bba7f340223..b7f1d811d66 100644 --- a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java @@ -57,8 +57,7 @@ public void testAll() { ______TS("Click join link: invalid key"); joinLink = createFrontendUrl(Const.WebPageURIs.JOIN_PAGE) .withIsCreatingAccount("true") - .withRegistrationKey("invalidKey") - .withEntityType(Const.EntityType.INSTRUCTOR); + .withRegistrationKey("invalidKey"); confirmationPage = loginToPage(joinLink, CourseJoinConfirmationPage.class, "ICJoinConf.newinstr"); confirmationPage.verifyDisplayedMessage("The course join link is invalid. You may have " From 15aec0d0ab79ce14c972fb9260b90aca754ad942 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 2 Feb 2022 00:21:47 +0800 Subject: [PATCH 73/79] Use invalidKey variable --- .../e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java index b7f1d811d66..8e7122a31d4 100644 --- a/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java +++ b/src/e2e/java/teammates/e2e/cases/InstructorCourseJoinConfirmationPageE2ETest.java @@ -57,7 +57,7 @@ public void testAll() { ______TS("Click join link: invalid key"); joinLink = createFrontendUrl(Const.WebPageURIs.JOIN_PAGE) .withIsCreatingAccount("true") - .withRegistrationKey("invalidKey"); + .withRegistrationKey(invalidKey); confirmationPage = loginToPage(joinLink, CourseJoinConfirmationPage.class, "ICJoinConf.newinstr"); confirmationPage.verifyDisplayedMessage("The course join link is invalid. You may have " From d9cf69ac7b6d2bd913dfa3cb73f35cd78d46457f Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Wed, 2 Feb 2022 00:48:31 +0800 Subject: [PATCH 74/79] Remove name from update options --- .../attributes/AccountRequestAttributes.java | 16 ++++++---------- .../teammates/ui/webapi/CreateAccountAction.java | 2 +- .../logic/core/AccountRequestsLogicTest.java | 4 ++-- .../storage/api/AccountRequestsDbTest.java | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java index 125bc0e0de2..8526653cf9a 100644 --- a/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java +++ b/src/main/java/teammates/common/datatransfer/attributes/AccountRequestAttributes.java @@ -156,8 +156,8 @@ public void update(UpdateOptions updateOptions) { /** * Returns a {@link UpdateOptions.Builder} to build {@link UpdateOptions} for an account request. */ - public static UpdateOptions.Builder updateOptionsBuilder(String email, String institute, String name) { - return new UpdateOptions.Builder(email, institute, name); + public static UpdateOptions.Builder updateOptionsBuilder(String email, String institute) { + return new UpdateOptions.Builder(email, institute); } /** @@ -167,7 +167,7 @@ public static class Builder extends BasicBuilder registeredAtOption = UpdateOption.empty(); - private UpdateOptions(String email, String institute, String name) { + private UpdateOptions(String email, String institute) { assert email != null; assert institute != null; - assert name != null; this.email = email; this.institute = institute; - this.name = name; } public String getEmail() { @@ -214,7 +211,6 @@ public String toString() { return "AccountRequestAttributes.UpdateOptions [" + ", email = " + email + ", institute = " + institute - + ", name = " + name + ", registeredAt = " + registeredAtOption + "]"; } @@ -223,8 +219,8 @@ public String toString() { * Builder class to build {@link UpdateOptions}. */ public static class Builder extends BasicBuilder { - private Builder(String email, String institute, String name) { - super(new UpdateOptions(email, institute, name)); + private Builder(String email, String institute) { + super(new UpdateOptions(email, institute)); thisBuilder = this; } diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 16c500e585a..8cbdff2e261 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -71,7 +71,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera try { logic.joinCourseForInstructor(instructorList.get(0).getKey(), userInfo.id); logic.updateAccountRequest(AccountRequestAttributes - .updateOptionsBuilder(instructorEmail, instructorInstitution, instructorName) + .updateOptionsBuilder(instructorEmail, instructorInstitution) .withRegisteredAt(Instant.now()) .build()); } catch (EntityDoesNotExistException ednee) { diff --git a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java index 630f504ad29..88cfd8430b3 100644 --- a/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java +++ b/src/test/java/teammates/logic/core/AccountRequestsLogicTest.java @@ -86,7 +86,7 @@ public void testUpdateAccountRequest() throws Exception { ______TS("typical success case"); AccountRequestAttributes.UpdateOptions updateOptions = AccountRequestAttributes - .updateOptionsBuilder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") + .updateOptionsBuilder("valid@test.com", "TEAMMATES Test Institute 1") .withRegisteredAt(Const.TIME_REPRESENTS_NOW) .build(); accountRequestsLogic.updateAccountRequest(updateOptions); @@ -98,7 +98,7 @@ public void testUpdateAccountRequest() throws Exception { ______TS("failure: account request not found"); AccountRequestAttributes.UpdateOptions updateOptionsNotFound = AccountRequestAttributes - .updateOptionsBuilder("not_found@test.com", "Unknown Test Institute 1", "Unknown Name") + .updateOptionsBuilder("not_found@test.com", "Unknown Test Institute 1") .withRegisteredAt(Const.TIME_REPRESENTS_NOW) .build(); diff --git a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java index 1efc1ac4614..fd2396d218e 100644 --- a/src/test/java/teammates/storage/api/AccountRequestsDbTest.java +++ b/src/test/java/teammates/storage/api/AccountRequestsDbTest.java @@ -69,7 +69,7 @@ public void testUpdateAccountRequest() throws Exception { ______TS("typical success case"); AccountRequestAttributes.UpdateOptions updateOptions = AccountRequestAttributes - .updateOptionsBuilder("valid@test.com", "TEAMMATES Test Institute 1", "Test account Name") + .updateOptionsBuilder("valid@test.com", "TEAMMATES Test Institute 1") .withRegisteredAt(Const.TIME_REPRESENTS_NOW) .build(); accountRequestsDb.updateAccountRequest(updateOptions); @@ -81,7 +81,7 @@ public void testUpdateAccountRequest() throws Exception { ______TS("failure: account request not found"); AccountRequestAttributes.UpdateOptions updateOptionsNotFound = AccountRequestAttributes - .updateOptionsBuilder("not_found@test.com", "Unknown Test Institute 1", "Unknown Name") + .updateOptionsBuilder("not_found@test.com", "Unknown Test Institute 1") .withRegisteredAt(Const.TIME_REPRESENTS_NOW) .build(); From 7b4732e7e99d516f84d12f4112112d386c766143 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 3 Feb 2022 01:07:10 +0800 Subject: [PATCH 75/79] Handle case where accountRequestAttributes not found with assertions instead --- .../teammates/ui/webapi/CreateAccountRequestAction.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index b2f58c19720..59504a5ff27 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -40,12 +40,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera accountRequestAttributes = logic.getAccountRequest(instructorEmail, instructorInstitution); } - if (accountRequestAttributes == null) { - String errorMessage = "Account Request for instructor with email " + instructorEmail - + " and institute " + instructorInstitution + " could not be found"; - log.severe("Unexpected error: " + errorMessage); - return new JsonResult(errorMessage, HttpStatus.SC_INTERNAL_SERVER_ERROR); - } + assert accountRequestAttributes != null; String joinLink = accountRequestAttributes.getRegistrationUrl(); From a53cb8228308e81df443d2482b7b5636f70a309e Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 3 Feb 2022 01:16:33 +0800 Subject: [PATCH 76/79] Seperate try block in CreateAccountAction.java --- .../ui/webapi/CreateAccountAction.java | 25 +++++++++++++------ .../ui/webapi/CreateAccountRequestAction.java | 5 ---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 8cbdff2e261..84ff5bb4cb9 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -60,20 +60,16 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera try { courseId = importDemoData(instructorEmail, instructorName, instructorInstitution); - } catch (InvalidParametersException e) { + } catch (InvalidParametersException ipe) { // There should not be any invalid parameter here - log.severe("Unexpected error", e); - return new JsonResult(e.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); + log.severe("Unexpected error", ipe); + return new JsonResult(ipe.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } List instructorList = logic.getInstructorsForCourse(courseId); try { logic.joinCourseForInstructor(instructorList.get(0).getKey(), userInfo.id); - logic.updateAccountRequest(AccountRequestAttributes - .updateOptionsBuilder(instructorEmail, instructorInstitution) - .withRegisteredAt(Instant.now()) - .build()); } catch (EntityDoesNotExistException ednee) { throw new EntityNotFoundException(ednee); } catch (EntityAlreadyExistsException eaee) { @@ -82,6 +78,21 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera throw new InvalidHttpRequestBodyException(ipe); } + try { + logic.updateAccountRequest(AccountRequestAttributes + .updateOptionsBuilder(instructorEmail, instructorInstitution) + .withRegisteredAt(Instant.now()) + .build()); + } catch (EntityDoesNotExistException ednee) { + // Existence of account request validated before, this exception should not be thrown + log.severe("Unexpected error", ednee); + return new JsonResult(ednee.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); + } catch (InvalidParametersException ipe) { + // There should not be any invalid parameter here + log.severe("Unexpected error", ipe); + return new JsonResult(ipe.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); + } + return new JsonResult("Account successfully created", HttpStatus.SC_OK); } diff --git a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java index 59504a5ff27..fc39584bca0 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountRequestAction.java @@ -1,12 +1,9 @@ package teammates.ui.webapi; -import org.apache.http.HttpStatus; - import teammates.common.datatransfer.attributes.AccountRequestAttributes; import teammates.common.exception.EntityAlreadyExistsException; import teammates.common.exception.InvalidParametersException; import teammates.common.util.EmailWrapper; -import teammates.common.util.Logger; import teammates.ui.output.JoinLinkData; import teammates.ui.request.AccountCreateRequest; import teammates.ui.request.InvalidHttpRequestBodyException; @@ -16,8 +13,6 @@ */ class CreateAccountRequestAction extends AdminOnlyAction { - private static final Logger log = Logger.getLogger(); - @Override public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOperationException { AccountCreateRequest createRequest = getAndValidateRequestBody(AccountCreateRequest.class); From 7191591109c9ec2053d6651a3572e83fd69be745 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 3 Feb 2022 12:53:32 +0800 Subject: [PATCH 77/79] Handle cases in CreateAccountAction where exceptions should not be thrown --- .../teammates/ui/webapi/CreateAccountAction.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 84ff5bb4cb9..51631d98ef7 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -68,14 +68,22 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera List instructorList = logic.getInstructorsForCourse(courseId); + assert !instructorList.isEmpty(); + try { logic.joinCourseForInstructor(instructorList.get(0).getKey(), userInfo.id); } catch (EntityDoesNotExistException ednee) { - throw new EntityNotFoundException(ednee); + // All entities should exist in demo course, this exception should not be thrown + log.severe("Unexpected error", ednee); + return new JsonResult(ednee.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } catch (EntityAlreadyExistsException eaee) { - throw new InvalidOperationException(eaee); + // Updated entities should not have conflict with generated entities in new demo course + log.severe("Unexpected error", eaee); + return new JsonResult(eaee.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } catch (InvalidParametersException ipe) { - throw new InvalidHttpRequestBodyException(ipe); + // Both parameters should be valid + log.severe("Unexpected error", ipe); + return new JsonResult(ipe.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } try { From 6726a73376c2b44f21c94e34723be14b2849d5a0 Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 3 Feb 2022 12:56:15 +0800 Subject: [PATCH 78/79] Tweak comment language used to be consistent --- src/main/java/teammates/ui/webapi/CreateAccountAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index 51631d98ef7..f271d2be2f0 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -81,7 +81,7 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera log.severe("Unexpected error", eaee); return new JsonResult(eaee.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } catch (InvalidParametersException ipe) { - // Both parameters should be valid + // There should not be any invalid parameter here log.severe("Unexpected error", ipe); return new JsonResult(ipe.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } From 570011a8d463039b88599715bbf73985ee8ee3cb Mon Sep 17 00:00:00 2001 From: Samuel Fang Date: Thu, 3 Feb 2022 13:34:29 +0800 Subject: [PATCH 79/79] Collapse catch blocks in CreateAccountAction.java --- .../ui/webapi/CreateAccountAction.java | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/main/java/teammates/ui/webapi/CreateAccountAction.java b/src/main/java/teammates/ui/webapi/CreateAccountAction.java index f271d2be2f0..52e439dbadf 100644 --- a/src/main/java/teammates/ui/webapi/CreateAccountAction.java +++ b/src/main/java/teammates/ui/webapi/CreateAccountAction.java @@ -72,18 +72,13 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera try { logic.joinCourseForInstructor(instructorList.get(0).getKey(), userInfo.id); - } catch (EntityDoesNotExistException ednee) { - // All entities should exist in demo course, this exception should not be thrown - log.severe("Unexpected error", ednee); - return new JsonResult(ednee.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); - } catch (EntityAlreadyExistsException eaee) { - // Updated entities should not have conflict with generated entities in new demo course - log.severe("Unexpected error", eaee); - return new JsonResult(eaee.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); - } catch (InvalidParametersException ipe) { - // There should not be any invalid parameter here - log.severe("Unexpected error", ipe); - return new JsonResult(ipe.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); + } catch (EntityDoesNotExistException | EntityAlreadyExistsException | InvalidParametersException e) { + // EntityDoesNotExistException should not be thrown as all entities should exist in demo course. + // EntityAlreadyExistsException should not be thrown as updated entities should not have + // conflict with generated entities in new demo course. + // InvalidParametersException should not be thrown as as there should not be any invalid parameters. + log.severe("Unexpected error", e); + return new JsonResult(e.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } try { @@ -91,14 +86,11 @@ public JsonResult execute() throws InvalidHttpRequestBodyException, InvalidOpera .updateOptionsBuilder(instructorEmail, instructorInstitution) .withRegisteredAt(Instant.now()) .build()); - } catch (EntityDoesNotExistException ednee) { - // Existence of account request validated before, this exception should not be thrown - log.severe("Unexpected error", ednee); - return new JsonResult(ednee.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); - } catch (InvalidParametersException ipe) { - // There should not be any invalid parameter here - log.severe("Unexpected error", ipe); - return new JsonResult(ipe.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); + } catch (EntityDoesNotExistException | InvalidParametersException e) { + // EntityDoesNotExistException should not be thrown as existence of account request has been validated before. + // InvalidParametersException should not be thrown as there should not be any invalid parameters. + log.severe("Unexpected error", e); + return new JsonResult(e.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR); } return new JsonResult("Account successfully created", HttpStatus.SC_OK);