diff --git a/src/main/java/ru/oa2/lti/config/RestClientConfig.java b/src/main/java/ru/oa2/lti/config/RestClientConfig.java new file mode 100644 index 0000000..ae7062c --- /dev/null +++ b/src/main/java/ru/oa2/lti/config/RestClientConfig.java @@ -0,0 +1,25 @@ +package ru.oa2.lti.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; +import org.springframework.web.client.RestClient; + +import java.nio.charset.StandardCharsets; + +@Configuration +public class RestClientConfig { + + @Bean + public RestClient getRestClient(RestClient.Builder restClientBuilder) { + return restClientBuilder + .baseUrl("http://openolat.local") + .defaultHeader(HttpHeaders.USER_AGENT, "SpringBootApp") + .defaultStatusHandler(HttpStatusCode::isError, (req, res) -> { + throw new RuntimeException("API error: " + res.getStatusCode() + " body: " + + new String(res.getBody().readAllBytes(), StandardCharsets.UTF_8)); //TODO business exception + }) + .build(); + } +} diff --git a/src/main/java/ru/oa2/lti/controller/LoginController.java b/src/main/java/ru/oa2/lti/controller/LoginController.java index e254a9c..4eb5f3f 100644 --- a/src/main/java/ru/oa2/lti/controller/LoginController.java +++ b/src/main/java/ru/oa2/lti/controller/LoginController.java @@ -4,12 +4,14 @@ import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import ru.oa2.lti.ApplicationService; import ru.oa2.lti.model.LtiLogin; +import ru.oa2.lti.service.jwt.Payload; import ru.oa2.lti.service.jwt.TokenService; @Slf4j @@ -26,16 +28,17 @@ public class LoginController { this.tokenService = tokenService; } + @ResponseBody @PostMapping("/login") public String login(@RequestParam("client_id") String clientId, - @RequestParam("iss") String iss, - @RequestParam("login_hint") String loginHint, - @RequestParam("lti_deployment_id") String ltiDeploymentId, - @RequestParam("lti_message_hint") String ltiMessageHint, - @RequestParam("target_link_uri") String targetLinkUri, - HttpServletRequest request, - RedirectAttributes redirectAttributes, - ServletResponse servletResponse) { + @RequestParam("iss") String iss, + @RequestParam("login_hint") String loginHint, + @RequestParam("lti_deployment_id") String ltiDeploymentId, + @RequestParam("lti_message_hint") String ltiMessageHint, + @RequestParam("target_link_uri") String targetLinkUri, + HttpServletRequest request, + RedirectAttributes redirectAttributes, + ServletResponse servletResponse) { var ltiLogin = LtiLogin.builder() .clientId(clientId) @@ -49,62 +52,64 @@ public class LoginController { log.info("BODY: {}", ltiLogin); var payload = service.getPayload(ltiLogin); - var accessToken = tokenService.exchangeForAccessToken(ltiLogin.getClientId(), "http://openolat.local/lti/token"); - // 4. Сохранить access token в сессии - HttpSession session = request.getSession(); - session.setAttribute("lti_access_token", accessToken); - session.setAttribute("lti_context_id", payload.getContextId()); - session.setAttribute("lti_user_id", ltiLogin.getClientId()); + // 4. Сохранить access token в сессии + HttpSession session = request.getSession(); + session.setAttribute("lti_login", ltiLogin); + session.setAttribute("lti_message_hint", Long.valueOf(ltiMessageHint)); + session.setAttribute("lti_context_id", payload.getContextId()); + session.setAttribute("lti_user_id", ltiLogin.getClientId()); + session.setAttribute("payload", payload); - var data = service.getTask(payload.getContextId()); + if (payload.getContextId() != null) { + var body = tokenService.ltiAuth(ltiMessageHint, iss, loginHint); + return body.getBody(); + } - redirectAttributes.addFlashAttribute("description", data); - - //TODO заполнять из data - redirectAttributes.addFlashAttribute("codeTitle", "Dockerfile:"); - redirectAttributes.addFlashAttribute("inputCode", "FROM maven:3.9.9-eclipse-temurin-21-jammy AS build\n" + - "\n" + - "COPY . /build\n" + - "WORKDIR /build\n" + - "\n" + - "RUN --mount=type=cache,target=/root/.m2/repository,rw \\\n" + - "\tmvn clean package -DskipTests -B\n" + - "\n" + - "FROM eclipse-temurin:21-jdk AS extract\n" + - "\n" + - "COPY --from=build /build/target/lti-provider-*.jar app.jar\n" + - "RUN java -Djarmode=tools -jar app.jar extract --layers --destination extracted\n" + - "\n" + - "FROM eclipse-temurin:21-jre\n" + - "\n" + - "LABEL org.opencontainers.image.title=\"LTI Provider\"\n" + - "LABEL org.opencontainers.image.description=\"LTI провайдер для лабораторных по Docker и Kubernetes\"\n" + - "LABEL org.opencontainers.image.url=\"TODO\"\n" + - "LABEL org.opencontainers.image.source=\"TODO\"\n" + - "LABEL org.opencontainers.image.documentation=\"#TODO\"\n" + - "\n" + - "WORKDIR /opt\n" + - "\n" + - "COPY --from=extract extracted/dependencies/ ./\n" + - "COPY --from=extract extracted/spring-boot-loader/ ./\n" + - "COPY --from=extract extracted/snapshot-dependencies/ ./\n" + - "COPY --from=extract extracted/application/ ./\n" + - "\n" + - "ENV TZ=\"Europe/Moscow\"\n" + - "ENV JAVA_TOOL_OPTIONS=\"-Xmx1g -Xms1g\"\n" + - "\n" + - "ENTRYPOINT [\"java\", \"-jar\", \"/opt/app.jar\"]"); - - return "redirect:/tool/lti/docker-task"; + return "redirect:/tool/lti/select"; } - @GetMapping("/docker-task") - public String showDockerTas(Model model) { - if (!model.containsAttribute("description")) { - return "redirect:/error"; + @GetMapping("/task") + public String showDockerTas(Model model, + HttpServletRequest request) { + + var session = request.getSession(); + var payload = (Payload) session.getAttribute("payload"); + + if (payload != null) { + var data = service.getTask(payload.getContextId()); + + if (data == null) { + return "redirect:/error"; + } + + model.addAttribute("description", data); + model.addAttribute("codeTitle", "Dockerfile:"); + model.addAttribute("inputCode", "FROM ..."); } - return "docker-task"; + + return "task"; + } + + @GetMapping("/select") + public String select(Model model) { + return "select"; + } + + @PostMapping("/redirect") + public String redirect(@RequestParam("id_token") String idToken, + @RequestParam("state") String state, + HttpServletRequest request) { + + if (log.isDebugEnabled()) { + log.debug("/redirect idToken: {}, state: {}", idToken, state); + } + + HttpSession session = request.getSession(); + session.setAttribute("id_token", idToken); + session.setAttribute("state", state); + + return "redirect:/tool/lti/task"; } } diff --git a/src/main/java/ru/oa2/lti/controller/ResultController.java b/src/main/java/ru/oa2/lti/controller/ResultController.java index 7c5e99d..bc689bf 100644 --- a/src/main/java/ru/oa2/lti/controller/ResultController.java +++ b/src/main/java/ru/oa2/lti/controller/ResultController.java @@ -1,11 +1,22 @@ package ru.oa2.lti.controller; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.web.client.RestClientBuilderConfigurer; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestClient; import ru.oa2.lti.ApplicationService; +import ru.oa2.lti.model.LtiLogin; +import ru.oa2.lti.service.jwt.JwtService; +import ru.oa2.lti.service.jwt.TokenService; +import ru.oa2.lti.service.results.ResultService; + +import java.util.UUID; @Slf4j @RestController @@ -13,17 +24,44 @@ import ru.oa2.lti.ApplicationService; public class ResultController { final ApplicationService service; + final ResultService resultService; + final TokenService tokenService; + final JwtService jwtService; - public ResultController(ApplicationService applicationService) { + public ResultController(ApplicationService applicationService, + ResultService resultService, + TokenService tokenService, + JwtService jwtService) { this.service = applicationService; + this.resultService = resultService; + this.tokenService = tokenService; + this.jwtService = jwtService; } @PostMapping("/docker") - public String result(@RequestBody String body) { + public ResponseEntity result(@RequestBody String body, + HttpServletRequest req) { log.info("RESULT: {}", body); + HttpSession session = req.getSession(false); + if (session == null) return ResponseEntity.status(401).build(); + + var ltiLogin = (LtiLogin) session.getAttribute("lti_login"); + var assessToken = tokenService.exchangeForAccessToken(ltiLogin.getClientId(), "http://openolat.local/lti/token"); + + String userId = (String) session.getAttribute("lti_user_id"); + UUID ltiContextId = (UUID) session.getAttribute("lti_context_id"); + String idToken = (String) session.getAttribute("id_token"); + + resultService.setResult( + assessToken, + jwtService.getTokenPayload(idToken) + ); + //TODO возвращать json - return service.saveResult(body); + return ResponseEntity + .accepted() + .body(service.saveResult(body)); } } diff --git a/src/main/java/ru/oa2/lti/service/jwt/IdTokenPayload.java b/src/main/java/ru/oa2/lti/service/jwt/IdTokenPayload.java new file mode 100644 index 0000000..598527c --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/jwt/IdTokenPayload.java @@ -0,0 +1,27 @@ +package ru.oa2.lti.service.jwt; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +@Data +public class IdTokenPayload { + + LocalDateTime iat; + LocalDateTime exp; + String iss; + UUID aud; + UUID sub; + String messageType; + ResourceLink resourceLink; + List roles; + TokenContext context; + NamesRoleService namesRoleService; + LaunchPresentation launchPresentation; + TokenEndpoint tokenEndpoint; + String version; + UUID deploymentId; + String targetLinkUri; +} diff --git a/src/main/java/ru/oa2/lti/service/jwt/JwtService.java b/src/main/java/ru/oa2/lti/service/jwt/JwtService.java index 97e4751..2ceae25 100644 --- a/src/main/java/ru/oa2/lti/service/jwt/JwtService.java +++ b/src/main/java/ru/oa2/lti/service/jwt/JwtService.java @@ -3,4 +3,6 @@ package ru.oa2.lti.service.jwt; public interface JwtService { Payload getPayload(String jwt); + + IdTokenPayload getTokenPayload(String jwt); } diff --git a/src/main/java/ru/oa2/lti/service/jwt/JwtServiceImpl.java b/src/main/java/ru/oa2/lti/service/jwt/JwtServiceImpl.java index 3de1886..ba3a9c5 100644 --- a/src/main/java/ru/oa2/lti/service/jwt/JwtServiceImpl.java +++ b/src/main/java/ru/oa2/lti/service/jwt/JwtServiceImpl.java @@ -1,8 +1,14 @@ package ru.oa2.lti.service.jwt; +import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.stereotype.Service; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; @Service @@ -18,14 +24,89 @@ public class JwtServiceImpl implements JwtService { public Payload getPayload(String token) { var jwt = jwtDecoder.decode(token); - return Payload.builder() - .deploymentKey(jwt.getClaim("deploymentKey")) - .deploymentId(UUID.fromString(jwt.getClaim("deploymentId"))) - .contextKey(jwt.getClaim("contextKey")) - .contextId(UUID.fromString(jwt.getClaim("contextId"))) - .courseadmin(jwt.getClaim("courseadmin")) - .coach(jwt.getClaim("coach")) - .participant(jwt.getClaim("participant")) - .build(); + var payload = new Payload(); + for (var key : jwt.getClaims().entrySet()) { + switch (key.getKey()) { + case "deploymentKey" -> payload.setDeploymentKey((Long) key.getValue()); + case "deploymentId" -> payload.setDeploymentId(UUID.fromString((String) key.getValue())); + case "contextKey" -> payload.setContextKey((Long) key.getValue()); + case "contextId" -> payload.setContextId(UUID.fromString((String) key.getValue())); + case "courseadmin" -> payload.setCourseadmin((Boolean) key.getValue()); + case "coach" -> payload.setCoach((Boolean) key.getValue()); + case "participant" -> payload.setParticipant((Boolean) key.getValue()); + } + } + + return payload; + } + + @Override + public IdTokenPayload getTokenPayload(String idToken) { + var jwt = jwtDecoder.decode(idToken); + + var payload = new IdTokenPayload(); + for (var el : jwt.getClaims().entrySet()) { + switch (el.getKey()) { + case "iat" -> payload.setIat(LocalDateTime.ofInstant((Instant) el.getValue(), ZoneId.systemDefault())); + case "exp" -> payload.setExp(LocalDateTime.ofInstant((Instant) el.getValue(), ZoneId.systemDefault())); + case "iss" -> payload.setIss((String) el.getValue()); + case "aud" -> payload.setAud(UUID.fromString((String) ((ArrayList) el.getValue()).get(0))); + case "sub" -> payload.setSub(UUID.fromString((String) el.getValue())); + case "https://purl.imsglobal.org/spec/lti/claim/message_type" + -> payload.setMessageType((String) el.getValue()); + case "https://purl.imsglobal.org/spec/lti/claim/resource_link" + -> { + var m = (LinkedTreeMap) el.getValue(); + payload.setResourceLink( + new ResourceLink(UUID.fromString((String) m.get("id")), (String) m.get("title")) + ); + } + case "https://purl.imsglobal.org/spec/lti/claim/roles" -> + payload.setRoles((List) el.getValue()); + case "https://purl.imsglobal.org/spec/lti/claim/context" -> { + var tc = (LinkedTreeMap) el.getValue(); + payload.setContext( + new TokenContext( + UUID.fromString((String) tc.get("id")), + (String) tc.get("label"), + (String) tc.get("title"), + (List) tc.get("type") + ) + ); + } + case "https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice" -> { + var nrs = (LinkedTreeMap) el.getValue(); + payload.setNamesRoleService( + new NamesRoleService( + (String) nrs.get("context_memberships_url"), + (List) nrs.get("service_versions") + ) + ); + } + case "https://purl.imsglobal.org/spec/lti/claim/launch_presentation" -> { + payload.setLaunchPresentation(new LaunchPresentation( + (String) ((LinkedTreeMap) el.getValue()).get("document_target") + )); + } + case "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint" -> { + var endpoint = (LinkedTreeMap) el.getValue(); + payload.setTokenEndpoint( + new TokenEndpoint( + (List) endpoint.get("scope"), + (String) endpoint.get("lineitems"), + (String) endpoint.get("lineitem") + ) + ); + } + case "https://purl.imsglobal.org/spec/lti/claim/version" -> + payload.setVersion((String) el.getValue()); + case "https://purl.imsglobal.org/spec/lti/claim/deployment_id" -> + payload.setDeploymentId(UUID.fromString((String) el.getValue())); + case "https://purl.imsglobal.org/spec/lti/claim/target_link_uri" -> + payload.setTargetLinkUri((String) el.getValue()); + } + } + + return payload; } } diff --git a/src/main/java/ru/oa2/lti/service/jwt/LaunchPresentation.java b/src/main/java/ru/oa2/lti/service/jwt/LaunchPresentation.java new file mode 100644 index 0000000..cf75349 --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/jwt/LaunchPresentation.java @@ -0,0 +1,11 @@ +package ru.oa2.lti.service.jwt; + +/* + "https://purl.imsglobal.org/spec/lti/claim/launch_presentation" : { + "document_target" : "iframe" + }, + */ +public record LaunchPresentation( + String documentTarget +) { +} diff --git a/src/main/java/ru/oa2/lti/service/jwt/NamesRoleService.java b/src/main/java/ru/oa2/lti/service/jwt/NamesRoleService.java new file mode 100644 index 0000000..167fa3d --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/jwt/NamesRoleService.java @@ -0,0 +1,15 @@ +package ru.oa2.lti.service.jwt; + +import java.util.List; + +/* + "https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice" : { + "context_memberships_url" : "http://openolat.local/lti/nrps/b0d12061-2564-47a0-9291-b0d226f1eefe/memberships/", + "service_versions" : [ "2.0" ] + }, + */ +public record NamesRoleService( + String contextMembershipsUrl, + ListserviceVersions +) { +} diff --git a/src/main/java/ru/oa2/lti/service/jwt/Payload.java b/src/main/java/ru/oa2/lti/service/jwt/Payload.java index 78cc27c..f50840e 100644 --- a/src/main/java/ru/oa2/lti/service/jwt/Payload.java +++ b/src/main/java/ru/oa2/lti/service/jwt/Payload.java @@ -1,12 +1,12 @@ package ru.oa2.lti.service.jwt; -import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; import java.util.UUID; @Data -@Builder +@NoArgsConstructor public class Payload { private Long deploymentKey; private UUID deploymentId; diff --git a/src/main/java/ru/oa2/lti/service/jwt/ResourceLink.java b/src/main/java/ru/oa2/lti/service/jwt/ResourceLink.java new file mode 100644 index 0000000..788372e --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/jwt/ResourceLink.java @@ -0,0 +1,9 @@ +package ru.oa2.lti.service.jwt; + +import java.util.UUID; + +public record ResourceLink( + UUID id, + String title +) { +} diff --git a/src/main/java/ru/oa2/lti/service/jwt/TokenContext.java b/src/main/java/ru/oa2/lti/service/jwt/TokenContext.java new file mode 100644 index 0000000..19d0431 --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/jwt/TokenContext.java @@ -0,0 +1,13 @@ +package ru.oa2.lti.service.jwt; + +import java.util.List; +import java.util.UUID; + + +public record TokenContext( + UUID id, + String label, + String title, + List type +) { +} diff --git a/src/main/java/ru/oa2/lti/service/jwt/TokenEndpoint.java b/src/main/java/ru/oa2/lti/service/jwt/TokenEndpoint.java new file mode 100644 index 0000000..39c3788 --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/jwt/TokenEndpoint.java @@ -0,0 +1,17 @@ +package ru.oa2.lti.service.jwt; + +import java.util.List; + +/* + "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint" : { + "scope" : [ "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem", "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly", "https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly", "https://purl.imsglobal.org/spec/lti-ags/scope/score" ], + "lineitems" : "http://openolat.local/lti/ags/b0d12061-2564-47a0-9291-b0d226f1eefe/context/112999233990970/lineitems/", + "lineitem" : "http://openolat.local/lti/ags/b0d12061-2564-47a0-9291-b0d226f1eefe/context/112999233990970/lineitems/112999233990979/lineitem" + }, + */ +public record TokenEndpoint( + List scope, + String lineitems, + String lineitem +) { +} diff --git a/src/main/java/ru/oa2/lti/service/jwt/TokenService.java b/src/main/java/ru/oa2/lti/service/jwt/TokenService.java index a23991c..21ca6d8 100644 --- a/src/main/java/ru/oa2/lti/service/jwt/TokenService.java +++ b/src/main/java/ru/oa2/lti/service/jwt/TokenService.java @@ -1,17 +1,25 @@ package ru.oa2.lti.service.jwt; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClient; import org.springframework.web.client.RestTemplate; import java.util.Map; +@Slf4j @Service public class TokenService { private final RestTemplate restTemplate = new RestTemplate(); + private final RestClient restClient; + + public TokenService(RestClient restClient) { + this.restClient = restClient; + } // Вызывается после LTI-запуска public String exchangeForAccessToken( @@ -31,7 +39,7 @@ public class TokenService { body.add("grant_type", "client_credentials"); body.add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); body.add("client_assertion", clientAssertion); - body.add("scope", "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem https://purl.imsglobal.org/spec/lti-ags/scope/result"); + body.add("scope", "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem https://purl.imsglobal.org/spec/lti-ags/scope/scope https://purl.imsglobal.org/spec/lti-ags/claim/endpoint"); // Заголовки HttpHeaders headers = new HttpHeaders(); @@ -53,4 +61,24 @@ public class TokenService { throw new RuntimeException("Не удалось получить access token", e); } } + + public ResponseEntity ltiAuth(String ltiLoginHint, String iss, String loginHint) { + var result = restClient + .get() + .uri("/lti/auth?" + + "state=start" + + "&code=code1" + + "&iss=" + iss + + "&login_hint=" + loginHint + + "&redirect_uri=http://openolat.local/tool/lti/redirect" + + "<i_message_hint=" + ltiLoginHint) + .retrieve(); + + ResponseEntity body = result.toEntity(String.class); + if (log.isDebugEnabled()) { + log.debug("lti/auth RESPONSE: {}", body); + } + return body; + } + } \ No newline at end of file diff --git a/src/main/java/ru/oa2/lti/service/results/Lineitems.java b/src/main/java/ru/oa2/lti/service/results/Lineitems.java new file mode 100644 index 0000000..b99c310 --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/results/Lineitems.java @@ -0,0 +1,8 @@ +package ru.oa2.lti.service.results; + +public record Lineitems( + String id, + String label, + String resourceId +) { +} diff --git a/src/main/java/ru/oa2/lti/service/results/ResultService.java b/src/main/java/ru/oa2/lti/service/results/ResultService.java new file mode 100644 index 0000000..d37a840 --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/results/ResultService.java @@ -0,0 +1,10 @@ +package ru.oa2.lti.service.results; + +import ru.oa2.lti.service.jwt.IdTokenPayload; + +import java.util.UUID; + +public interface ResultService { + + void setResult(String accessToken, IdTokenPayload idToken); +} diff --git a/src/main/java/ru/oa2/lti/service/results/ResultServiceImpl.java b/src/main/java/ru/oa2/lti/service/results/ResultServiceImpl.java new file mode 100644 index 0000000..b206d1a --- /dev/null +++ b/src/main/java/ru/oa2/lti/service/results/ResultServiceImpl.java @@ -0,0 +1,65 @@ +package ru.oa2.lti.service.results; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; +import ru.oa2.lti.service.jwt.IdTokenPayload; + +import java.util.UUID; + +@Slf4j +@Service +public class ResultServiceImpl implements ResultService { + + private final RestClient restClient; + private final ObjectMapper mapper; + + public ResultServiceImpl(RestClient client) { + this.restClient = client; + this.mapper = new ObjectMapper(); + } + + @Override + public void setResult(String accessToken, IdTokenPayload idToken) { + + // http://openolat.local/lti/ags/b0d12061-2564-47a0-9291-b0d226f1eefe/context/112999233990970/lineitems/112999233990979/lineitem + + + var resp = restClient.get() + .uri("http://openolat.local/lti/ags/b0d12061-2564-47a0-9291-b0d226f1eefe/context/112999233990970/lineitems/112999233990979/lineitem") + .header("Authorization", "Bearer " + accessToken) + .header("Content-Type", "application/vnd.ims.lis.v1.score+json") + .header("Accept", "application/vnd.ims.lis.v1.score+json") + .retrieve(); + + var respBody = resp.toEntity(String.class).getBody(); + try { + Lineitems lineitems = mapper.readValue(respBody, Lineitems.class); + + String resourceId = "112999233990970"; //TODO + String userId = "efc0b988-cfe0-4d00-9466-cf86fcf8f885"; //TODO + + var result = restClient + .post() + .uri(String.format("/lti/ags/%s/context/%s/lineitems/laba/lineitem/scores/x", + idToken.getContext().id(), resourceId)) + .header("Authorization", "Bearer " + accessToken) + .header("Content-Type", "application/vnd.ims.lis.v1.score+json") + .header("Accept", "application/vnd.ims.lis.v1.score+json") + .body( + String.format( + "{\"id\":\"%s\", \"userId\": \"%s\", \"scoreMaximum\": 1, \"label\":\"LTI page 1\", \"resourceId\": \"%s\"}", + lineitems.id(), userId, lineitems.resourceId() + ) + ) + .retrieve(); + log.info("RESULT RESP: {}", result.toEntity(String.class)); + } catch (Exception e) { + log.error(e.getMessage()); + } + + + + } +} diff --git a/src/main/resources/templates/select.html b/src/main/resources/templates/select.html new file mode 100644 index 0000000..cf4e40f --- /dev/null +++ b/src/main/resources/templates/select.html @@ -0,0 +1,6 @@ + + + +

(.)_(.)

+ + \ No newline at end of file diff --git a/src/main/resources/templates/docker-task.html b/src/main/resources/templates/task.html similarity index 97% rename from src/main/resources/templates/docker-task.html rename to src/main/resources/templates/task.html index 251f670..8b6c0ce 100644 --- a/src/main/resources/templates/docker-task.html +++ b/src/main/resources/templates/task.html @@ -125,9 +125,6 @@ - - -