From 7729cd47eefbcde3c24f02f7f995d739ea82e40e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:05:29 +0000 Subject: [PATCH 1/2] Migrate from Java 11 to Java 17 - Update sourceCompatibility/targetCompatibility to 17 - Upgrade Spring Boot 2.6.3 -> 2.7.18 - Upgrade Spring Dependency Management 1.0.11 -> 1.1.6 - Upgrade Netflix DGS 4.9.21 -> 5.5.1 with platform BOM - Upgrade DGS Codegen plugin 5.0.6 -> 5.6.9 - Upgrade JJWT 0.11.2 -> 0.12.6 (new API: subject/expiration/parseSignedClaims) - Upgrade MyBatis Spring Boot Starter 2.2.2 -> 2.3.2 - Upgrade SQLite JDBC 3.36.0.3 -> 3.42.0.1 - Upgrade joda-time 2.10.13 -> 2.12.7 - Upgrade Spotless 6.2.1 -> 6.25.0 - Fix DGS PageInfo type compatibility in ArticleDatafetcher and CommentDatafetcher - Use Keys.hmacShaKeyFor() for JJWT 0.12.x key handling Co-Authored-By: shayan@cognition.ai --- build.gradle | 34 +++++++++++-------- .../io/spring/graphql/ArticleDatafetcher.java | 31 ++++++++--------- .../io/spring/graphql/CommentDatafetcher.java | 23 ++++++------- .../service/DefaultJwtService.java | 15 ++++---- 4 files changed, 50 insertions(+), 53 deletions(-) diff --git a/build.gradle b/build.gradle index da384dc69..932a16ef0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ plugins { - id 'org.springframework.boot' version '2.6.3' - id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'org.springframework.boot' version '2.7.18' + id 'io.spring.dependency-management' version '1.1.6' id 'java' - id "com.netflix.dgs.codegen" version "5.0.6" - id "com.diffplug.spotless" version "6.2.1" + id "com.netflix.dgs.codegen" version "5.6.9" + id "com.diffplug.spotless" version "6.25.0" } version = '0.0.1-SNAPSHOT' -sourceCompatibility = '11' -targetCompatibility = '11' +sourceCompatibility = '17' +targetCompatibility = '17' spotless { java { @@ -24,6 +24,12 @@ repositories { mavenCentral() } +dependencyManagement { + imports { + mavenBom 'com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:5.5.1' + } +} + configurations { compileOnly { extendsFrom annotationProcessor @@ -35,14 +41,14 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-hateoas' implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2' - implementation 'com.netflix.graphql.dgs:graphql-dgs-spring-boot-starter:4.9.21' + implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.2' + implementation 'com.netflix.graphql.dgs:graphql-dgs-spring-boot-starter' implementation 'org.flywaydb:flyway-core' - implementation 'io.jsonwebtoken:jjwt-api:0.11.2' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2', - 'io.jsonwebtoken:jjwt-jackson:0.11.2' - implementation 'joda-time:joda-time:2.10.13' - implementation 'org.xerial:sqlite-jdbc:3.36.0.3' + implementation 'io.jsonwebtoken:jjwt-api:0.12.6' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6', + 'io.jsonwebtoken:jjwt-jackson:0.12.6' + implementation 'joda-time:joda-time:2.12.7' + implementation 'org.xerial:sqlite-jdbc:3.42.0.1' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' @@ -53,7 +59,7 @@ dependencies { testImplementation 'io.rest-assured:spring-mock-mvc:4.5.1' testImplementation 'org.springframework.security:spring-security-test' testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:2.2.2' + testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:2.3.2' } tasks.named('test') { diff --git a/src/main/java/io/spring/graphql/ArticleDatafetcher.java b/src/main/java/io/spring/graphql/ArticleDatafetcher.java index 37c82939a..29817371c 100644 --- a/src/main/java/io/spring/graphql/ArticleDatafetcher.java +++ b/src/main/java/io/spring/graphql/ArticleDatafetcher.java @@ -6,8 +6,7 @@ import com.netflix.graphql.dgs.DgsQuery; import com.netflix.graphql.dgs.InputArgument; import graphql.execution.DataFetcherResult; -import graphql.relay.DefaultConnectionCursor; -import graphql.relay.DefaultPageInfo; +import io.spring.graphql.types.PageInfo; import graphql.schema.DataFetchingEnvironment; import io.spring.api.exception.ResourceNotFoundException; import io.spring.application.ArticleQueryService; @@ -64,7 +63,7 @@ public DataFetcherResult getFeed( current, new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV)); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); + PageInfo pageInfo = buildArticlePageInfo(articles); ArticlesConnection articlesConnection = ArticlesConnection.newBuilder() .pageInfo(pageInfo) @@ -114,7 +113,7 @@ public DataFetcherResult userFeed( target, new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV)); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); + PageInfo pageInfo = buildArticlePageInfo(articles); ArticlesConnection articlesConnection = ArticlesConnection.newBuilder() .pageInfo(pageInfo) @@ -167,7 +166,7 @@ public DataFetcherResult userFavorites( new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); + PageInfo pageInfo = buildArticlePageInfo(articles); ArticlesConnection articlesConnection = ArticlesConnection.newBuilder() @@ -221,7 +220,7 @@ public DataFetcherResult userArticles( new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); + PageInfo pageInfo = buildArticlePageInfo(articles); ArticlesConnection articlesConnection = ArticlesConnection.newBuilder() .pageInfo(pageInfo) @@ -276,7 +275,7 @@ public DataFetcherResult getArticles( new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } - graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); + PageInfo pageInfo = buildArticlePageInfo(articles); ArticlesConnection articlesConnection = ArticlesConnection.newBuilder() .pageInfo(pageInfo) @@ -356,16 +355,14 @@ public DataFetcherResult
findArticleBySlug(@InputArgument("slug") Strin .build(); } - private DefaultPageInfo buildArticlePageInfo(CursorPager articles) { - return new DefaultPageInfo( - articles.getStartCursor() == null - ? null - : new DefaultConnectionCursor(articles.getStartCursor().toString()), - articles.getEndCursor() == null - ? null - : new DefaultConnectionCursor(articles.getEndCursor().toString()), - articles.hasPrevious(), - articles.hasNext()); + private PageInfo buildArticlePageInfo(CursorPager articles) { + return PageInfo.newBuilder() + .startCursor( + articles.getStartCursor() == null ? null : articles.getStartCursor().toString()) + .endCursor(articles.getEndCursor() == null ? null : articles.getEndCursor().toString()) + .hasPreviousPage(articles.hasPrevious()) + .hasNextPage(articles.hasNext()) + .build(); } private Article buildArticleResult(ArticleData articleData) { diff --git a/src/main/java/io/spring/graphql/CommentDatafetcher.java b/src/main/java/io/spring/graphql/CommentDatafetcher.java index 334a04c36..2862fd0c2 100644 --- a/src/main/java/io/spring/graphql/CommentDatafetcher.java +++ b/src/main/java/io/spring/graphql/CommentDatafetcher.java @@ -5,8 +5,7 @@ import com.netflix.graphql.dgs.DgsDataFetchingEnvironment; import com.netflix.graphql.dgs.InputArgument; import graphql.execution.DataFetcherResult; -import graphql.relay.DefaultConnectionCursor; -import graphql.relay.DefaultPageInfo; +import io.spring.graphql.types.PageInfo; import io.spring.application.CommentQueryService; import io.spring.application.CursorPageParameter; import io.spring.application.CursorPager; @@ -78,7 +77,7 @@ public DataFetcherResult articleComments( current, new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV)); } - graphql.relay.PageInfo pageInfo = buildCommentPageInfo(comments); + PageInfo pageInfo = buildCommentPageInfo(comments); CommentsConnection result = CommentsConnection.newBuilder() .pageInfo(pageInfo) @@ -99,16 +98,14 @@ public DataFetcherResult articleComments( .build(); } - private DefaultPageInfo buildCommentPageInfo(CursorPager comments) { - return new DefaultPageInfo( - comments.getStartCursor() == null - ? null - : new DefaultConnectionCursor(comments.getStartCursor().toString()), - comments.getEndCursor() == null - ? null - : new DefaultConnectionCursor(comments.getEndCursor().toString()), - comments.hasPrevious(), - comments.hasNext()); + private PageInfo buildCommentPageInfo(CursorPager comments) { + return PageInfo.newBuilder() + .startCursor( + comments.getStartCursor() == null ? null : comments.getStartCursor().toString()) + .endCursor(comments.getEndCursor() == null ? null : comments.getEndCursor().toString()) + .hasPreviousPage(comments.hasPrevious()) + .hasNextPage(comments.hasNext()) + .build(); } private Comment buildCommentResult(CommentData comment) { diff --git a/src/main/java/io/spring/infrastructure/service/DefaultJwtService.java b/src/main/java/io/spring/infrastructure/service/DefaultJwtService.java index 515d66106..071532380 100644 --- a/src/main/java/io/spring/infrastructure/service/DefaultJwtService.java +++ b/src/main/java/io/spring/infrastructure/service/DefaultJwtService.java @@ -3,13 +3,12 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; import io.spring.core.service.JwtService; import io.spring.core.user.User; import java.util.Date; import java.util.Optional; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -17,22 +16,20 @@ @Component public class DefaultJwtService implements JwtService { private final SecretKey signingKey; - private final SignatureAlgorithm signatureAlgorithm; private int sessionTime; @Autowired public DefaultJwtService( @Value("${jwt.secret}") String secret, @Value("${jwt.sessionTime}") int sessionTime) { this.sessionTime = sessionTime; - signatureAlgorithm = SignatureAlgorithm.HS512; - this.signingKey = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName()); + this.signingKey = Keys.hmacShaKeyFor(secret.getBytes()); } @Override public String toToken(User user) { return Jwts.builder() - .setSubject(user.getId()) - .setExpiration(expireTimeFromNow()) + .subject(user.getId()) + .expiration(expireTimeFromNow()) .signWith(signingKey) .compact(); } @@ -41,8 +38,8 @@ public String toToken(User user) { public Optional getSubFromToken(String token) { try { Jws claimsJws = - Jwts.parserBuilder().setSigningKey(signingKey).build().parseClaimsJws(token); - return Optional.ofNullable(claimsJws.getBody().getSubject()); + Jwts.parser().verifyWith(signingKey).build().parseSignedClaims(token); + return Optional.ofNullable(claimsJws.getPayload().getSubject()); } catch (Exception e) { return Optional.empty(); } From 8ccd650d3de4e346d3f97580af338ce9e727ca0f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:08:03 +0000 Subject: [PATCH 2/2] Update CI workflow to use Java 17 Co-Authored-By: shayan@cognition.ai --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 38bce4bbb..1dbc33528 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -17,11 +17,11 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: zulu - java-version: '11' + java-version: '17' - uses: actions/cache@v4 with: path: |