Skip to content

Commit

Permalink
Adding API to support linking runs to CK projects
Browse files Browse the repository at this point in the history
  • Loading branch information
vkorir authored and hirokiterashima committed May 21, 2023
1 parent 7b17425 commit b9d8ce3
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 40 deletions.
4 changes: 4 additions & 0 deletions src/main/java/org/wise/portal/domain/run/Run.java
Original file line number Diff line number Diff line change
Expand Up @@ -397,4 +397,8 @@ public interface Run extends Persistable {
boolean isLockedAfterEndDate();

void setLockedAfterEndDate(boolean isLockedAfterEndDate);

String getConnectCode();

void setConnectCode(String connectCode);
}
8 changes: 8 additions & 0 deletions src/main/java/org/wise/portal/domain/run/impl/RunImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ public class RunImpl implements Run {
@Transient
private static final String COLUMN_NAME_IS_LOCKED_AFTER_END_DATE = "isLockedAfterEndDate";

@Transient
private static final String COLUMN_NAME_CK_PROJECT_CODE = "ckProjectCode";

@Id
@Getter
@Setter
Expand Down Expand Up @@ -247,6 +250,11 @@ public class RunImpl implements Run {
@Setter
private boolean isRandomPeriodAssignment = false;

@Column(name = RunImpl.COLUMN_NAME_CK_PROJECT_CODE)
@Getter
@Setter
private String connectCode;

public Group getPeriodByName(String periodName) throws PeriodNotFoundException {
Set<Group> periods = getPeriods();
for (Group period : periods) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,27 @@
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
Expand Down Expand Up @@ -216,6 +224,7 @@ public static JSONObject getRunJSON(Run run) throws JSONException {
runJSON.put("runCode", run.getRuncode());
runJSON.put("startTime", run.getStartTimeMilliseconds());
runJSON.put("endTime", run.getEndTimeMilliseconds());
runJSON.put("code", run.getConnectCode());
Set<Group> periods = run.getPeriods();
JSONArray periodsArray = new JSONArray();
for (Group period : periods) {
Expand Down Expand Up @@ -502,4 +511,56 @@ public static JSONObject createErrorResponse(String messageCode) {
}
return response;
}

/**
* Do a POST request to CK Backend for:
* 1. Linking run to CK Board project.
* 2. Unlinking run from CK Board project.
* 3. Adding members of a run to CK Board project.
*/
public static String doCkBoardPost(HttpServletRequest request, Authentication authentication,
String params, String url) {
try {
String ckSessionCookie = getCkSessionCookie(request);
if (ckSessionCookie != null) {
HttpClient client = HttpClientBuilder.create().build();
HttpPost ckBoardLinkReq = new HttpPost(getCkBoardUrl(url));
ckBoardLinkReq.addHeader("content-type", "application/json");
ckBoardLinkReq.setHeader("Authorization", "Bearer " + ckSessionCookie);
ckBoardLinkReq.setEntity(new StringEntity(params, ContentType.APPLICATION_JSON));
HttpEntity response = client.execute(ckBoardLinkReq).getEntity();
return EntityUtils.toString(response);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

public static String getCkBoardUrl(String apiEndpoint) {
String ckBoardUrl = appProperties.getProperty("ck_board_url");

// The CK Board local backend url is only used for local development and should only be set in
// local development environments. When we are running locally, we need the local IP and port of
// the CK Board backend because the SCORE API is served using Docker. If the SCORE API makes a
// request to localhost:8001, it won't be able to access the CK Board backend. This is because
// the SCORE API expects localhost to be within the container but the CK Board backend is not in
// the container.
String ckBoardLocalBackendUrl = appProperties.getProperty("ck_board_local_backend_url");
if (ckBoardLocalBackendUrl != null && !ckBoardLocalBackendUrl.equals("")) {
ckBoardUrl = ckBoardLocalBackendUrl;
}
return ckBoardUrl + apiEndpoint;
}

public static String getCkSessionCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals("CK_SESSION")) {
return cookie.getValue();
}
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

import org.apache.commons.lang3.RandomStringUtils;
import org.hibernate.StaleObjectStateException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -263,7 +264,7 @@ private Set<User> getUsers(String[] userIds) throws ObjectNotFoundException {
* return a map containing an error field with an error string.
*/
@PostMapping("/run/register")
HashMap<String, Object> addStudentToRun(Authentication auth,
HashMap<String, Object> addStudentToRun(HttpServletRequest request, Authentication auth,
@RequestParam("runCode") String runCode, @RequestParam("period") String period) {
User user = userService.retrieveUserByUsername(auth.getName());
Run run = getRun(runCode);
Expand All @@ -278,6 +279,19 @@ HashMap<String, Object> addStudentToRun(Authentication auth,
while (currentLoopIndex < maxLoop) {
try {
studentService.addStudentToRun(user, projectCode);
String connectCode = run.getConnectCode();
if (connectCode != null && !connectCode.equals("")) {
try {
String url = "/api/projects/score/addMember";
JSONObject params = new JSONObject();
params.put("username", user.getUserDetails().getUsername());
params.put("role", "student");
params.put("code", connectCode);
ControllerUtil.doCkBoardPost(request, auth, params.toString(), url);
} catch (Exception e) {
e.printStackTrace();
}
}
HashMap<String, Object> runMap = getRunMap(user, run);
return runMap;
} catch (ObjectNotFoundException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.RandomStringUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -62,6 +63,12 @@ public class TeacherAPIController extends UserAPIController {
@Value("${google.clientSecret:}")
private String googleClientSecret;

@Value("${ck_board_url}")
private String ckBoardUrl;

@Value("${ck_board_local_backend_url}")
private String ckBoardBackendUrl;

@GetMapping("/runs")
List<HashMap<String, Object>> getRuns(Authentication auth,
@RequestParam(required = false) Integer max) {
Expand Down Expand Up @@ -469,4 +476,64 @@ HashMap<String, Object> editRunIsLockedAfterEndDate(Authentication authenticatio
}
return response;
}

@PostMapping("/run/score/link")
Map<String, Object> linkRunToCkProject(HttpServletRequest request, Authentication authentication,
@RequestParam("runId") Long runId, @RequestParam("code") String code) {
Map<String, Object> result = new HashMap<>();
try {
User user = userService.retrieveUserByUsername(authentication.getName());
Run run = runService.retrieveById(runId);
if (run.isTeacherAssociatedToThisRun(user)) {
String url = "/api/projects/score/link";
JSONObject params = new JSONObject();
params.put("runId", runId);
params.put("code", code);
String jsonRes = ControllerUtil.doCkBoardPost(request, authentication, params.toString(),
url);
JSONObject json = new JSONObject(jsonRes);
if (json.has("code") && json.getString("code").equals(code)) {
runService.updateCkProjectConnectCode(runId, code);
result.put("code", code);
}
if (json.has("message")) {
result.put("message", json.getString("message"));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}

@PostMapping("/run/score/unlink")
Map<String, Object> ulinkRunFromCkProject(HttpServletRequest request,
Authentication authentication, @RequestParam("runId") Long runId,
@RequestParam("code") String code) {
Map<String, Object> result = new HashMap<>();
try {
User user = userService.retrieveUserByUsername(authentication.getName());
Run run = runService.retrieveById(runId);
if (run.isTeacherAssociatedToThisRun(user)) {
String url = "/api/projects/score/unlink";
JSONObject params = new JSONObject();
params.put("runId", runId);
params.put("code", code);
String jsonRes = ControllerUtil.doCkBoardPost(request, authentication, params.toString(),
url);
JSONObject json = new JSONObject(jsonRes);
if (json.has("code") && json.getString("code").equals(code)) {
runService.updateCkProjectConnectCode(runId, "");
result.put("code", code);
}
if (json.has("message")) {
result.put("message", json.getString("message"));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package org.wise.portal.presentation.web.controllers.teacher;

import javax.servlet.http.HttpServletRequest;

import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import org.wise.portal.dao.ObjectNotFoundException;
import org.wise.portal.domain.run.Run;
import org.wise.portal.domain.user.User;
import org.wise.portal.presentation.web.controllers.ControllerUtil;
import org.wise.portal.presentation.web.exception.TeacherAlreadySharedWithRunException;
import org.wise.portal.presentation.web.response.SharedOwner;
import org.wise.portal.presentation.web.response.SimpleResponse;
import org.wise.portal.service.run.RunService;
import org.wise.portal.service.user.UserService;

/**
* REST API endpoints for Teacher Permissions
Expand All @@ -21,11 +30,29 @@ public class TeacherRunPermissionsAPIController {
@Autowired
private RunService runService;

@Autowired
private UserService userService;

@RequestMapping(value = "/{runId}/{teacherUsername}", method = RequestMethod.PUT)
protected SharedOwner addSharedOwner(@PathVariable Long runId,
@PathVariable String teacherUsername) {
protected SharedOwner addSharedOwner(HttpServletRequest request, Authentication auth,
@PathVariable Long runId, @PathVariable String teacherUsername) {
try {
return runService.addSharedTeacher(runId, teacherUsername);
SharedOwner sharedOwner = runService.addSharedTeacher(runId, teacherUsername);
Run run = runService.retrieveById(runId);
String connectCode = run.getConnectCode();
if (connectCode != null && !connectCode.equals("")) {
try {
String url = "/api/projects/score/addMember";
JSONObject params = new JSONObject();
params.put("username", teacherUsername);
params.put("role", "teacher");
params.put("code", connectCode);
ControllerUtil.doCkBoardPost(request, auth, params.toString(), url);
} catch (Exception e) {
e.printStackTrace();
}
}
return sharedOwner;
} catch (ObjectNotFoundException e) {
return null;
} catch (TeacherAlreadySharedWithRunException e) {
Expand All @@ -34,20 +61,49 @@ protected SharedOwner addSharedOwner(@PathVariable Long runId,
}

@PutMapping("/transfer/{runId}/{teacherUsername}")
protected String transferRunOwnership(@PathVariable Long runId,
@PathVariable String teacherUsername) {
protected String transferRunOwnership(HttpServletRequest request, Authentication auth,
@PathVariable Long runId, @PathVariable String teacherUsername) {
try {
return runService.transferRunOwnership(runId, teacherUsername).toString();
JSONObject result = runService.transferRunOwnership(runId, teacherUsername);
if (result != null) {
try {
String connectCode = result.getString("code");
if (connectCode != null && !connectCode.equals("")) {
String url = "/api/projects/score/addMember";
JSONObject params = new JSONObject();
params.put("username", teacherUsername);
params.put("role", "teacher");
params.put("code", connectCode);
ControllerUtil.doCkBoardPost(request, auth, params.toString(), url);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return result.toString();
} catch (ObjectNotFoundException e) {
return null;
}
}

@RequestMapping(value = "/{runId}/{username}", method = RequestMethod.DELETE)
protected SimpleResponse removeSharedOwner(@PathVariable Long runId,
@PathVariable String username) {
protected SimpleResponse removeSharedOwner(HttpServletRequest request, Authentication auth,
@PathVariable Long runId, @PathVariable String username) {
try {
runService.removeSharedTeacher(username, runId);
Run run = runService.retrieveById(runId);
String connectCode = run.getConnectCode();
if (connectCode != null && !connectCode.equals("")) {
try {
String url = "/api/projects/score/removeMember";
JSONObject params = new JSONObject();
params.put("username", username);
params.put("code", connectCode);
ControllerUtil.doCkBoardPost(request, auth, params.toString(), url);
} catch (JSONException e) {
e.printStackTrace();
}
}
return new SimpleResponse("success", "successfully removed shared owner");
} catch (ObjectNotFoundException e) {
return new SimpleResponse("error", "user or run was not found");
Expand Down
Loading

0 comments on commit b9d8ce3

Please sign in to comment.