рефакторинг кода
This commit is contained in:
parent
23ea2ed09f
commit
6f42fe0ee6
|
|
@ -1,68 +0,0 @@
|
|||
package ru.oa2.lti;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.oa2.lti.infrastructure.runner.Runner;
|
||||
import ru.oa2.lti.model.LtiLogin;
|
||||
import ru.oa2.lti.model.TaskData;
|
||||
import ru.oa2.lti.repository.LMSContentRepository;
|
||||
import ru.oa2.lti.repository.entities.LMSContent;
|
||||
import ru.oa2.lti.repository.entities.Task;
|
||||
import ru.oa2.lti.service.jwt.JwtService;
|
||||
import ru.oa2.lti.service.jwt.Payload;
|
||||
import ru.oa2.lti.service.results.dto.ResultResponse;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class ApplicationService {
|
||||
|
||||
private final JwtService jwtService;
|
||||
private final LMSContentRepository lmsContentRepository;
|
||||
private final Runner runner;
|
||||
|
||||
public ApplicationService(JwtService jwtService,
|
||||
LMSContentRepository lmsContentRepository,
|
||||
Runner runner) {
|
||||
this.jwtService = jwtService;
|
||||
this.lmsContentRepository = lmsContentRepository;
|
||||
this.runner = runner;
|
||||
}
|
||||
|
||||
public Payload getPayload(LtiLogin ltiLogin) {
|
||||
return jwtService.getPayload(ltiLogin.getLoginHint());
|
||||
}
|
||||
|
||||
public String getTask(UUID contextId) {
|
||||
|
||||
var content = lmsContentRepository.getLMSContentByContentId(contextId);
|
||||
|
||||
if(content.isPresent()) {
|
||||
LMSContent lmsContent = content.get();
|
||||
Collection<Task> tasks = lmsContent.getTasks();
|
||||
|
||||
//TODO добавить версию в Task и выбирать самую старшую и опубликованную
|
||||
TaskData data = tasks.stream().findFirst().get().getData();
|
||||
|
||||
if (!runner.run(contextId, data.getInitScript())) {
|
||||
return "Init script FAILED";
|
||||
}
|
||||
|
||||
return data.getDescription();
|
||||
} else {
|
||||
return "Not Page";
|
||||
}
|
||||
}
|
||||
|
||||
public ResultResponse updateTask(String body) {
|
||||
return new ResultResponse("success");
|
||||
}
|
||||
|
||||
public ResultResponse saveResult(String body) {
|
||||
|
||||
//TODO заполнять из результатов проверок check скрипта
|
||||
return new ResultResponse("success");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.config;
|
||||
package ru.oa2.lti.application.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.config;
|
||||
package ru.oa2.lti.application.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
|
|
@ -1,30 +1,30 @@
|
|||
package ru.oa2.lti.controller;
|
||||
package ru.oa2.lti.application.controller;
|
||||
|
||||
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 ru.oa2.lti.ApplicationService;
|
||||
import ru.oa2.lti.infrastructure.LMSService;
|
||||
import ru.oa2.lti.model.LtiLogin;
|
||||
import ru.oa2.lti.service.jwt.Payload;
|
||||
import ru.oa2.lti.application.infrastructure.lms.LMSService;
|
||||
import ru.oa2.lti.application.service.jwt.JwtService;
|
||||
import ru.oa2.lti.domain.model.auth.LtiLogin;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
/*
|
||||
Входная точка в external tool по протоколу LTI 1.3
|
||||
|
||||
#TODO расписать процесс и добавить линк на доку
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/tool/lti")
|
||||
public class LaunchController {
|
||||
|
||||
private final ApplicationService service;
|
||||
private final JwtService jwtService;
|
||||
private final LMSService lmsService;
|
||||
|
||||
public LaunchController(ApplicationService applicationService,
|
||||
public LaunchController(JwtService jwtService,
|
||||
LMSService lmsService) {
|
||||
this.service = applicationService;
|
||||
this.jwtService = jwtService;
|
||||
this.lmsService = lmsService;
|
||||
}
|
||||
|
||||
|
|
@ -47,9 +47,7 @@ public class LaunchController {
|
|||
.targetLinkUri(targetLinkUri)
|
||||
.build();
|
||||
|
||||
log.info("BODY: {}", ltiLogin);
|
||||
|
||||
var payload = service.getPayload(ltiLogin);
|
||||
var payload = jwtService.getPayload(ltiLogin.getLoginHint());
|
||||
|
||||
// 4. Сохранить access token в сессии
|
||||
HttpSession session = request.getSession();
|
||||
|
|
@ -66,37 +64,9 @@ public class LaunchController {
|
|||
return "redirect:/tool/lti/select";
|
||||
}
|
||||
|
||||
@PostMapping("/task")
|
||||
public ResponseEntity updateTask() {
|
||||
|
||||
return service.updateTask("TODO");
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
if (Objects.requireNonNull(payload).getCoach()) {
|
||||
return "task-editor";
|
||||
}
|
||||
return "task";
|
||||
}
|
||||
|
||||
@GetMapping("/select")
|
||||
public String select(Model model) {
|
||||
//TODO доработать форму выбора существующих заданий
|
||||
return "select";
|
||||
}
|
||||
|
||||
|
|
@ -105,10 +75,6 @@ public class LaunchController {
|
|||
@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);
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package ru.oa2.lti.application.controller;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import ru.oa2.lti.domain.model.auth.Payload;
|
||||
import ru.oa2.lti.application.service.task.TaskService;
|
||||
import ru.oa2.lti.domain.model.auth.LtiLogin;
|
||||
import ru.oa2.lti.domain.model.ResultRequest;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/*
|
||||
Контроллер работы с задачами(лабораторными работами)
|
||||
|
||||
GET - возвращает контекст лабораторной работы
|
||||
POST - обновление/создание лабораторной работы (только для преподавателей/администраторов)
|
||||
POST /submit - отправка на автоматическую проверку
|
||||
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/tool/lti/task")
|
||||
public class TaskController {
|
||||
|
||||
private final TaskService taskService;
|
||||
|
||||
public TaskController(TaskService taskService) {
|
||||
this.taskService = taskService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String showDockerTas(Model model,
|
||||
HttpServletRequest request) {
|
||||
|
||||
var session = request.getSession();
|
||||
var payload = (Payload) session.getAttribute("payload");
|
||||
|
||||
if (payload != null) {
|
||||
var data = taskService.getTask(payload.getContextId());
|
||||
|
||||
if (data == null) {
|
||||
return "redirect:/error";
|
||||
}
|
||||
|
||||
model.addAttribute("description", data);
|
||||
}
|
||||
|
||||
if (Objects.requireNonNull(payload).getCoach()) {
|
||||
return "task-editor";
|
||||
}
|
||||
return "task";
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity updateTask() {
|
||||
|
||||
//TODO exception
|
||||
taskService.saveTask("TODO", "TODO", "TODO");
|
||||
return ResponseEntity.accepted().build();
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
public ResponseEntity result(HttpServletRequest req) {
|
||||
|
||||
HttpSession session = req.getSession(false);
|
||||
if (session == null) return ResponseEntity.status(401).build();
|
||||
|
||||
var ltiLogin = (LtiLogin) session.getAttribute("lti_login");
|
||||
var idToken = (String) session.getAttribute("id_token");
|
||||
|
||||
return ResponseEntity
|
||||
.accepted()
|
||||
.body(taskService.checkTask(new ResultRequest(ltiLogin.getClientId(), idToken)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package ru.oa2.lti.application.domain.carecase;
|
||||
|
||||
public class GetTask {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.oa2.lti.application.infrastructure.lms;
|
||||
|
||||
public interface LMSService {
|
||||
|
||||
String ltiAuth(String ltiLoginHint, String iss, String loginHint);
|
||||
|
||||
// Вызывается после LTI-запуска
|
||||
String exchangeForAccessToken(String clientId);
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package ru.oa2.lti.application.infrastructure.lms;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import ru.oa2.lti.application.config.AppProperties;
|
||||
import ru.oa2.lti.application.service.jwt.JwtAssertionGenerator;
|
||||
import ru.oa2.lti.domain.model.auth.Scope;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LMSServiceImpl implements LMSService {
|
||||
|
||||
private final RestClient restClient;
|
||||
private final AppProperties appProperties;
|
||||
|
||||
public LMSServiceImpl(RestClient restClient, AppProperties appProperties) {
|
||||
this.restClient = restClient;
|
||||
this.appProperties = appProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String ltiAuth(String ltiLoginHint, String iss, String loginHint) {
|
||||
var result = restClient
|
||||
.get()
|
||||
.uri("/lti/auth?" +
|
||||
"state=start" + //TODO а какие могут быть статусы? нужно в Enum перенести
|
||||
"&code=code1" + //TODO что за коды?
|
||||
"&iss=" + iss +
|
||||
"&login_hint=" + loginHint +
|
||||
"&redirect_uri=" + appProperties.lmsUrl() + "/tool/lti/redirect" +
|
||||
"<i_message_hint=" + ltiLoginHint)
|
||||
.retrieve();
|
||||
|
||||
var body = result.toEntity(String.class).getBody();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("lti/auth RESPONSE: {}", body);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String exchangeForAccessToken(String clientId) {
|
||||
|
||||
var endpointUrl = appProperties.lmsUrl() + "/lti/token";
|
||||
|
||||
try {
|
||||
|
||||
var clientAssertion = getClientAssertion(clientId, endpointUrl);
|
||||
var body = getBody(clientAssertion);
|
||||
|
||||
try {
|
||||
var responseBody = restClient.post()
|
||||
.uri(endpointUrl)
|
||||
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
.body(body)
|
||||
.retrieve()
|
||||
.body(Map.class);
|
||||
|
||||
if (responseBody != null && responseBody.containsKey("access_token")) {
|
||||
return (String) responseBody.get("access_token");
|
||||
} else {
|
||||
throw new RuntimeException("Ответ не содержит access_token");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Ошибка получения токена: " + e.getMessage(), e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Не удалось получить access token", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getClientAssertion(String clientId, String endpointUrl) throws Exception {
|
||||
// Генерируем client_assertion
|
||||
return JwtAssertionGenerator.generateClientAssertion(
|
||||
clientId,
|
||||
endpointUrl,
|
||||
"my-key-id-1" //TODO должен совпадать с тем, что в JWKS
|
||||
);
|
||||
}
|
||||
|
||||
private MultiValueMap<String, String> getBody(String clientAssertion) {
|
||||
|
||||
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
|
||||
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", Scope.scope(Scope.LINE_ITEM, Scope.SCOPE, Scope.ENDPOINT));
|
||||
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package ru.oa2.lti.repository;
|
||||
package ru.oa2.lti.application.infrastructure.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.oa2.lti.repository.entities.LMSContent;
|
||||
import ru.oa2.lti.application.infrastructure.repository.entities.LMSContent;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package ru.oa2.lti.repository;
|
||||
package ru.oa2.lti.application.infrastructure.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.oa2.lti.repository.entities.TaskQueue;
|
||||
import ru.oa2.lti.application.infrastructure.repository.entities.TaskQueue;
|
||||
|
||||
@Repository
|
||||
public interface TaskQueueRepository extends JpaRepository<TaskQueue, Long> {
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package ru.oa2.lti.repository.entities;
|
||||
package ru.oa2.lti.application.infrastructure.repository.entities;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Collection;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package ru.oa2.lti.repository.entities;
|
||||
package ru.oa2.lti.application.infrastructure.repository.entities;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import ru.oa2.lti.model.TaskData;
|
||||
import ru.oa2.lti.model.TaskType;
|
||||
import ru.oa2.lti.domain.model.task.TaskData;
|
||||
import ru.oa2.lti.domain.model.task.TaskType;
|
||||
|
||||
@Getter
|
||||
@Entity
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
package ru.oa2.lti.repository.entities;
|
||||
package ru.oa2.lti.application.infrastructure.repository.entities;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import ru.oa2.lti.model.TaskQueueStatus;
|
||||
import ru.oa2.lti.domain.model.task.TaskQueueStatus;
|
||||
|
||||
@Getter
|
||||
@Entity
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.infrastructure.runner;
|
||||
package ru.oa2.lti.application.infrastructure.runner;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.infrastructure.runner;
|
||||
package ru.oa2.lti.application.infrastructure.runner;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.application.service.jwt;
|
||||
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.JWSHeader;
|
||||
import com.nimbusds.jose.crypto.RSASSASigner;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
import ru.oa2.lti.utils.Keys;
|
||||
import ru.oa2.lti.application.utils.Keys;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package ru.oa2.lti.application.service.jwt;
|
||||
|
||||
import ru.oa2.lti.domain.model.auth.IdTokenPayload;
|
||||
import ru.oa2.lti.domain.model.auth.Payload;
|
||||
|
||||
public interface JwtService {
|
||||
|
||||
Payload getPayload(String jwt);
|
||||
|
||||
IdTokenPayload getTokenPayload(String jwt);
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.application.service.jwt;
|
||||
|
||||
import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.oa2.lti.domain.model.auth.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.oa2.lti.application.service.results;
|
||||
|
||||
public interface ResultService {
|
||||
|
||||
void setResult(String accessToken, String idToken);
|
||||
}
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
package ru.oa2.lti.service.results;
|
||||
package ru.oa2.lti.application.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 ru.oa2.lti.service.results.dto.GradingType;
|
||||
import ru.oa2.lti.service.results.dto.Lineitems;
|
||||
import ru.oa2.lti.service.results.dto.ProgressType;
|
||||
import ru.oa2.lti.service.results.dto.ResultRequest;
|
||||
import ru.oa2.lti.application.service.jwt.JwtService;
|
||||
import ru.oa2.lti.domain.model.results.GradingType;
|
||||
import ru.oa2.lti.domain.model.results.ProgressType;
|
||||
import ru.oa2.lti.domain.model.results.ResultRequest;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
|
@ -18,29 +17,26 @@ public class ResultServiceImpl implements ResultService {
|
|||
|
||||
private final RestClient restClient;
|
||||
private final ObjectMapper mapper;
|
||||
private final JwtService jwtService;
|
||||
|
||||
private final String CONTENT_TYPE = "application/vnd.ims.lis.v1.score+json";
|
||||
private final String ACCEPT = "application/vnd.ims.lis.v1.score+json";
|
||||
|
||||
public ResultServiceImpl(RestClient client,
|
||||
ObjectMapper mapper,
|
||||
JwtService jwtService) {
|
||||
|
||||
public ResultServiceImpl(RestClient client, ObjectMapper mapper) {
|
||||
this.restClient = client;
|
||||
this.mapper = mapper;
|
||||
this.jwtService = jwtService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(String accessToken, IdTokenPayload idToken) {
|
||||
public void setResult(String accessToken, String idTokenString) {
|
||||
|
||||
// http://openolat.local/lti/ags/b0d12061-2564-47a0-9291-b0d226f1eefe/context/112999233990970/lineitems/112999233990979/lineitem
|
||||
var idToken = jwtService.getTokenPayload(idTokenString);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
var body = mapper.writeValueAsString(
|
||||
ResultRequest.builder()
|
||||
.scoreGiven(1L)
|
||||
|
|
@ -56,8 +52,8 @@ public class ResultServiceImpl implements ResultService {
|
|||
.post()
|
||||
.uri(idToken.getTokenEndpoint().lineitem() + "/scores/result")
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header("Content-Type", "application/vnd.ims.lis.v1.score+json")
|
||||
.header("Accept", "application/vnd.ims.lis.v1.score+json")
|
||||
.header("Content-Type", CONTENT_TYPE)
|
||||
.header("Accept", ACCEPT)
|
||||
.body(body)
|
||||
.retrieve();
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package ru.oa2.lti.application.service.task;
|
||||
|
||||
import ru.oa2.lti.domain.model.ResultRequest;
|
||||
import ru.oa2.lti.domain.model.results.ResultResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface TaskService {
|
||||
|
||||
/*
|
||||
Получение задачи (контекста лабораторной работы)
|
||||
*/
|
||||
String getTask(UUID contextId);
|
||||
|
||||
/*
|
||||
Запуск проверки через скрипт
|
||||
*/
|
||||
ResultResponse checkTask(ResultRequest resultRequest);
|
||||
|
||||
/*
|
||||
Сохранение/обновление task-а
|
||||
*/
|
||||
void saveTask(String initScript, String checkScript, String description);
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package ru.oa2.lti.application.service.task;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.oa2.lti.application.infrastructure.lms.LMSService;
|
||||
import ru.oa2.lti.application.infrastructure.runner.Runner;
|
||||
import ru.oa2.lti.application.service.results.ResultService;
|
||||
import ru.oa2.lti.domain.model.ResultRequest;
|
||||
import ru.oa2.lti.domain.model.task.TaskData;
|
||||
import ru.oa2.lti.application.infrastructure.repository.LMSContentRepository;
|
||||
import ru.oa2.lti.application.infrastructure.repository.entities.LMSContent;
|
||||
import ru.oa2.lti.application.infrastructure.repository.entities.Task;
|
||||
import ru.oa2.lti.domain.model.results.ResultResponse;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class TaskServiceImpl implements TaskService {
|
||||
|
||||
private final LMSContentRepository lmsContentRepository;
|
||||
private final Runner runner;
|
||||
|
||||
//TODO объединить в один?
|
||||
final ResultService resultService;
|
||||
final LMSService lmsService;
|
||||
|
||||
public TaskServiceImpl(LMSContentRepository lmsContentRepository,
|
||||
Runner runner,
|
||||
ResultService resultService,
|
||||
LMSService lmsService) {
|
||||
|
||||
this.lmsContentRepository = lmsContentRepository;
|
||||
this.runner = runner;
|
||||
this.resultService = resultService;
|
||||
this.lmsService = lmsService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTask(UUID contextId) {
|
||||
var content = lmsContentRepository.getLMSContentByContentId(contextId);
|
||||
|
||||
if(content.isPresent()) {
|
||||
LMSContent lmsContent = content.get();
|
||||
Collection<Task> tasks = lmsContent.getTasks();
|
||||
|
||||
//TODO добавить версию в Task и выбирать самую старшую и опубликованную
|
||||
TaskData data = tasks.stream().findFirst().get().getData();
|
||||
|
||||
//TODO string -> exception?
|
||||
if (!runner.run(contextId, data.getInitScript())) {
|
||||
return "Init script FAILED";
|
||||
}
|
||||
|
||||
return data.getDescription();
|
||||
} else {
|
||||
return "Not Page";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultResponse checkTask(ResultRequest resultRequest) {
|
||||
|
||||
//TODO запуск скрипта проверки
|
||||
var assessToken = lmsService.exchangeForAccessToken(
|
||||
resultRequest.clientId());
|
||||
resultService.setResult(assessToken, resultRequest.idToken());
|
||||
|
||||
//TODO обработка и запуск скрипта
|
||||
return new ResultResponse("success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveTask(String initScript, String checkScript, String description) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.utils;
|
||||
package ru.oa2.lti.application.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package ru.oa2.lti.controller;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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 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;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/tool/lti/result")
|
||||
public class ResultController {
|
||||
|
||||
final ApplicationService service;
|
||||
final ResultService resultService;
|
||||
final TokenService tokenService;
|
||||
final JwtService jwtService;
|
||||
|
||||
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 ResponseEntity result(@RequestBody String body,
|
||||
HttpServletRequest req) {
|
||||
|
||||
log.info("RESULT: {}", body);
|
||||
|
||||
HttpSession session = req.getSession(false);
|
||||
if (session == null) return ResponseEntity.status(401).build();
|
||||
|
||||
//TODO запуск скрипта проверки
|
||||
var ltiLogin = (LtiLogin) session.getAttribute("lti_login");
|
||||
var assessToken = tokenService.exchangeForAccessToken(
|
||||
ltiLogin.getClientId());
|
||||
|
||||
String idToken = (String) session.getAttribute("id_token");
|
||||
|
||||
resultService.setResult(
|
||||
assessToken,
|
||||
jwtService.getTokenPayload(idToken)
|
||||
);
|
||||
|
||||
return ResponseEntity
|
||||
.accepted()
|
||||
.body(service.saveResult(body));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package ru.oa2.lti.domain.carecase;
|
||||
|
||||
public class GetTask {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.oa2.lti.domain.model;
|
||||
|
||||
//TODO 2 одинаковых имени? )
|
||||
public record ResultRequest(
|
||||
String clientId,
|
||||
String idToken
|
||||
) {
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
/*
|
||||
"https://purl.imsglobal.org/spec/lti/claim/launch_presentation" : {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.model;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
package ru.oa2.lti.domain.model.auth;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.results.dto;
|
||||
package ru.oa2.lti.domain.model.results;
|
||||
|
||||
public enum GradingType {
|
||||
NotStarted,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.results.dto;
|
||||
package ru.oa2.lti.domain.model.results;
|
||||
|
||||
public record Lineitems(
|
||||
String id,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.results.dto;
|
||||
package ru.oa2.lti.domain.model.results;
|
||||
|
||||
public enum ProgressType {
|
||||
Initialized,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.results.dto;
|
||||
package ru.oa2.lti.domain.model.results;
|
||||
|
||||
|
||||
import lombok.Builder;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.service.results.dto;
|
||||
package ru.oa2.lti.domain.model.results;
|
||||
|
||||
public record ResultResponse(
|
||||
String status
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.model;
|
||||
package ru.oa2.lti.domain.model.task;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.model;
|
||||
package ru.oa2.lti.domain.model.task;
|
||||
|
||||
public enum TaskQueueStatus {
|
||||
NEW,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.model;
|
||||
package ru.oa2.lti.domain.model.task;
|
||||
|
||||
public record TaskResult(
|
||||
//TODO результат работы(лог), true/false, error:<msg>, оценка
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.oa2.lti.model;
|
||||
package ru.oa2.lti.domain.model.task;
|
||||
|
||||
public enum TaskType {
|
||||
DOCKER,
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package ru.oa2.lti.infrastructure;
|
||||
|
||||
public interface LMSService {
|
||||
|
||||
String ltiAuth(String ltiLoginHint, String iss, String loginHint);
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package ru.oa2.lti.infrastructure;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import ru.oa2.lti.config.AppProperties;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LMSServiceImpl implements LMSService {
|
||||
|
||||
private final RestClient restClient;
|
||||
private final AppProperties appProperties;
|
||||
|
||||
public LMSServiceImpl(RestClient restClient, AppProperties appProperties) {
|
||||
this.restClient = restClient;
|
||||
this.appProperties = appProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String 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=" + appProperties.lmsUrl() + "/tool/lti/redirect" +
|
||||
"<i_message_hint=" + ltiLoginHint)
|
||||
.retrieve();
|
||||
|
||||
ResponseEntity<String> body = result.toEntity(String.class);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("lti/auth RESPONSE: {}", body);
|
||||
}
|
||||
return body.getBody();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package ru.oa2.lti.model;
|
||||
|
||||
public record ResultRequest(
|
||||
String taskId,
|
||||
String userId,
|
||||
String answer
|
||||
) {
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package ru.oa2.lti.model;
|
||||
|
||||
public record User(
|
||||
String clientId,
|
||||
String role
|
||||
) {
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package ru.oa2.lti.service.jwt;
|
||||
|
||||
public interface JwtService {
|
||||
|
||||
Payload getPayload(String jwt);
|
||||
|
||||
IdTokenPayload getTokenPayload(String jwt);
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
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 ru.oa2.lti.config.AppProperties;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TokenService {
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
private final AppProperties appProperties;
|
||||
|
||||
public TokenService(AppProperties appProperties) {
|
||||
this.appProperties = appProperties;
|
||||
}
|
||||
|
||||
// Вызывается после LTI-запуска
|
||||
public String exchangeForAccessToken(
|
||||
String clientId) {
|
||||
|
||||
var endpointUrl = appProperties.lmsUrl() + "/lti/token";
|
||||
|
||||
try {
|
||||
// Генерируем client_assertion
|
||||
String clientAssertion = JwtAssertionGenerator.generateClientAssertion(
|
||||
clientId,
|
||||
endpointUrl,
|
||||
"my-key-id-1" // должен совпадать с тем, что в JWKS
|
||||
);
|
||||
|
||||
// Формируем тело запроса
|
||||
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
|
||||
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", Scope.scope(Scope.LINE_ITEM, Scope.SCOPE, Scope.ENDPOINT));
|
||||
|
||||
// Заголовки
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);
|
||||
|
||||
// Отправляем запрос к OpenOLAT
|
||||
ResponseEntity<Map> response = restTemplate.postForEntity(endpointUrl, request, Map.class);
|
||||
|
||||
if (response.getStatusCode() == HttpStatus.OK) {
|
||||
Map<String, Object> responseBody = response.getBody();
|
||||
return (String) responseBody.get("access_token");
|
||||
} else {
|
||||
throw new RuntimeException("Ошибка получения токена: " + response.getStatusCode());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Не удалось получить access token", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
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);
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package ru.oa2.lti.service.task;
|
||||
|
||||
import ru.oa2.lti.model.ResultRequest;
|
||||
import ru.oa2.lti.model.TaskResult;
|
||||
import ru.oa2.lti.model.User;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface TaskService {
|
||||
|
||||
/*
|
||||
Получение задачи (контекста лабораторной работы)
|
||||
*/
|
||||
//TODO возвращаем объект -> в String отдельный тех.класс сериализации через шаблонизатор
|
||||
String getTask(String id, User user);
|
||||
|
||||
/*
|
||||
Отправить ответ по задаче
|
||||
UUID - id - kеу kafka message?
|
||||
*/
|
||||
UUID submit(ResultRequest resultRequest);
|
||||
|
||||
/*
|
||||
Проверить результат по UUID отправленного результата
|
||||
*/
|
||||
TaskResult getResult(UUID requestId);
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package ru.oa2.lti.service.task;
|
||||
|
||||
import ru.oa2.lti.model.ResultRequest;
|
||||
import ru.oa2.lti.model.TaskResult;
|
||||
import ru.oa2.lti.model.User;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
//TODO реализовать методы
|
||||
public class TaskServiceImpl implements TaskService {
|
||||
|
||||
@Override
|
||||
public String getTask(String id, User user) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID submit(ResultRequest resultRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskResult getResult(UUID requestId) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,8 @@ document.getElementById('submitBtn').addEventListener('click', function () {
|
|||
participantId: participantId
|
||||
};
|
||||
|
||||
fetch('/tool/lti/result/docker', {
|
||||
//TODO payload не нужен, убрать
|
||||
fetch('/tool/lti/task/submit', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package ru.oa2.lti;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ru.oa2.lti.service.jwt.Scope;
|
||||
import ru.oa2.lti.domain.model.auth.Scope;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package ru.oa2.lti.infrastructure;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import ru.oa2.lti.infrastructure.runner.Runner;
|
||||
import ru.oa2.lti.infrastructure.runner.RunnerImpl;
|
||||
import ru.oa2.lti.application.infrastructure.runner.Runner;
|
||||
import ru.oa2.lti.application.infrastructure.runner.RunnerImpl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package ru.oa2.lti.jwt;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import ru.oa2.lti.config.AppConfig;
|
||||
import ru.oa2.lti.service.jwt.JwtService;
|
||||
import ru.oa2.lti.service.jwt.JwtServiceImpl;
|
||||
import ru.oa2.lti.application.config.AppConfig;
|
||||
import ru.oa2.lti.application.service.jwt.JwtService;
|
||||
import ru.oa2.lti.application.service.jwt.JwtServiceImpl;
|
||||
|
||||
@SpringBootTest(classes = {AppConfig.class, JwtServiceImpl.class})
|
||||
public class IdTokenTest {
|
||||
|
|
|
|||
Loading…
Reference in New Issue