Skip to content

Commit

Permalink
Upgrade to Spring Boot 3.2
Browse files Browse the repository at this point in the history
  • Loading branch information
mbhave committed May 9, 2024
1 parent 975bac8 commit d65e2a3
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 137 deletions.
17 changes: 8 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ buildscript {
mavenCentral()
}
dependencies {
classpath("io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.35")
classpath("io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.41")
}
}

plugins {
id 'org.springframework.boot' version '2.7.8'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id 'org.springframework.boot' version '3.2.5'
id 'io.spring.dependency-management' version '1.1.4'
id 'java'
}

Expand All @@ -29,28 +29,27 @@ bootJar {
}

dependencies {
implementation 'org.pegdown:pegdown:1.6.0'
implementation('org.asciidoctor:asciidoctorj:2.5.7') {
implementation('org.asciidoctor:asciidoctorj:2.5.12') {
exclude group: 'org.jruby'
}
implementation 'org.jruby:jruby-complete:9.4.0.0'
implementation 'org.jsoup:jsoup:1.15.3'
implementation 'org.jruby:jruby-complete:9.4.6.0'
implementation 'org.jsoup:jsoup:1.17.1'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-hateoas'
implementation 'org.springframework.boot:spring-boot-starter-security'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'com.azure.spring:spring-cloud-azure-starter-keyvault-secrets'
implementation 'javax.xml.bind:jaxb-api:2.1'
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.2'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}

dependencyManagement {
imports {
mavenBom "com.azure.spring:spring-cloud-azure-dependencies:4.5.0"
mavenBom "com.azure.spring:spring-cloud-azure-dependencies:5.11.0"
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/spring/renderer/RendererProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import java.util.HashMap;
import java.util.Map;

import javax.validation.constraints.Pattern;
import jakarta.validation.constraints.Pattern;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/io/spring/renderer/SecurityConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

Expand All @@ -31,11 +32,11 @@ public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((request) -> {
request.mvcMatchers("/webhook/guides").permitAll();
request.requestMatchers("/webhook/guides").permitAll();
request.anyRequest().authenticated();
});
http.csrf(csrf -> csrf.ignoringAntMatchers("/webhook/**"));
http.httpBasic();
http.csrf(csrf -> csrf.ignoringRequestMatchers("/webhook/**"));
http.httpBasic(Customizer.withDefaults());
return http.build();
}

Expand Down
14 changes: 9 additions & 5 deletions src/main/java/io/spring/renderer/github/GithubClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,17 @@ public class GithubClient {
private static final String REPO_ZIPBALL_PATH = REPO_INFO_PATH + "/zipball";

private static final MediaType GITHUB_PREVIEW_TYPE = MediaType
.parseMediaType("application/vnd.github.mercy-preview+json");
.parseMediaType("application/vnd.github.mercy-preview+json");

private final RestTemplate restTemplate;

public GithubClient(RestTemplateBuilder restTemplateBuilder, RendererProperties properties) {
restTemplateBuilder = restTemplateBuilder.rootUri(API_URL_BASE)
.additionalInterceptors(new GithubAcceptInterceptor());
.additionalInterceptors(new GithubAcceptInterceptor());
if (StringUtils.hasText(properties.getGithub().getToken())) {
this.restTemplate = restTemplateBuilder
.additionalInterceptors(new GithubAppTokenInterceptor(properties.getGithub().getToken())).build();
.additionalInterceptors(new GithubAppTokenInterceptor(properties.getGithub().getToken()))
.build();
}
else {
this.logger.warn("GitHub API access will be rate-limited at 60 req/hour");
Expand Down Expand Up @@ -138,8 +139,11 @@ private Optional<String> findNextPageLink(ResponseEntity response) {
if (links == null) {
return Optional.empty();
}
return links.stream().map(NEXT_LINK_PATTERN::matcher).filter(Matcher::matches).map(matcher -> matcher.group(1))
.findFirst();
return links.stream()
.map(NEXT_LINK_PATTERN::matcher)
.filter(Matcher::matches)
.map(matcher -> matcher.group(1))
.findFirst();
}

public RateLimit fetchRateLimitInfo() {
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/io/spring/renderer/guides/GuideModelAssembler.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ class GuideModelAssembler extends RepresentationModelAssemblerSupport<GuideMetad
@Override
public GuideModel toModel(GuideMetadata guideMetadata) {
GuideModel resource = new GuideModel(guideMetadata);
resource.add(
linkTo(methodOn(GuidesController.class).showGuide(resource.getType().getSlug(), resource.getName()))
.withSelfRel());
resource.add(
linkTo(methodOn(GuidesController.class).renderGuide(resource.getType().getSlug(), resource.getName()))
.withRel("content"));
resource
.add(linkTo(methodOn(GuidesController.class).showGuide(resource.getType().getSlug(), resource.getName()))
.withSelfRel());
resource
.add(linkTo(methodOn(GuidesController.class).renderGuide(resource.getType().getSlug(), resource.getName()))
.withRel("content"));
resource.add(linkTo(methodOn(GuidesController.class).listGuides()).withRel("guides"));
return resource;
}
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/io/spring/renderer/guides/GuideType.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,17 @@ enum GuideType {
}

public static GuideType fromSlug(String slug) {
return Arrays.stream(GuideType.values()).filter(type -> type.getSlug().equals(slug)).findFirst()
.orElse(GuideType.UNKNOWN);
return Arrays.stream(GuideType.values())
.filter(type -> type.getSlug().equals(slug))
.findFirst()
.orElse(GuideType.UNKNOWN);
}

public static GuideType fromRepositoryName(String repositoryName) {
return Arrays.stream(GuideType.values()).filter(type -> repositoryName.startsWith(type.getPrefix())).findFirst()
.orElse(GuideType.UNKNOWN);
return Arrays.stream(GuideType.values())
.filter(type -> repositoryName.startsWith(type.getPrefix()))
.findFirst()
.orElse(GuideType.UNKNOWN);
}

public String stripPrefix(String repositoryName) {
Expand Down
18 changes: 11 additions & 7 deletions src/main/java/io/spring/renderer/guides/GuidesController.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,20 @@ public ResponseEntity resourceNotFound() {
@GetMapping("")
public CollectionModel<GuideModel> listGuides() {
List<Repository> repositories = this.githubClient
.fetchOrgRepositories(this.properties.getGithub().getOrganization());
.fetchOrgRepositories(this.properties.getGithub().getOrganization());
List<GuideMetadata> guideMetadataList = repositories.stream()
.map((repository) -> new GuideMetadata(repository, getAcademyUrl(repository))).toList();
List<GuideModel> guideModels = this.guideAssembler.toCollectionModel(guideMetadataList).getContent().stream()
.filter(guide -> !guide.getType().equals(GuideType.UNKNOWN)).collect(Collectors.toList());
.map((repository) -> new GuideMetadata(repository, getAcademyUrl(repository)))
.toList();
List<GuideModel> guideModels = this.guideAssembler.toCollectionModel(guideMetadataList)
.getContent()
.stream()
.filter(guide -> !guide.getType().equals(GuideType.UNKNOWN))
.collect(Collectors.toList());
CollectionModel<GuideModel> resources = CollectionModel.of(guideModels);
for (GuideType type : GuideType.values()) {
if (!GuideType.UNKNOWN.equals(type)) {
resources.add(linkTo(methodOn(GuidesController.class).showGuide(type.getSlug(), null))
.withRel(type.getSlug()));
.withRel(type.getSlug()));
}
}
return resources;
Expand Down Expand Up @@ -109,9 +113,9 @@ public ResponseEntity<GuideContentModel> renderGuide(@PathVariable String type,
}
GuideContentModel guideContentModel = this.guideRenderer.render(guideType, guide);
guideContentModel
.add(linkTo(methodOn(GuidesController.class).renderGuide(guideType.getSlug(), guide)).withSelfRel());
.add(linkTo(methodOn(GuidesController.class).renderGuide(guideType.getSlug(), guide)).withSelfRel());
guideContentModel
.add(linkTo(methodOn(GuidesController.class).showGuide(guideType.getSlug(), guide)).withRel("guide"));
.add(linkTo(methodOn(GuidesController.class).showGuide(guideType.getSlug(), guide)).withRel("guide"));
return ResponseEntity.ok(guideContentModel);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@ public void contribute(GuideContentModel guideContent, File repositoryRoot) {
try {
Attributes attributes = Attributes.builder().allowUriRead(true).skipFrontMatter(true).build();
File readmeAdocFile = new File(repositoryRoot.getAbsolutePath() + File.separator + README_FILENAME);
Options options = Options.builder().safe(SafeMode.SAFE).baseDir(repositoryRoot).headerFooter(true)
.attributes(attributes).build();
Options options = Options.builder()
.safe(SafeMode.SAFE)
.baseDir(repositoryRoot)
.headerFooter(true)
.attributes(attributes)
.build();
StringWriter writer = new StringWriter();
this.asciidoctor.convert(new FileReader(readmeAdocFile), writer, options);
Document doc = Jsoup.parse(writer.toString());
Expand All @@ -75,10 +79,15 @@ public void contribute(GuideContentModel guideContent, File repositoryRoot) {
private String findTableOfContents(Document doc) {
Elements toc = doc.select("div#toc > ul.sectlevel1");
toc.select("ul.sectlevel2").forEach(Node::remove);
toc.forEach(part -> part
.select("a[href]").stream().filter(anchor -> doc.select(anchor.attr("href")).get(0).parent()
.classNames().stream().anyMatch(clazz -> clazz.startsWith("reveal")))
.forEach(href -> href.parent().remove()));
toc.forEach(part -> part.select("a[href]")
.stream()
.filter(anchor -> doc.select(anchor.attr("href"))
.get(0)
.parent()
.classNames()
.stream()
.anyMatch(clazz -> clazz.startsWith("reveal")))
.forEach(href -> href.parent().remove()));
return toc.toString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ class GithubActionsService {

void triggerRespositoryDispatch(String org, String repo, String token) {
RequestEntity<String> entity = RequestEntity.post(DISPATCH_PATH_TEMPLATE, org, repo)
.header("Authorization", "Bearer " + token).header("Accept", ACCEPT_HEADER)
.body("{\"event_type\": \"guides\"}");
.header("Authorization", "Bearer " + token)
.header("Accept", ACCEPT_HEADER)
.body("{\"event_type\": \"guides\"}");
try {
this.restTemplate.exchange(entity, Void.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.spring.renderer.RendererProperties;
import jakarta.xml.bind.DatatypeConverter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

Expand Down
60 changes: 34 additions & 26 deletions src/test/java/io/spring/renderer/github/GithubClientTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
public class GithubClientTests {

private static final MediaType GITHUB_PREVIEW = MediaType
.parseMediaType("application/vnd.github.mercy-preview+json");
.parseMediaType("application/vnd.github.mercy-preview+json");

private static final MediaType APPLICATION_ZIP = MediaType.parseMediaType("application/zip");

Expand All @@ -61,9 +61,10 @@ void downloadRepositoryInfo() {
String repo = "gs-rest-service";
String expectedUrl = String.format("/repos/%s/%s", org, repo);
String authorization = getAuthorizationHeader();
this.server.expect(requestTo(expectedUrl)).andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("gs-rest-service.json"), GITHUB_PREVIEW));
this.server.expect(requestTo(expectedUrl))
.andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("gs-rest-service.json"), GITHUB_PREVIEW));
Repository repository = this.client.fetchOrgRepository(org, repo);
assertThat(repository).extracting("name").isEqualTo("gs-rest-service");
}
Expand All @@ -74,13 +75,15 @@ void downloadRepositoryInfoRedirected() {
String repo = "gs-redirected";
String expectedUrl = String.format("/repos/%s/%s", org, repo);
String authorization = getAuthorizationHeader();
this.server.expect(requestTo(expectedUrl)).andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("gs-rest-service.json"), GITHUB_PREVIEW));
this.server.expect(requestTo(expectedUrl))
.andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("gs-rest-service.json"), GITHUB_PREVIEW));
assertThatThrownBy(() -> this.client.fetchOrgRepository(org, repo))
.isInstanceOf(GithubResourceNotFoundException.class)
.hasMessage("Could not find github repository [spring-guides/gs-redirected]").getCause()
.hasMessage("Repository [gs-redirected] redirected to [spring-guides/gs-rest-service]");
.isInstanceOf(GithubResourceNotFoundException.class)
.hasMessage("Could not find github repository [spring-guides/gs-redirected]")
.getCause()
.hasMessage("Repository [gs-redirected] redirected to [spring-guides/gs-rest-service]");
}

@Test
Expand All @@ -89,9 +92,10 @@ void downloadRepositoryAsZipBall() throws Exception {
String repo = "gs-rest-service";
String expectedUrl = String.format("/repos/%s/%s/zipball", org, repo);
String authorization = getAuthorizationHeader();
this.server.expect(requestTo(expectedUrl)).andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("gs-rest-service.zip"), APPLICATION_ZIP));
this.server.expect(requestTo(expectedUrl))
.andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("gs-rest-service.zip"), APPLICATION_ZIP));
byte[] result = this.client.downloadRepositoryAsZipball(org, repo);
ClassPathResource resource = getClassPathResource("gs-rest-service.zip");
assertThat(result).isEqualTo(StreamUtils.copyToByteArray(resource.getInputStream()));
Expand All @@ -106,28 +110,32 @@ void fetchRepositoriesMultiplePages() {
"<https://api.github.com/organizations/4161866/repos?per_page=100&page=2>; rel=\"next\","
+ " <https://api.github.com/organizations/4161866/repos?per_page=100&page=2>; rel=\"last\"");
this.server.expect(requestTo("/orgs/spring-guides/repos?per_page=100"))
.andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString())).andRespond(
withSuccess(getClassPathResource("spring-guides-repos-page1.json"), MediaType.APPLICATION_JSON)
.headers(firstPageHeaders));
.andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("spring-guides-repos-page1.json"), MediaType.APPLICATION_JSON)
.headers(firstPageHeaders));
this.server.expect(requestTo("/organizations/4161866/repos?per_page=100&page=2"))
.andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString())).andRespond(withSuccess(
getClassPathResource("spring-guides-repos-page2.json"), MediaType.APPLICATION_JSON));
.andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(
withSuccess(getClassPathResource("spring-guides-repos-page2.json"), MediaType.APPLICATION_JSON));

List<Repository> repositories = this.client.fetchOrgRepositories(org);
Assertions.assertThat(repositories).hasSize(5).extracting("name").containsExactlyInAnyOrder("gs-rest-service",
"gs-scheduling-tasks", "gs-consuming-rest", "gs-relational-data-access",
"deprecate-gs-device-detection");
Assertions.assertThat(repositories)
.hasSize(5)
.extracting("name")
.containsExactlyInAnyOrder("gs-rest-service", "gs-scheduling-tasks", "gs-consuming-rest",
"gs-relational-data-access", "deprecate-gs-device-detection");

}

