From 73fc7b88d4a4512e5a5763f15d6b7ea0b43db612 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:42:21 +0000 Subject: [PATCH 1/2] chore: migrate to Java 17 with updated Spring Boot and dependencies - Update Java source/target compatibility from 11 to 17 - Upgrade Spring Boot from 2.6.3 to 2.7.18 - Upgrade Netflix DGS codegen plugin from 5.0.6 to 6.0.3 - Upgrade Netflix DGS from 4.9.21 to 5.5.1 - Upgrade MyBatis from 2.2.2 to 2.3.2 - Upgrade JJWT from 0.11.2 to 0.12.5 - Upgrade Joda-Time from 2.10.13 to 2.12.7 - Upgrade SQLite JDBC from 3.36.0.3 to 3.45.0.0 - Upgrade REST Assured from 4.5.1 to 5.4.0 - Update CI pipeline to use JDK 17 Co-Authored-By: Vedant Khanna --- .github/workflows/gradle.yml | 4 ++-- build.gradle | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 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: | diff --git a/build.gradle b/build.gradle index da384dc69..12fb90812 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ plugins { - id 'org.springframework.boot' version '2.6.3' + id 'org.springframework.boot' version '2.7.18' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' - id "com.netflix.dgs.codegen" version "5.0.6" + id "com.netflix.dgs.codegen" version "6.0.3" id "com.diffplug.spotless" version "6.2.1" } version = '0.0.1-SNAPSHOT' -sourceCompatibility = '11' -targetCompatibility = '11' +sourceCompatibility = '17' +targetCompatibility = '17' spotless { java { @@ -35,25 +35,25 @@ 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:5.5.1' 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.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5', + 'io.jsonwebtoken:jjwt-jackson:0.12.5' + implementation 'joda-time:joda-time:2.12.7' + implementation 'org.xerial:sqlite-jdbc:3.45.0.0' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' - testImplementation 'io.rest-assured:rest-assured:4.5.1' - testImplementation 'io.rest-assured:json-path:4.5.1' - testImplementation 'io.rest-assured:xml-path:4.5.1' - testImplementation 'io.rest-assured:spring-mock-mvc:4.5.1' + testImplementation 'io.rest-assured:rest-assured:5.4.0' + testImplementation 'io.rest-assured:json-path:5.4.0' + testImplementation 'io.rest-assured:xml-path:5.4.0' + testImplementation 'io.rest-assured:spring-mock-mvc:5.4.0' 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') { From 77554e62664fc7119ba017a18723d9df3e289435 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:48:42 +0000 Subject: [PATCH 2/2] fix: resolve compilation and runtime errors from dependency upgrades - Fix PageInfo type mismatch: use DGS-generated PageInfo instead of graphql.relay.DefaultPageInfo - Fix JJWT 0.12.x API changes: update to new builder/parser methods - Fix JJWT key handling: use Keys.hmacShaKeyFor() for proper key size detection - Fix graphql-java version: force 19.2 for federation-graphql-java-support compatibility - Fix REST Assured version: add explicit rest-assured-common and spring-commons 5.4.0 Co-Authored-By: Vedant Khanna --- build.gradle | 3 ++ .../io/spring/graphql/ArticleDatafetcher.java | 31 +++++++++---------- .../io/spring/graphql/CommentDatafetcher.java | 23 ++++++-------- .../service/DefaultJwtService.java | 15 ++++----- 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/build.gradle b/build.gradle index 12fb90812..cdd326e6d 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.2' implementation 'com.netflix.graphql.dgs:graphql-dgs-spring-boot-starter:5.5.1' + implementation 'com.graphql-java:graphql-java:19.2' implementation 'org.flywaydb:flyway-core' implementation 'io.jsonwebtoken:jjwt-api:0.12.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5', @@ -48,9 +49,11 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'io.rest-assured:rest-assured:5.4.0' + testImplementation 'io.rest-assured:rest-assured-common:5.4.0' testImplementation 'io.rest-assured:json-path:5.4.0' testImplementation 'io.rest-assured:xml-path:5.4.0' testImplementation 'io.rest-assured:spring-mock-mvc:5.4.0' + testImplementation 'io.rest-assured:spring-commons:5.4.0' 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.3.2' 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(); }