diff --git a/datavines-core/src/main/java/io/datavines/core/constant/DataVinesConstants.java b/datavines-core/src/main/java/io/datavines/core/constant/DataVinesConstants.java index 8628dc569..070b107c1 100644 --- a/datavines-core/src/main/java/io/datavines/core/constant/DataVinesConstants.java +++ b/datavines-core/src/main/java/io/datavines/core/constant/DataVinesConstants.java @@ -18,6 +18,8 @@ public class DataVinesConstants { + public static String TOKEN = "token"; + public static String LOGIN_USER = "login_user"; public static String WORKSPACE_ID = "workspace_id"; @@ -61,11 +63,11 @@ public class DataVinesConstants { public static final String TOKEN_HEADER_STRING = "Authorization"; - public static final String TOKEN_USER_NAME = "token_user_name"; + public static final String TOKEN_USER_NAME = "un"; - public static final String TOKEN_USER_PASSWORD = "token_user_password"; + public static final String TOKEN_USER_PASSWORD = "up"; - public static final String TOKEN_CREATE_TIME = "token_create_time"; + public static final String TOKEN_CREATE_TIME = "ct"; public static final String TOKEN_VERIFICATION_CODE = "token_verification_code"; diff --git a/datavines-core/src/main/java/io/datavines/core/utils/TokenManager.java b/datavines-core/src/main/java/io/datavines/core/utils/TokenManager.java index 8c57fae5a..39c0e4474 100644 --- a/datavines-core/src/main/java/io/datavines/core/utils/TokenManager.java +++ b/datavines-core/src/main/java/io/datavines/core/utils/TokenManager.java @@ -17,15 +17,15 @@ package io.datavines.core.utils; import io.datavines.core.constant.DataVinesConstants; +import io.datavines.core.exception.DataVinesServerException; +import io.jsonwebtoken.CompressionCodecs; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.HashMap; import java.util.Map; -import java.util.Random; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -35,6 +35,7 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.datavines.common.entity.TokenInfo; +@Slf4j @Component public class TokenManager { @@ -44,7 +45,7 @@ public class TokenManager { @Value("${jwt.token.timeout:8640000}") private Long timeout; - @Value("${jwt.token.algorithm:HS512}") + @Value("${jwt.token.algorithm:HS256}") private String algorithm; public String generateToken(String username, String password) { @@ -63,6 +64,21 @@ public String generateToken(TokenInfo tokenInfo) { return generate(claims); } + public String generateToken(String token, Long timeOutMillis) { + Map claims = new HashMap<>(); + String username = getUsername(token); + String password = getPassword(token); + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { + throw new DataVinesServerException("can not get the user info from token"); + } + + claims.put(DataVinesConstants.TOKEN_USER_NAME, username); + claims.put(DataVinesConstants.TOKEN_USER_PASSWORD, password); + claims.put(DataVinesConstants.TOKEN_CREATE_TIME, System.currentTimeMillis()); + + return toTokenString(timeOutMillis, claims); + } + public String refreshToken(String token) { Claims claims = getClaims(token); claims.put(DataVinesConstants.TOKEN_CREATE_TIME, System.currentTimeMillis()); @@ -88,6 +104,7 @@ public String generateContinuousToken(TokenInfo tokenInfo) { .setClaims(claims) .setSubject(claims.get(DataVinesConstants.TOKEN_USER_NAME).toString()) .signWith(SignatureAlgorithm.valueOf(algorithm), tokenSecret.getBytes(StandardCharsets.UTF_8)) + .compressWith(CompressionCodecs.DEFLATE) .compact(); } @@ -105,6 +122,7 @@ public String toTokenString(Long timeOutMillis, Map claims) { .setSubject(null == claims.get(DataVinesConstants.TOKEN_USER_NAME) ? null : claims.get(DataVinesConstants.TOKEN_USER_NAME).toString()) .setExpiration(new Date(expiration)) .signWith(SignatureAlgorithm.valueOf(algorithm), tokenSecret.getBytes(StandardCharsets.UTF_8)) + .compressWith(CompressionCodecs.DEFLATE) .compact(); } @@ -114,7 +132,7 @@ public String getUsername(String token) { final Claims claims = getClaims(token); username = claims.get(DataVinesConstants.TOKEN_USER_NAME).toString(); } catch (Exception e) { - e.printStackTrace(); + log.error("get username from token error : ", e); } return username; } @@ -125,7 +143,7 @@ public String getPassword(String token) { final Claims claims = getClaims(token); password = claims.get(DataVinesConstants.TOKEN_USER_PASSWORD).toString(); } catch (Exception e) { - e.printStackTrace(); + log.error("get password from token error : ", e); } return password; } @@ -151,7 +169,7 @@ private Date getCreatedDate(String token) { final Claims claims = getClaims(token); created = new Date((Long) claims.get(DataVinesConstants.TOKEN_CREATE_TIME)); } catch (Exception e) { - e.printStackTrace(); + log.error("get create time from token error : ", e); } return created; } @@ -162,7 +180,7 @@ private Date getExpirationDate(String token) { final Claims claims = getClaims(token); expiration = claims.getExpiration(); } catch (Exception e) { - e.printStackTrace(); + log.error("get expiration time from token error : ", e); } return expiration; } diff --git a/datavines-server/src/main/java/io/datavines/server/api/annotation/CheckTokenExist.java b/datavines-server/src/main/java/io/datavines/server/api/annotation/CheckTokenExist.java new file mode 100644 index 000000000..1b06c61bf --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/annotation/CheckTokenExist.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.datavines.server.api.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface CheckTokenExist { +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/controller/AccessTokenController.java b/datavines-server/src/main/java/io/datavines/server/api/controller/AccessTokenController.java new file mode 100644 index 000000000..6cfaf535f --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/controller/AccessTokenController.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.datavines.server.api.controller; + +import io.datavines.core.aop.RefreshToken; +import io.datavines.core.constant.DataVinesConstants; +import io.datavines.core.exception.DataVinesServerException; +import io.datavines.server.api.dto.bo.token.TokenCreate; +import io.datavines.server.api.dto.bo.token.TokenUpdate; +import io.datavines.server.repository.service.AccessTokenService; +import io.datavines.server.utils.ContextHolder; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; + +@Api(value = "token", tags = "token", produces = MediaType.APPLICATION_JSON_VALUE) +@RestController +@RequestMapping(value = DataVinesConstants.BASE_API_PATH + "/token", produces = MediaType.APPLICATION_JSON_VALUE) +@RefreshToken +public class AccessTokenController { + + @Resource + private AccessTokenService accessTokenService; + + @ApiOperation(value = "create token") + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + public Object createToken(@Valid @RequestBody TokenCreate tokenCreate) throws DataVinesServerException { + return accessTokenService.create(tokenCreate); + } + + @ApiOperation(value = "update token") + @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + public Object updateToken(@Valid @RequestBody TokenUpdate tokenUpdate) throws DataVinesServerException { + return accessTokenService.update(tokenUpdate); + } + + @ApiOperation(value = "delete token") + @DeleteMapping(value = "/{id}") + public Object deleteToken(@PathVariable Long id) { + // 加入黑名单,并且需要拦截器进行处理 + return accessTokenService.deleteToken(id); + } + + @ApiOperation(value = "page token") + @GetMapping(value = "/page") + public Object listByUserId(@RequestParam("workspaceId") Long workspaceId, + @RequestParam("pageNumber") Integer pageNumber, + @RequestParam("pageSize") Integer pageSize) { + return accessTokenService.page(workspaceId, ContextHolder.getUserId(), pageNumber, pageSize); + } +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/controller/ConfigController.java b/datavines-server/src/main/java/io/datavines/server/api/controller/ConfigController.java index 51864fefa..bb70bef3c 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/controller/ConfigController.java +++ b/datavines-server/src/main/java/io/datavines/server/api/controller/ConfigController.java @@ -42,14 +42,14 @@ public class ConfigController { @ApiOperation(value = "create config") @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - public Object createConfig(@Valid @RequestBody ConfigCreate ConfigCreate) throws DataVinesServerException { - return configService.create(ConfigCreate); + public Object createConfig(@Valid @RequestBody ConfigCreate configCreate) throws DataVinesServerException { + return configService.create(configCreate); } @ApiOperation(value = "update config") @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - public Object updateConfig(@Valid @RequestBody ConfigUpdate ConfigUpdate) throws DataVinesServerException { - return configService.update(ConfigUpdate)>0; + public Object updateConfig(@Valid @RequestBody ConfigUpdate configUpdate) throws DataVinesServerException { + return configService.update(configUpdate)>0; } @ApiOperation(value = "delete config") diff --git a/datavines-server/src/main/java/io/datavines/server/api/controller/OpenApiController.java b/datavines-server/src/main/java/io/datavines/server/api/controller/OpenApiController.java new file mode 100644 index 000000000..3ba081f12 --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/controller/OpenApiController.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.datavines.server.api.controller; + +import io.datavines.core.aop.RefreshToken; +import io.datavines.core.constant.DataVinesConstants; +import io.datavines.core.exception.DataVinesServerException; +import io.datavines.server.api.annotation.CheckTokenExist; +import io.datavines.server.api.dto.vo.JobExecutionResultVO; +import io.datavines.server.repository.service.JobExecutionResultService; +import io.datavines.server.repository.service.JobExecutionService; +import io.datavines.server.repository.service.JobService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +@Api(value = "openapi", tags = "openapi", produces = MediaType.APPLICATION_JSON_VALUE) +@RestController +@RequestMapping(value = DataVinesConstants.BASE_API_PATH + "/openapi", produces = MediaType.APPLICATION_JSON_VALUE) +@RefreshToken +@Validated +public class OpenApiController { + + @Autowired + private JobService jobService; + + @Autowired + private JobExecutionService jobExecutionService; + + @Autowired + private JobExecutionResultService jobExecutionResultService; + + @CheckTokenExist + @ApiOperation(value = "execute job") + @PostMapping(value = "/job/execute/{id}") + public Object executeJob(@PathVariable("id") Long jobId) throws DataVinesServerException { + return jobService.execute(jobId, null); + } + + @CheckTokenExist + @ApiOperation(value = "kill job", response = Long.class) + @DeleteMapping(value = "/job/execution/kill/{executionId}") + public Object kill(@PathVariable("executionId") Long executionId) { + return jobExecutionService.killJob(executionId); + } + + @CheckTokenExist + @ApiOperation(value = "get job execution status", response = String.class) + @GetMapping(value = "/job/execution/status/{executionId}") + public Object getTaskStatus(@PathVariable("executionId") Long executionId) { + return jobExecutionService.getById(executionId).getStatus().getCode(); + } + + @CheckTokenExist + @Deprecated + @ApiOperation(value = "get job execution result", response = JobExecutionResultVO.class) + @GetMapping(value = "/job/execution/result/{executionId}") + public Object getJobExecutionResultInfo(@PathVariable("executionId") Long executionId) { + return jobExecutionResultService.getCheckResultByJobExecutionId(executionId); + } +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/bo/token/TokenCreate.java b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/token/TokenCreate.java new file mode 100644 index 000000000..39f4a729b --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/token/TokenCreate.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.datavines.server.api.dto.bo.token; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +@NotNull(message = "Token Create cannot be null") +public class TokenCreate { + + @NotNull(message = "WorkspaceId cannot be empty") + private long workspaceId; + + @NotBlank(message = "expireTime cannot be empty") + private String expireTime; +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/bo/token/TokenUpdate.java b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/token/TokenUpdate.java new file mode 100644 index 000000000..e75eb6112 --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/bo/token/TokenUpdate.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.datavines.server.api.dto.bo.token; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotNull; + +@Data +@EqualsAndHashCode(callSuper = true) +@NotNull(message = "Token Update cannot be null") +public class TokenUpdate extends TokenCreate { + + @NotNull(message = "Config id cannot be null") + private Long id; +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionCheckResultVO.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionCheckResultVO.java new file mode 100644 index 000000000..bae54b09d --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionCheckResultVO.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.datavines.server.api.dto.vo; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class JobExecutionCheckResultVO implements Serializable { + + private Long executionId; + + private int checkResult; +} diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionVO.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionVO.java index 2b876dfe9..0b58f787f 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionVO.java +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobExecutionVO.java @@ -20,7 +20,7 @@ import io.datavines.common.enums.ExecutionStatus; import io.datavines.common.enums.JobType; import io.datavines.core.utils.LanguageUtils; -import io.datavines.server.enums.DqJobExecutionState; +import io.datavines.server.enums.JobCheckState; import lombok.Data; import java.io.Serializable; @@ -47,7 +47,7 @@ public class JobExecutionVO implements Serializable { private ExecutionStatus status; - private DqJobExecutionState checkState; + private JobCheckState checkState; @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private LocalDateTime startTime; @@ -64,7 +64,7 @@ public String getJobType() { public String getCheckState() { if (checkState == null) { - return LanguageUtils.isZhContext()? DqJobExecutionState.NONE.getZhDescription() : DqJobExecutionState.NONE.getDescription(); + return LanguageUtils.isZhContext()? JobCheckState.NONE.getZhDescription() : JobCheckState.NONE.getDescription(); } return LanguageUtils.isZhContext()? checkState.getZhDescription() : checkState.getDescription(); } diff --git a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobQualityReportVO.java b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobQualityReportVO.java index 7fc0ef2aa..9f8455bdc 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobQualityReportVO.java +++ b/datavines-server/src/main/java/io/datavines/server/api/dto/vo/JobQualityReportVO.java @@ -17,16 +17,11 @@ package io.datavines.server.api.dto.vo; import com.fasterxml.jackson.annotation.JsonFormat; -import io.datavines.common.enums.ExecutionStatus; -import io.datavines.common.enums.JobType; -import io.datavines.core.utils.LanguageUtils; -import io.datavines.server.enums.DqJobExecutionState; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; @Data public class JobQualityReportVO implements Serializable { diff --git a/datavines-server/src/main/java/io/datavines/server/api/inteceptor/AuthenticationInterceptor.java b/datavines-server/src/main/java/io/datavines/server/api/inteceptor/AuthenticationInterceptor.java index 226f52d10..d8642cd0d 100644 --- a/datavines-server/src/main/java/io/datavines/server/api/inteceptor/AuthenticationInterceptor.java +++ b/datavines-server/src/main/java/io/datavines/server/api/inteceptor/AuthenticationInterceptor.java @@ -20,12 +20,15 @@ import io.datavines.core.constant.DataVinesConstants; import io.datavines.server.api.annotation.AuthIgnore; import io.datavines.core.enums.Status; +import io.datavines.server.api.annotation.CheckTokenExist; import io.datavines.server.repository.entity.User; +import io.datavines.server.repository.service.AccessTokenService; import io.datavines.server.repository.service.UserService; import io.datavines.core.exception.DataVinesServerException; import io.datavines.server.utils.ContextHolder; import io.datavines.core.utils.TokenManager; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; @@ -44,8 +47,11 @@ public class AuthenticationInterceptor implements HandlerInterceptor { @Resource private UserService userService; + @Resource + private AccessTokenService accessTokenService; + @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) { HandlerMethod handlerMethod = null; try { @@ -63,6 +69,8 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return true; } + CheckTokenExist checkTokenExist = method.getAnnotation(CheckTokenExist.class); + String token = request.getHeader(DataVinesConstants.TOKEN_HEADER_STRING); if (StringUtils.isEmpty(token)){ @@ -72,6 +80,12 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons } } + if (checkTokenExist != null) { + if (!accessTokenService.checkTokenExist(token)) { + throw new DataVinesServerException(Status.INVALID_TOKEN, token); + } + } + String username = tokeManager.getUsername(token); User user = userService.getByUsername(username); if (null == user) { @@ -84,17 +98,18 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons } ContextHolder.setParam(DataVinesConstants.LOGIN_USER, user); + ContextHolder.setParam(DataVinesConstants.TOKEN, token); return true; } @Override - public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, ModelAndView modelAndView) { } @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) { ContextHolder.removeAll(); } } diff --git a/datavines-server/src/main/java/io/datavines/server/dqc/coordinator/validator/JobResultValidator.java b/datavines-server/src/main/java/io/datavines/server/dqc/coordinator/validator/JobResultValidator.java index 5dc50ba39..947cf8abf 100644 --- a/datavines-server/src/main/java/io/datavines/server/dqc/coordinator/validator/JobResultValidator.java +++ b/datavines-server/src/main/java/io/datavines/server/dqc/coordinator/validator/JobResultValidator.java @@ -29,7 +29,7 @@ import io.datavines.notification.api.entity.SlaSenderMessage; import io.datavines.notification.core.client.NotificationClient; import io.datavines.server.api.dto.bo.issue.IssueCreate; -import io.datavines.server.enums.DqJobExecutionState; +import io.datavines.server.enums.JobCheckState; import io.datavines.server.repository.entity.DataSource; import io.datavines.server.repository.entity.Job; import io.datavines.server.repository.entity.JobExecution; @@ -112,11 +112,11 @@ private boolean checkDqExecuteResult(JobExecutionResult jobExecutionResult) { BeanUtils.copyProperties(jobExecutionResult, metricExecutionResult); if (MetricValidator.isSuccess(metricExecutionResult)) { jobExecutionResult.setScore(MetricValidator.getQualityScore(metricExecutionResult, true)); - jobExecutionResult.setState(DqJobExecutionState.SUCCESS.getCode()); + jobExecutionResult.setState(JobCheckState.SUCCESS.getCode()); result = true; } else { jobExecutionResult.setScore(MetricValidator.getQualityScore(metricExecutionResult, false)); - jobExecutionResult.setState(DqJobExecutionState.FAILURE.getCode()); + jobExecutionResult.setState(JobCheckState.FAILURE.getCode()); } jobExternalService.updateJobExecutionResult(jobExecutionResult); diff --git a/datavines-server/src/main/java/io/datavines/server/enums/DqJobExecutionState.java b/datavines-server/src/main/java/io/datavines/server/enums/JobCheckState.java similarity index 74% rename from datavines-server/src/main/java/io/datavines/server/enums/DqJobExecutionState.java rename to datavines-server/src/main/java/io/datavines/server/enums/JobCheckState.java index 2f20472f2..17a16820a 100644 --- a/datavines-server/src/main/java/io/datavines/server/enums/DqJobExecutionState.java +++ b/datavines-server/src/main/java/io/datavines/server/enums/JobCheckState.java @@ -26,17 +26,17 @@ /** * data quality task state */ -public enum DqJobExecutionState { +public enum JobCheckState { /** * 0-none * 1-success * 2-failure */ - NONE(0, "none", "默认"), + NONE(0, "none", "未知"), SUCCESS(1, "success", "成功"), FAILURE(2, "failure", "失败"); - DqJobExecutionState(int code, String description, String zhDescription) { + JobCheckState(int code, String description, String zhDescription) { this.code = code; this.description = description; this.zhDescription = zhDescription; @@ -58,18 +58,18 @@ public String getDescription(boolean isEn) { return isEn ? description : zhDescription; } - private static final Map VALUES_MAP = new HashMap<>(); + private static final Map VALUES_MAP = new HashMap<>(); static { - for (DqJobExecutionState type : DqJobExecutionState.values()) { - VALUES_MAP.put(type.code,type); + for (JobCheckState type : JobCheckState.values()) { + VALUES_MAP.put(type.code, type); } } - public static DqJobExecutionState of(Integer status) { - if (VALUES_MAP.containsKey(status)) { - return VALUES_MAP.get(status); + public static JobCheckState of(Integer code) { + if (VALUES_MAP.containsKey(code)) { + return VALUES_MAP.get(code); } - throw new IllegalArgumentException("invalid code : " + status); + throw new IllegalArgumentException("invalid code : " + code); } } \ No newline at end of file diff --git a/datavines-server/src/main/java/io/datavines/server/registry/Register.java b/datavines-server/src/main/java/io/datavines/server/registry/Register.java index 01c791cad..4928be338 100644 --- a/datavines-server/src/main/java/io/datavines/server/registry/Register.java +++ b/datavines-server/src/main/java/io/datavines/server/registry/Register.java @@ -28,6 +28,7 @@ import io.datavines.server.repository.entity.Config; import io.datavines.server.repository.service.ConfigService; import io.datavines.server.utils.SpringApplicationContext; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; @@ -52,6 +53,7 @@ public class Register { private volatile int currentSlot = 0; + @Getter private volatile int totalSlot = 0; private static final Integer QUEUE_MAX_SIZE = 20; @@ -162,10 +164,6 @@ public int getSlot() { return currentSlot; } - public int getTotalSlot() { - return totalSlot; - } - public void updateServerListInfo() { log.info("active server list:{}", registry.getActiveServerList()); queue.clear(); diff --git a/datavines-server/src/main/java/io/datavines/server/repository/entity/AccessToken.java b/datavines-server/src/main/java/io/datavines/server/repository/entity/AccessToken.java new file mode 100644 index 000000000..6b9c7107f --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/repository/entity/AccessToken.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.datavines.server.repository.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@TableName("dv_access_token") +public class AccessToken implements Serializable { + + private static final long serialVersionUID = -1L; + + @TableId(type= IdType.AUTO) + private Long id; + + @TableField(value = "workspace_id") + private Long workspaceId; + + @TableField(value = "user_id") + private Long userId; + + @TableField(value = "token") + private String token; + + @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + @TableField(value = "expire_time") + private LocalDateTime expireTime; + + @TableField(value = "create_by") + private Long createBy; + + @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + @TableField(value = "create_time") + private LocalDateTime createTime; + + @TableField(value = "update_by") + private Long updateBy; + + @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + @TableField(value = "update_time") + private LocalDateTime updateTime; +} diff --git a/datavines-server/src/main/java/io/datavines/server/repository/mapper/AccessTokenMapper.java b/datavines-server/src/main/java/io/datavines/server/repository/mapper/AccessTokenMapper.java new file mode 100644 index 000000000..bafd1a7ba --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/repository/mapper/AccessTokenMapper.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.datavines.server.repository.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import io.datavines.server.repository.entity.AccessToken; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface AccessTokenMapper extends BaseMapper { +} diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/AccessTokenService.java b/datavines-server/src/main/java/io/datavines/server/repository/service/AccessTokenService.java new file mode 100644 index 000000000..6aa12285d --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/AccessTokenService.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.datavines.server.repository.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import io.datavines.server.api.dto.bo.token.TokenCreate; +import io.datavines.server.api.dto.bo.token.TokenUpdate; +import io.datavines.server.repository.entity.AccessToken; + +import java.util.List; + +public interface AccessTokenService extends IService { + + Boolean checkTokenExist(String token); + + Long create(TokenCreate tokenCreate); + + Long update(TokenUpdate tokenUpdate); + + boolean deleteToken(Long id); + + IPage page(Long workspaceId, Long userId, Integer pageNumber, Integer pageSize); +} diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionResultService.java b/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionResultService.java index 74fe24436..7f4e2c9d5 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionResultService.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/JobExecutionResultService.java @@ -17,6 +17,7 @@ package io.datavines.server.repository.service; import com.baomidou.mybatisplus.extension.service.IService; +import io.datavines.server.api.dto.vo.JobExecutionCheckResultVO; import io.datavines.server.api.dto.vo.JobExecutionResultVO; import io.datavines.server.repository.entity.JobExecutionResult; @@ -40,6 +41,8 @@ public interface JobExecutionResultService extends IService JobExecutionResultVO getResultVOByJobExecutionId(long jobExecutionId); + int getCheckResultByJobExecutionId(long jobExecutionId); + List getResultVOListByJobExecutionId(long jobExecutionId); List listByJobIdAndTimeRange(Long jobId, String startTime, String endTime); diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/JobService.java b/datavines-server/src/main/java/io/datavines/server/repository/service/JobService.java index 4fd59c41c..43353de98 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/JobService.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/JobService.java @@ -56,7 +56,7 @@ IPage getJobPage(String searchVal, Integer pageNumber, Integer pageSize); - boolean execute(Long jobId, LocalDateTime scheduleTime) throws DataVinesServerException; + Long execute(Long jobId, LocalDateTime scheduleTime) throws DataVinesServerException; String getJobExecutionConfig(Long jobId, LocalDateTime scheduleTime) throws DataVinesServerException; diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/AccessTokenServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/AccessTokenServiceImpl.java new file mode 100644 index 000000000..6ea180351 --- /dev/null +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/AccessTokenServiceImpl.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.datavines.server.repository.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import io.datavines.core.constant.DataVinesConstants; +import io.datavines.core.exception.DataVinesServerException; +import io.datavines.core.utils.TokenManager; +import io.datavines.server.api.dto.bo.token.TokenCreate; +import io.datavines.server.api.dto.bo.token.TokenUpdate; +import io.datavines.server.repository.entity.AccessToken; +import io.datavines.server.repository.mapper.AccessTokenMapper; +import io.datavines.server.repository.service.AccessTokenService; + +import io.datavines.server.utils.ContextHolder; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.List; + +@Slf4j +@Service("accessTokenService") +public class AccessTokenServiceImpl extends ServiceImpl implements AccessTokenService { + + @Resource + private TokenManager tokenManager; + + @Override + public Boolean checkTokenExist(String token) { + token = token.replace("Bearer ",""); + return getOne(new LambdaQueryWrapper().eq(AccessToken::getToken, token), false) != null; + } + + @Override + public Long create(TokenCreate tokenCreate) { + + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime expireDateTime = LocalDateTime.parse(tokenCreate.getExpireTime(), dateTimeFormatter); + LocalDateTime now = LocalDateTime.now(); + if (expireDateTime.isBefore(now)) { + throw new DataVinesServerException("expire time must after now"); + } + long secondsBetween = ChronoUnit.SECONDS.between(now, expireDateTime); + String token = tokenManager.generateToken(String.valueOf(ContextHolder.getParam(DataVinesConstants.TOKEN)), secondsBetween); + + AccessToken accessToken = new AccessToken(); + accessToken.setToken(token); + accessToken.setUserId(ContextHolder.getUserId()); + accessToken.setWorkspaceId(tokenCreate.getWorkspaceId()); + accessToken.setExpireTime(expireDateTime); + accessToken.setCreateBy(ContextHolder.getUserId()); + accessToken.setCreateTime(LocalDateTime.now()); + accessToken.setUpdateBy(ContextHolder.getUserId()); + accessToken.setUpdateTime(LocalDateTime.now()); + save(accessToken); + + return accessToken.getId(); + } + + @Override + public Long update(TokenUpdate tokenUpdate) { + AccessToken accessToken = getById(tokenUpdate.getId()); + if (accessToken == null) { + throw new DataVinesServerException("token not exist"); + } + + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime expireDateTime = LocalDateTime.parse(tokenUpdate.getExpireTime(), dateTimeFormatter); + LocalDateTime now = LocalDateTime.now(); + if (expireDateTime.isBefore(now)) { + throw new DataVinesServerException("expire time must after now"); + } + + if (!expireDateTime.isEqual(accessToken.getExpireTime())) { + long secondsBetween = ChronoUnit.SECONDS.between(now, expireDateTime); + String token = tokenManager.generateToken(String.valueOf(ContextHolder.getParam(DataVinesConstants.TOKEN)), secondsBetween); + + accessToken.setToken(token); + accessToken.setUserId(ContextHolder.getUserId()); + accessToken.setWorkspaceId(tokenUpdate.getWorkspaceId()); + accessToken.setExpireTime(expireDateTime); + accessToken.setCreateBy(ContextHolder.getUserId()); + accessToken.setCreateTime(LocalDateTime.now()); + accessToken.setUpdateBy(ContextHolder.getUserId()); + accessToken.setUpdateTime(LocalDateTime.now()); + updateById(accessToken); + } + + return accessToken.getId(); + } + + @Override + public boolean deleteToken(Long id) { + return removeById(id); + } + + @Override + public IPage page(Long workspaceId, Long userId, Integer pageNumber, Integer pageSize) { + Page page = new Page<>(pageNumber, pageSize); + return page(page, new LambdaQueryWrapper().eq(AccessToken::getWorkspaceId, workspaceId).eq(AccessToken::getUserId, userId)); + } +} diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionResultServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionResultServiceImpl.java index b72c4785e..ad92af4a7 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionResultServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionResultServiceImpl.java @@ -25,13 +25,14 @@ import io.datavines.metric.api.ExpectedValue; import io.datavines.metric.api.ResultFormula; import io.datavines.metric.api.SqlMetric; +import io.datavines.server.api.dto.vo.JobExecutionCheckResultVO; import io.datavines.server.api.dto.vo.JobExecutionResultVO; import io.datavines.server.repository.entity.Job; import io.datavines.server.repository.entity.JobExecution; import io.datavines.server.repository.entity.JobExecutionResult; import io.datavines.server.repository.service.JobService; import io.datavines.server.repository.service.JobExecutionService; -import io.datavines.server.enums.DqJobExecutionState; +import io.datavines.server.enums.JobCheckState; import io.datavines.common.enums.OperatorType; import io.datavines.server.repository.mapper.JobExecutionResultMapper; import io.datavines.server.repository.service.JobExecutionResultService; @@ -96,10 +97,29 @@ public List listByJobExecutionId(long jobExecutionId) { public List listByErrorJobExecutionId(long jobExecutionId) { return baseMapper.selectList(new QueryWrapper().lambda() .eq(JobExecutionResult::getJobExecutionId, jobExecutionId) - .eq(JobExecutionResult::getState, DqJobExecutionState.FAILURE.getCode()) + .eq(JobExecutionResult::getState, JobCheckState.FAILURE.getCode()) .orderByDesc(JobExecutionResult::getUpdateTime)); } + @Override + public int getCheckResultByJobExecutionId(long jobExecutionId) { + + int result = JobCheckState.NONE.getCode(); + List jobExecutionResultList = listByJobExecutionId(jobExecutionId); + if (CollectionUtils.isEmpty(jobExecutionResultList)) { + return result; + } + int resultState = 1; + for (JobExecutionResult executionResult : jobExecutionResultList) { + if (executionResult.getState() != 1) { + resultState = 2; + break; + } + } + + return JobCheckState.of(resultState).getCode(); + } + @Override public JobExecutionResultVO getResultVOByJobExecutionId(long jobExecutionId) { @@ -159,7 +179,7 @@ private JobExecutionResultVO generateJobExecutionResultVO(long jobExecutionId, J checkSubject += "." + jobExecutionResult.getColumnName(); } jobExecutionResultVO.setCheckSubject(checkSubject); - jobExecutionResultVO.setCheckResult(DqJobExecutionState.of(jobExecutionResult.getState()).getDescription(!LanguageUtils.isZhContext())); + jobExecutionResultVO.setCheckResult(JobCheckState.of(jobExecutionResult.getState()).getDescription(!LanguageUtils.isZhContext())); SqlMetric sqlMetric = PluginLoader.getPluginLoader(SqlMetric.class).getOrCreatePlugin(jobExecutionResult.getMetricName()); if (!"multi_table_value_comparison".equalsIgnoreCase(sqlMetric.getName())) { ExpectedValue expectedValue = PluginLoader.getPluginLoader(ExpectedValue.class).getOrCreatePlugin(jobExecution.getEngineType() + "_" + jobExecutionResult.getExpectedType()); diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java index cc8adf59f..07568a376 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobExecutionServiceImpl.java @@ -37,7 +37,7 @@ import io.datavines.server.api.dto.bo.job.JobExecutionPageParam; import io.datavines.server.api.dto.vo.*; import io.datavines.core.exception.DataVinesServerException; -import io.datavines.server.enums.DqJobExecutionState; +import io.datavines.server.enums.JobCheckState; import io.datavines.server.repository.entity.JobExecution; import io.datavines.server.repository.entity.JobExecutionResult; import io.datavines.server.repository.mapper.JobExecutionResultMapper; @@ -136,7 +136,7 @@ public IPage getJobExecutionPage(JobExecutionPageParam pageParam )); jobExecutionList.forEach(jobExecution -> { if (jobExecutionStateMap.get(jobExecution.getId()) != null) { - jobExecution.setCheckState(DqJobExecutionState.of(jobExecutionStateMap.get(jobExecution.getId()))); + jobExecution.setCheckState(JobCheckState.of(jobExecutionStateMap.get(jobExecution.getId()))); } }); } diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobQualityReportServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobQualityReportServiceImpl.java index cd88e30b4..882705e39 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobQualityReportServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobQualityReportServiceImpl.java @@ -30,7 +30,7 @@ import io.datavines.server.api.dto.bo.job.JobQualityReportDashboardParam; import io.datavines.server.api.dto.vo.*; import io.datavines.server.enums.DataQualityLevel; -import io.datavines.server.enums.DqJobExecutionState; +import io.datavines.server.enums.JobCheckState; import io.datavines.server.repository.entity.*; import io.datavines.server.repository.mapper.JobExecutionResultMapper; import io.datavines.server.repository.mapper.JobQualityReportMapper; @@ -419,7 +419,7 @@ public List listColumnExecution(Long reportId) { String resultFormulaFormat = resultFormula.getResultFormat(!LanguageUtils.isZhContext())+" ${operator} ${threshold}"; jobExecutionResultVO.setCheckSubject(jobExecutionResult.getDatabaseName() + "." + jobExecutionResult.getTableName() + "." + jobExecutionResult.getColumnName()); - jobExecutionResultVO.setCheckResult(DqJobExecutionState.of(jobExecutionResult.getState()).getDescription(!LanguageUtils.isZhContext())); + jobExecutionResultVO.setCheckResult(JobCheckState.of(jobExecutionResult.getState()).getDescription(!LanguageUtils.isZhContext())); SqlMetric sqlMetric = PluginLoader.getPluginLoader(SqlMetric.class).getOrCreatePlugin(jobExecutionResult.getMetricName()); if (!"multi_table_value_comparison".equalsIgnoreCase(sqlMetric.getName())) { ExpectedValue expectedValue = PluginLoader.getPluginLoader(ExpectedValue.class).getOrCreatePlugin(jobExecution.getEngineType() + "_" + jobExecutionResult.getExpectedType()); diff --git a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobServiceImpl.java b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobServiceImpl.java index d2af1f897..e099187b3 100644 --- a/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobServiceImpl.java +++ b/datavines-server/src/main/java/io/datavines/server/repository/service/impl/JobServiceImpl.java @@ -493,15 +493,13 @@ public IPage getJobPage(String searchVal, } @Override - public boolean execute(Long jobId, LocalDateTime scheduleTime) throws DataVinesServerException { + public Long execute(Long jobId, LocalDateTime scheduleTime) throws DataVinesServerException { Job job = baseMapper.selectById(jobId); if (job == null) { throw new DataVinesServerException(Status.JOB_NOT_EXIST_ERROR, jobId); } - executeJob(job, scheduleTime); - - return true; + return executeJob(job, scheduleTime); } @Override @@ -525,7 +523,7 @@ public String getJobExecutionConfig(Long jobId, LocalDateTime scheduleTime) thro } - private void executeJob(Job job, LocalDateTime scheduleTime) { + private Long executeJob(Job job, LocalDateTime scheduleTime) { JobExecution jobExecution = getJobExecution(job, scheduleTime); @@ -537,6 +535,8 @@ private void executeJob(Job job, LocalDateTime scheduleTime) { command.setPriority(Priority.MEDIUM); command.setJobExecutionId(jobExecution.getId()); commandService.insert(command); + + return jobExecution.getId(); } private JobExecution getJobExecution(Job job, LocalDateTime scheduleTime) { diff --git a/datavines-ui/src/component/Menu/MenuAside/index.tsx b/datavines-ui/src/component/Menu/MenuAside/index.tsx index 50502dece..7c3fff33d 100644 --- a/datavines-ui/src/component/Menu/MenuAside/index.tsx +++ b/datavines-ui/src/component/Menu/MenuAside/index.tsx @@ -41,8 +41,8 @@ const MenuAside: React.FC = ({ menus, history }) => { const { loginInfo } = useSelector((r) => r.userReducer); const intl = useIntl(); const { Render: RenderCreateSpace, show } = useAddSpace({}); - const { Render: RenderResetPassword, show1 } = useResetPassword({}); + const setActiveKeyRedirect = (redirect = false) => { const url = window.location.href; const match = menus.find((item) => { @@ -101,15 +101,15 @@ const MenuAside: React.FC = ({ menus, history }) => { justifyContent: 'space-between', }} > - {intl.formatMessage({ id: 'header_top_workspace' })} + {intl.formatMessage({id: 'header_top_workspace'})} onShowSpace(undefined)} > - - {intl.formatMessage({ id: 'common_create_btn' })} + + {intl.formatMessage({id: 'common_create_btn'})}
= ({ menus, history }) => { style={{ cursor: 'pointer', }} - onClick={()=>onShowResetPassword(loginInfo.id)} + onClick={() => onShowResetPassword(loginInfo.id)} > - - {intl.formatMessage({ id: 'common_reset_password' })} + + {intl.formatMessage({id: 'common_reset_password'})}
  • = ({ menus, history }) => { }} onClick={handleMenuClick} > - - {intl.formatMessage({ id: 'common_Logout' })} + + {intl.formatMessage({id: 'common_Logout'})}
  • ); const $munus = menus.map((item) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { exact, ...rest } = item; + const {exact, ...rest} = item; return { ...rest, style: { diff --git a/datavines-ui/src/locale/en_US.ts b/datavines-ui/src/locale/en_US.ts index f3918770d..8b2da650d 100644 --- a/datavines-ui/src/locale/en_US.ts +++ b/datavines-ui/src/locale/en_US.ts @@ -47,6 +47,7 @@ export default { '/main/detail/:id': 'Detail', '/main/label': 'Label', '/main/config': 'Config', + '/main/tokenManager': 'Token', confirm_text: 'OK', test_link: 'Test Connect', @@ -93,6 +94,7 @@ export default { jobs_list: 'Jobs', jobs_add: 'Add', jobs_add_tip: 'Please save the job first', + jobs_id: 'Jobs Id', jobs_name: 'Jobs Name', jobs_type: 'Jobs Type', jobs_updater: 'Updater', @@ -310,6 +312,11 @@ export default { config_var_value: 'Config Value', create_config: 'Create Config', + token_title: 'Token Management', + token_expire_time: 'Expire Time', + token_token: 'Token', + create_token: 'Create Token', + quality_dashboard_profile: 'Quality Profile', quality_dashboard_trend: 'Quality Trend', quality_dashboard_failure_execution: 'Fail Check', diff --git a/datavines-ui/src/locale/zh_CN.ts b/datavines-ui/src/locale/zh_CN.ts index a1fe685e6..4b9f06a69 100644 --- a/datavines-ui/src/locale/zh_CN.ts +++ b/datavines-ui/src/locale/zh_CN.ts @@ -47,6 +47,7 @@ export default { '/main/detail/:id': '详情', '/main/label': '标签管理', '/main/config': '参数管理', + '/main/tokenManager': '令牌管理', confirm_text: '确认', test_link: '测试链接', @@ -93,6 +94,7 @@ export default { jobs_list: '规则作业列表', jobs_add: '创建规则作业', jobs_add_tip: '请先保存规则作业', + jobs_id: '规则作业Id', jobs_name: '规则作业名称', jobs_type: '规则作业类型', jobs_updater: '更新人', @@ -308,6 +310,11 @@ export default { config_var_value: '参数值', create_config: '创建参数', + token_title: '令牌管理', + token_expire_time: '过期时间', + token_token: '令牌', + create_token: '创建令牌', + quality_dashboard_profile: '质量概览', quality_dashboard_trend: '质量趋势', quality_dashboard_failure_execution: '失败作业', diff --git a/datavines-ui/src/router/mainRouter.tsx b/datavines-ui/src/router/mainRouter.tsx index 319337f51..8f8d4445e 100644 --- a/datavines-ui/src/router/mainRouter.tsx +++ b/datavines-ui/src/router/mainRouter.tsx @@ -1,6 +1,6 @@ import React, { lazy } from 'react'; import { - DatabaseOutlined, WarningOutlined, UsergroupAddOutlined, CloseCircleOutlined, TagOutlined,FormOutlined + DatabaseOutlined, WarningOutlined, UsergroupAddOutlined, CloseCircleOutlined, TagOutlined,FormOutlined,SafetyCertificateOutlined } from '@ant-design/icons'; import { TRouter } from './type'; @@ -50,6 +50,12 @@ const router: TRouter = { icon: , component: lazy(() => import(/* webpackChunkName: 'view-label' */ '@/view/Main/Config')), }, + 'dv-token': { + path: '/main/tokenManager', + key: '/main/tokenManager', + icon: , + component: lazy(() => import(/* webpackChunkName: 'view-label' */ '@/view/Main/TokenManager')), + }, }; export default router; diff --git a/datavines-ui/src/type/token.ts b/datavines-ui/src/type/token.ts new file mode 100644 index 000000000..0e9f952f9 --- /dev/null +++ b/datavines-ui/src/type/token.ts @@ -0,0 +1,7 @@ +export type TTokenTableItem = { + id?: any, + expireTime?: any, + workspaceId?: any; + updateTime?: string, + token?: string, +} \ No newline at end of file diff --git a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsList.tsx b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsList.tsx index e291cd0c4..a31b11ce2 100644 --- a/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsList.tsx +++ b/datavines-ui/src/view/Main/HomeDetail/Jobs/JobsList.tsx @@ -186,6 +186,13 @@ const Jobs = ({ datasourceId }: TJobs) => { }; // @ts-ignore const columns: ColumnsType = [ + { + title: intl.formatMessage({ id: 'jobs_name' }), + dataIndex: 'id', + key: 'id', + width: 160, + render: (text: any) => defaultRender(text, 200), + }, { title: intl.formatMessage({ id: 'jobs_name' }), dataIndex: 'name', diff --git a/datavines-ui/src/view/Main/TokenManager/CreateToken.tsx b/datavines-ui/src/view/Main/TokenManager/CreateToken.tsx new file mode 100644 index 000000000..a8e9bc84c --- /dev/null +++ b/datavines-ui/src/view/Main/TokenManager/CreateToken.tsx @@ -0,0 +1,105 @@ +import React, { useRef, useState, useImperativeHandle } from 'react'; +import { + Input, ModalProps, Form, FormInstance, message, DatePicker, +} from 'antd'; +import { useIntl } from 'react-intl'; +import { + useModal, useImmutable, FormRender, IFormRender, usePersistFn, useLoading, +} from '@/common'; +import { $http } from '@/http'; +import { useSelector } from '@/store'; +import { TTokenTableItem } from "@/type/token"; +import dayjs from "dayjs"; + +type InnerProps = { + form: FormInstance, + detail?: TTokenTableItem | null, + innerRef?: any +} +export const CreateTokenComponent = ({ form, detail, innerRef }: InnerProps) => { + const intl = useIntl(); + const setBodyLoading = useLoading(); + const { workspaceId } = useSelector((r) => r.workSpaceReducer); + const schema: IFormRender = { + name: 'token-form', + layout: 'vertical', + formItemProps: { + style: { marginBottom: 10 }, + }, + meta: [ + { + label: intl.formatMessage({ id: 'token_expire_time' }), + name: 'expireTime', + initialValue: dayjs(detail?.expireTime), + rules: [ + { + required: true, + message: intl.formatMessage({ id: 'common_required_tip' }), + }, + ], + widget: , + } + ], + }; + useImperativeHandle(innerRef, () => ({ + saveUpdate(hide?: () => any) { + form.validateFields().then(async (values) => { + try { + setBodyLoading(true); + const { expireTime } = values; + console.log('expireTime:', expireTime); + const params = { + workspaceId, + expireTime: dayjs(expireTime).format('YYYY-MM-DD HH:mm:ss'), + }; + + console.log('params:', params); + + if (detail && detail.id) { + await $http.put('/token', { ...params, id: detail.id }); + } else { + await $http.post('/token', params); + } + message.success(intl.formatMessage({ id: 'common_success' })); + if (hide) { + hide(); + } + } catch (error) { + } finally { + setBodyLoading(false); + } + }).catch((err) => { + console.log(err); + }); + }, + })); + return ; +}; + +export const useCreateToken = (options: ModalProps) => { + const [form] = Form.useForm(); + const intl = useIntl(); + const innerRef = useRef(); + const [editInfo, setEditInfo] = useState(null); + const editRef = useRef(null); + editRef.current = editInfo; + + const onOk = usePersistFn(async () => { + innerRef.current.saveUpdate(hide); + }); + const { + Render, hide, show, ...rest + } = useModal({ + title: intl.formatMessage({ id: 'create_token' }), + onOk, + ...(options || {}), + }); + return { + Render: useImmutable(() => ()), + show(data: TTokenTableItem | null) { + setEditInfo(data); + show(data); + }, + ...rest, + }; +}; diff --git a/datavines-ui/src/view/Main/TokenManager/index.tsx b/datavines-ui/src/view/Main/TokenManager/index.tsx new file mode 100644 index 000000000..14941a4d7 --- /dev/null +++ b/datavines-ui/src/view/Main/TokenManager/index.tsx @@ -0,0 +1,168 @@ +import React, { useState } from 'react'; +import { + Table, Button, message, Form, +} from 'antd'; +import { ColumnsType } from 'antd/lib/table'; +import { useIntl } from 'react-intl'; +import { PlusOutlined } from '@ant-design/icons'; +import { useCreateToken } from './CreateToken'; +import { useMount, Popconfirm } from '@/common'; +import { $http } from '@/http'; +import { useSelector } from '@/store'; +import Title from "component/Title"; +import { TTokenTableItem } from "@/type/token"; + +const Index = () => { + const [loading, setLoading] = useState(false); + const intl = useIntl(); + const form = Form.useForm()[0]; + const { Render: RenderSLASModal, show } = useCreateToken({ + afterClose() { + getData(); + }, + }); + const { workspaceId } = useSelector((r) => r.workSpaceReducer); + const [tableData, setTableData] = useState<{ list: TTokenTableItem[], total: number}>({ list: [], total: 0 }); + const [pageParams, setPageParams] = useState({ + pageNumber: 1, + pageSize: 10, + }); + const onPageChange = ({ current, pageSize }: any) => { + setPageParams({ + pageNumber: current, + pageSize, + }); + getData({ + pageNumber: current, + pageSize, + }); + }; + const getData = async (values: any = null) => { + try { + setLoading(true); + const params = { + workspaceId, + ...pageParams, + ...(values || form.getFieldsValue()), + }; + const res = (await $http.get('/token/page', params)) || []; + setTableData({ + list: res?.records || [], + total: res?.total || 0, + }); + } catch (error) { + } finally { + setLoading(false); + } + }; + const onSearch = (_values: any) => { + setPageParams({ ...pageParams, pageNumber: 1 }); + getData({ + ..._values, + pageNumber: 1, + }); + }; + useMount(() => { + getData(); + }); + + const onEdit = (record: TTokenTableItem) => { + show(record); + }; + const onDelete = async (id: number) => { + try { + setLoading(true); + await $http.delete(`/token/${id}`); + getData(); + message.success(intl.formatMessage({ id: 'common_success' })); + } catch (error) { + } finally { + setLoading(false); + } + }; + const columns: ColumnsType = [ + { + title: intl.formatMessage({ id: 'token_expire_time' }), + dataIndex: 'expireTime', + key: 'expireTime', + width: 180, + render: (text: string) =>
    {text || '--'}
    , + }, + { + title: intl.formatMessage({ id: 'token_token' }), + dataIndex: 'token', + key: 'token', + render: (text: string) =>
    {text || '--'}
    , + }, + { + title: intl.formatMessage({ id: 'common_update_time' }), + dataIndex: 'updateTime', + key: 'updateTime', + width: 180, + render: (text: string) =>
    {text || '--'}
    , + }, + { + title: intl.formatMessage({ id: 'common_action' }), + fixed: 'right', + key: 'action', + dataIndex: 'action', + width: 150, + render: (text: string, record: TTokenTableItem) => ( + <> + { onEdit(record); }}>{intl.formatMessage({ id: 'common_edit' })} + onDelete(record.id)} + /> + + ), + }, + ]; + return ( + +
    + + {intl.formatMessage({ id: 'token_title' })} + +
    +
    + +
    +
    + + + loading={loading} + size="middle" + rowKey="id" + columns={columns} + dataSource={tableData.list || []} + onChange={onPageChange} + bordered + pagination={{ + size: 'small', + total: tableData.total, + showSizeChanger: true, + current: pageParams.pageNumber, + pageSize: pageParams.pageSize, + }} + /> + +
    + ); +}; + +export default Index; diff --git a/scripts/sql/datavines-mysql.sql b/scripts/sql/datavines-mysql.sql index d2e789bae..7ffbe67e7 100644 --- a/scripts/sql/datavines-mysql.sql +++ b/scripts/sql/datavines-mysql.sql @@ -447,6 +447,20 @@ CREATE TABLE `dv_env` ( UNIQUE KEY `env_name` (`name`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运行环境配置信息'; +DROP TABLE IF EXISTS `dv_access_token`; +CREATE TABLE `dv_access_token` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `workspace_id` bigint(20) NOT NULL COMMENT '工作空间ID', + `user_id` bigint(20) NOT NULL COMMENT '用户ID', + `token` varchar(1024) NOT NULL COMMENT 'token', + `expire_time` datetime NOT NULL COMMENT '过期时间', + `create_by` bigint(20) NOT NULL COMMENT '创建用户ID', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` bigint(20) NOT NULL COMMENT '更新用户ID', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='token 管理'; + -- ---------------------------- -- Table structure for dv_error_data_storage -- ----------------------------