@Test
void fetchRateLimitInformation() {
String authorization = getAuthorizationHeader();
this.server.expect(requestTo("/rate_limit")).andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("rate_limit.json"), MediaType.APPLICATION_JSON));
this.server.expect(requestTo("/rate_limit"))
.andExpect(header(HttpHeaders.AUTHORIZATION, authorization))
.andExpect(header(HttpHeaders.ACCEPT, GITHUB_PREVIEW.toString()))
.andRespond(withSuccess(getClassPathResource("rate_limit.json"), MediaType.APPLICATION_JSON));

RateLimit rateLimit = this.client.fetchRateLimitInfo();
assertThat(rateLimit.getLimit()).isEqualTo(60);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public void setup() {
@Test
public void renderAsciidoctorContent() throws Exception {
given(this.githubClient.downloadRepositoryAsZipball("spring-guides", "gs-sample"))
.willReturn(readAsBytes("gs-sample.zip"));
.willReturn(readAsBytes("gs-sample.zip"));
GuideContentModel result = this.renderer.render(GuideType.GETTING_STARTED, "sample");
assertThat(result.getName()).isEqualTo("sample");
assertThat(result.getContent()).contains("<p>This is a sample guide.</p>")
.contains("<!-- rendered by Sagan Renderer Service -->");
.contains("<!-- rendered by Sagan Renderer Service -->");
assertThat(result.getTableOfContents())
.contains("<li><a href=\"#_sample_guide_title\">Sample Guide title</a></li>");
.contains("<li><a href=\"#_sample_guide_title\">Sample Guide title</a></li>");
}

private byte[] readAsBytes(String path) throws IOException {
Expand Down
Loading

0 comments on commit d65e2a3

Please sign in to comment.