Skip to content

Commit

Permalink
Feature/init commit (#1)
Browse files Browse the repository at this point in the history
* add pipeline and env variable for deployment

* update description and validation

* change job_history order query

* fix job history query

* test rename 17

* test

* fix issue custom client_request join query when job_executor data is empty
update ddl schema for cron expression length
fix http method validator validation
fix missing @PathVariable annotation
remove unused r2dbc function
fix issue update job executor and missmatch error checking on validation
add integration and unit test
update pom for sonar coverage

* fix column length

* add manual trace id configuration on web client

* add github action

---------

Co-authored-by: pramuditya <[email protected]>
Co-authored-by: Dhody Rahmad Hidayat <[email protected]>
  • Loading branch information
3 people authored Dec 9, 2023
1 parent c2e9551 commit 2783f6c
Show file tree
Hide file tree
Showing 27 changed files with 248 additions and 44 deletions.
12 changes: 11 additions & 1 deletion .docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
FROM eclipse-temurin:17-jre-alpine

MAINTAINER nantaaditya

LABEL CREATOR=nantaaditya
LABEL PROJECT_NAME=cron-scheduler
LABEL JDK_VERSION=17
LABEL JDK_DISTRIBUTION=eclipse-temurin
LABEL JDK_BUILD_VERSION=17-jre-alpine
LABEL SPRING_VERSION=3.1.5

ENV TZ=Asia/Jakarta
ENV JAVA_OPTS='-Xms256m -Xmx512m'

RUN mkdir app
RUN mkdir app/logs
Expand All @@ -9,4 +19,4 @@ ADD target/*.jar app/app.jar

WORKDIR /app

ENTRYPOINT [ "java","-Xms256m","-Xmx512m","-jar","app.jar" ]
ENTRYPOINT exec java -server -XX:+UnlockExperimentalVMOptions $JAVA_OPTS -jar app.jar
11 changes: 11 additions & 0 deletions .docker/Dockerfile-GraalVM
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ RUN --mount=type=cache,target=${MAVEN_CONFIG} mvn -X -Pnative native:compile
# The deployment Image
FROM docker.io/oraclelinux:9-slim

MAINTAINER nantaaditya

LABEL CREATOR=nantaaditya
LABEL PROJECT_NAME=cron-scheduler
LABEL JDK_VERSION=17
LABEL JDK_DISTRIBUTION=eclipse-temurin
LABEL JDK_BUILD_VERSION=17-jre-alpine
LABEL SPRING_VERSION=3.1.5

ENV TZ=Asia/Jakarta

RUN mkdir app
RUN mkdir app/logs

Expand Down
9 changes: 9 additions & 0 deletions .docker/Dockerfile-Openj9
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
FROM cobaltinc/openj9-17.0.3_7-jre-alpine

MAINTAINER nantaaditya

LABEL CREATOR=nantaaditya
LABEL PROJECT_NAME=cron-scheduler
LABEL JDK_VERSION=17
LABEL JDK_DISTRIBUTION=eclipse-temurin
LABEL JDK_BUILD_VERSION=17-jre-alpine
LABEL SPRING_VERSION=3.1.5

ENV TZ=Asia/Jakarta
ENV JAVA_OPTS='-Xms256m -Xmx512m -XX:+IgnoreUnrecognizedVMOptions -XX:+UseContainerSupport -XX:+IdleTuningCompactOnIdle -XX:+IdleTuningGcOnIdle -Xtune:virtualized'

Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/build-push-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Build & Push Docker Image

on:
push:
tags:
- 'v.*'

jobs:
docker_build_push:
runs-on: ubuntu-latest
name: docker_build
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build project with Maven
run: mvn install -DskipTests
- name: Build & push Docker image
uses: mr-smithers-excellent/docker-build-push@v6
with:
image: nantaaditya/cron-scheduler
registry: docker.io
dockerfile: .docker/Dockerfile
multiPlatform: true
platform: linux/amd64,linux/arm64,linux/arm/v7
addLatest: true
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
19 changes: 19 additions & 0 deletions .github/workflows/maven-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Maven Build

on: [push]

jobs:
maven_build:
runs-on: ubuntu-latest
name: maven_build
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build project with Maven
run: mvn verify
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Application Configuration Properties
| DB_USER | user | application db username credential |
| DB_PASS | password | application db password credential |
| LOG_NAME | cron-scheduler.log | application log name |
| APP_LOG_LEVEL | INFO | application log level |
| MAX_LOG_HISTORY | 14 | max application log history |
| CONNECT_TIME_OUT | 10 | web client job connect time out |
| RESPONSE_TIME_OUT | 10 | web client job response time out |
Expand All @@ -33,7 +34,9 @@ Application Configuration Properties
| WEB_CLIENT_JOB_LOG_LEVEL | DEBUG | web client job log level |

## Swagger URL
swagger url format `http://HOST:PORT/swagger-ui.html`, <b>[Swagger URL](http://localhost:1000/swagger-ui.html)</b>
```shell
http://localhost:{server_port}/swagger-ui.html
```

## How to Use
- Create Client Request
Expand Down
13 changes: 13 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
Expand Down Expand Up @@ -69,6 +73,15 @@
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core-micrometer</artifactId>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-spring-webflux</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.nantaaditya.cronscheduler.configuration;

import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.aop.ObservedAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ObservationConfiguration {

@Bean
public ObservationRegistry observationRegistry() {
return ObservationRegistry.create();
}

@Bean
public ObservedAspect observedAspect(ObservationRegistry observationRegistry) {
return new ObservedAspect(observationRegistry);
}

@Bean
public Observation observation(ObservationRegistry observationRegistry) {
return Observation.start("cron-scheduler", observationRegistry);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.nantaaditya.cronscheduler.entity;

import com.github.f4b6a3.tsid.TsidCreator;
import com.nantaaditya.cronscheduler.util.IdGenerator;
import java.time.LocalDate;
import java.time.LocalTime;
import lombok.AllArgsConstructor;
Expand All @@ -12,6 +12,7 @@
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.Version;

@Data
Expand Down Expand Up @@ -46,7 +47,8 @@ public class BaseEntity {
@Version
private long version;

@Transient
public static String generateId() {
return TsidCreator.getTsid1024().toLowerCase();
return IdGenerator.createId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public JobExecutor getJobExecutor(String jobExecutorId) {
.filter(je -> je.getId().equals(jobExecutorId))
.findFirst()
.orElseThrow(() -> new InvalidParameterException(
Map.of("jobExecutorId", List.of("NotExists")), "invalid parameter"
Map.of("jobExecutorId", List.of("NotExists")), "invalid parameter"
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
import com.nantaaditya.cronscheduler.repository.JobHistoryDetailRepository;
import com.nantaaditya.cronscheduler.repository.JobHistoryRepository;
import com.nantaaditya.cronscheduler.service.NotificationCallback;
import com.nantaaditya.cronscheduler.util.IdGenerator;
import com.nantaaditya.cronscheduler.util.JsonHelper;
import io.netty.channel.ChannelOption;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import io.r2dbc.postgresql.codec.Json;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import lombok.SneakyThrows;
Expand All @@ -28,6 +30,7 @@
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Component;
Expand All @@ -45,6 +48,10 @@ public class WebClientJob implements Job {

public static final String WEB_CLIENT_JOB_GROUP = "WebClientJobGroup";
public static final String INSTANT_WEB_CLIENT_JOB_GROUP = "InstantWebClientJobGroup";
public static final String TRACE_ID_HEADER = "X-B3-TraceId";
public static final String PARENT_TRACE_ID_HEADER = "X-B3-ParentSpanId";
public static final String SPAN_ID_HEADER = "X-B3-SpanId";
public static final String SAMPLED_HEADER = "X-B3-Sampled";

private WebClient webClient;

Expand Down Expand Up @@ -144,11 +151,21 @@ private WebClient createWebClient(ClientRequest clientRequest) {

return WebClient.builder()
.baseUrl(clientRequest.getBaseUrl())
.defaultHeaders(headers -> headers.putAll(clientRequest.getHeaders()))
.defaultHeaders(headers -> composeHttpHeaders(headers, clientRequest.getHeaders()))
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}

private void composeHttpHeaders(HttpHeaders httpHeaders, Map<String, List<String>> headers) {
String traceId = IdGenerator.createId();

httpHeaders.putAll(headers);
httpHeaders.put(TRACE_ID_HEADER, List.of(traceId));
httpHeaders.put(PARENT_TRACE_ID_HEADER, List.of(traceId));
httpHeaders.put(SPAN_ID_HEADER, List.of(IdGenerator.createId()));
httpHeaders.put(SAMPLED_HEADER, List.of("1"));
}

@SneakyThrows
private ClientRequest getClientRequest(String json) {
Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ public class JobExecutorServiceImpl implements JobExecutorService {
@Override
public Mono<JobExecutorResponseDTO> create(CreateJobExecutorRequestDTO request) {
return Mono.zip(
clientRequestRepository.existsById(request.getClientId()),
jobExecutorRepository.existsByJobName(request.getJobName()),
Tuples::of
)
.handle((tuples, sink) -> {
if (tuples.getT1().booleanValue() && !tuples.getT2().booleanValue()) {
sink.next(request);
} else {
Map<String, List<String>> errors = new HashMap<>();
if (!tuples.getT1().booleanValue())
errors.put("clientId", List.of(NOT_EXISTS));
if (tuples.getT2().booleanValue())
errors.put("jobName", List.of(ALREADY_EXISTS));
clientRequestRepository.existsById(request.getClientId()),
jobExecutorRepository.existsByJobName(request.getJobName()),
Tuples::of
)
.handle((tuples, sink) -> {
if (tuples.getT1().booleanValue() && !tuples.getT2().booleanValue()) {
sink.next(request);
} else {
Map<String, List<String>> errors = new HashMap<>();
if (!tuples.getT1().booleanValue())
errors.put("clientId", List.of(NOT_EXISTS));
if (tuples.getT2().booleanValue())
errors.put("jobName", List.of(ALREADY_EXISTS));

sink.error(new InvalidParameterException(errors, INVALID_PARAMETER));
}
Expand Down Expand Up @@ -93,13 +93,13 @@ public Mono<JobExecutorResponseDTO> update(UpdateJobExecutorRequestDTO request)
if (!tuples.getT2().booleanValue())
errors.put("jobExecutorId", List.of(NOT_EXISTS));

sink.error(new InvalidParameterException(errors, INVALID_PARAMETER));
}
})
.flatMap(r -> customClientRequestRepository.findClientRequestAndJobDetailsByExecutorId(request.getJobExecutorId()))
.flatMap(tuples -> update(tuples, request))
.doOnNext(tuples -> quartzUtil.updateJob(tuples.getT1()))
.map(result -> JobExecutorResponseDTO.of(result.getT1(), result.getT2()));
sink.error(new InvalidParameterException(errors, INVALID_PARAMETER));
}
})
.flatMap(r -> customClientRequestRepository.findClientRequestAndJobDetailsByExecutorId(request.getJobExecutorId()))
.flatMap(tuples -> update(tuples, request))
.doOnNext(tuples -> quartzUtil.updateJob(tuples.getT1()))
.map(result -> JobExecutorResponseDTO.of(result.getT1(), result.getT2()));
}

private Mono<Tuple2<JobExecutor, ClientRequest>> update(ClientRequest clientRequest,
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/nantaaditya/cronscheduler/util/IdGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.nantaaditya.cronscheduler.util;

import com.github.f4b6a3.tsid.TsidCreator;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IdGenerator {

public static String createId() {
return TsidCreator.getTsid256().toLowerCase();
}
}
7 changes: 2 additions & 5 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ spring:
password: ${DB_PASS:password}

logging:
# logging.pattern.level: '%5p [${spring.application.name:},%mdc{traceId:-},%mdc{spanId:-}]'
pattern.level: '%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-},%X{traceFlags}]'
level:
ROOT: INFO
ROOT: ${APP_LOG_LEVEL:INFO}
com.nantaaditya: DEBUG
org.zalando.logbook: TRACE
reactor.netty.http.client: ${WEB_CLIENT_JOB_LOG_LEVEL:DEBUG}
Expand All @@ -31,8 +31,5 @@ management:
metrics.distribution.percentiles-histogram:
http.server.requests: true
tracing:
# enabled: true
# propagation.type: b3
# brave.span-joining-supported: true
sampling.probability: 1.0

2 changes: 1 addition & 1 deletion src/main/resources/ddl.sql
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ create table if not exists job_executor
job_name varchar(255),
job_group varchar(50),
job_data jsonb,
trigger_cron varchar(15),
trigger_cron varchar(255),
active boolean,
primary key (id)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;

import com.nantaaditya.cronscheduler.configuration.QuartzInitializerConfiguration;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,10 @@ void run() {
.getResponseBody()
.doOnNext(result -> log.info("#RESPONSE - {}", result))
.subscribe();

await("running web client job")
.pollDelay(Duration.ofSeconds(3))
.timeout(Duration.ofSeconds(5))
.pollDelay(Duration.ofSeconds(5))
.timeout(Duration.ofSeconds(10))
.until(() -> Boolean.TRUE);
}

Expand Down
Loading

0 comments on commit 2783f6c

Please sign in to comment.