Update spring.io metadata strategy to use the new API

Prior to this commit, the Spring Boot metadata reader would use a legacy
API endpoint on spring.io to fetch the Spring Boot versions.
The current website is about to be retired for a different
infrastructure and we should take this opportunity to update the
endpoint we're using.

This commit updates the metadata reader strategy to now use the
`https://spring.io/api/projects/spring-boot/releases` endpoint to fetch
the relevant information. The JSON format itself changed slightly and
this commit adapts to the new format as well.

Closes gh-1369
This commit is contained in:
Brian Clozel
2023-01-18 15:02:42 +01:00
parent d9fb4b05b4
commit c1e222901d
15 changed files with 341 additions and 316 deletions

View File

@@ -59,7 +59,7 @@ class SpringBootMetadataReader {
* @return the versions
*/
List<DefaultMetadataElement> getBootVersions() {
ArrayNode releases = (ArrayNode) this.content.get("projectReleases");
ArrayNode releases = (ArrayNode) this.content.get("_embedded").get("releases");
List<DefaultMetadataElement> list = new ArrayList<>();
for (JsonNode node : releases) {
DefaultMetadataElement versionMetadata = parseVersionMetadata(node);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,15 +35,15 @@ import org.springframework.web.client.RestTemplate;
* @author Stephane Nicoll
* @see Env#getSpringBootMetadataUrl()
*/
public class SaganInitializrMetadataUpdateStrategy implements InitializrMetadataUpdateStrategy {
public class SpringIoInitializrMetadataUpdateStrategy implements InitializrMetadataUpdateStrategy {
private static final Log logger = LogFactory.getLog(SaganInitializrMetadataUpdateStrategy.class);
private static final Log logger = LogFactory.getLog(SpringIoInitializrMetadataUpdateStrategy.class);
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public SaganInitializrMetadataUpdateStrategy(RestTemplate restTemplate, ObjectMapper objectMapper) {
public SpringIoInitializrMetadataUpdateStrategy(RestTemplate restTemplate, ObjectMapper objectMapper) {
this.restTemplate = restTemplate;
this.objectMapper = objectMapper;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,28 +52,30 @@ class SpringBootMetadataReaderTests {
@Test
void readAvailableVersions() throws IOException {
this.server.expect(requestTo("https://spring.io/project_metadata/spring-boot")).andRespond(
withSuccess(new ClassPathResource("metadata/sagan/spring-boot.json"), MediaType.APPLICATION_JSON));
this.server.expect(requestTo("https://spring.io/api/projects/spring-boot/releases")).andRespond(
withSuccess(new ClassPathResource("metadata/springio/spring-boot.json"), MediaType.APPLICATION_JSON));
List<DefaultMetadataElement> versions = new SpringBootMetadataReader(this.objectMapper, this.restTemplate,
this.metadata.getConfiguration().getEnv().getSpringBootMetadataUrl()).getBootVersions();
assertThat(versions).hasSize(5);
assertSpringBootVersion(versions.get(0), "2.5.0-M1", "2.5.0 (M1)", false);
assertSpringBootVersion(versions.get(1), "2.4.1-SNAPSHOT", "2.4.1 (SNAPSHOT)", false);
assertSpringBootVersion(versions.get(2), "2.4.0", "2.4.0", true);
assertSpringBootVersion(versions.get(3), "2.3.8.BUILD-SNAPSHOT", "2.3.8 (SNAPSHOT)", false);
assertSpringBootVersion(versions.get(4), "2.3.7.RELEASE", "2.3.7", false);
assertThat(versions).hasSize(7);
assertSpringBootVersion(versions.get(0), "3.0.2-SNAPSHOT", "3.0.2 (SNAPSHOT)", false);
assertSpringBootVersion(versions.get(1), "3.0.1", "3.0.1", true);
assertSpringBootVersion(versions.get(2), "2.7.8-SNAPSHOT", "2.7.8 (SNAPSHOT)", false);
assertSpringBootVersion(versions.get(3), "2.7.7", "2.7.7", false);
assertSpringBootVersion(versions.get(4), "2.6.14", "2.6.14", false);
assertSpringBootVersion(versions.get(5), "2.5.14", "2.5.14", false);
assertSpringBootVersion(versions.get(6), "2.4.13", "2.4.13", false);
this.server.verify();
}
@Test
void readAvailableVersionsWithInvalidVersion() throws IOException {
this.server.expect(requestTo("https://spring.io/project_metadata/spring-boot")).andRespond(withSuccess(
new ClassPathResource("metadata/sagan/spring-boot-invalid-version.json"), MediaType.APPLICATION_JSON));
this.server.expect(requestTo("https://spring.io/api/projects/spring-boot/releases"))
.andRespond(withSuccess(new ClassPathResource("metadata/springio/spring-boot-invalid-version.json"),
MediaType.APPLICATION_JSON));
List<DefaultMetadataElement> versions = new SpringBootMetadataReader(this.objectMapper, this.restTemplate,
this.metadata.getConfiguration().getEnv().getSpringBootMetadataUrl()).getBootVersions();
assertThat(versions).hasSize(2);
assertSpringBootVersion(versions.get(0), "2.5.0-M1", "2.5.0 (M1)", false);
assertSpringBootVersion(versions.get(1), "2.4.0", "2.4.0", true);
assertThat(versions).hasSize(1);
assertSpringBootVersion(versions.get(0), "3.0.1", "3.0.1", true);
this.server.verify();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,11 +39,11 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers.
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
/**
* Tests for {@link SaganInitializrMetadataUpdateStrategy}.
* Tests for {@link SpringIoInitializrMetadataUpdateStrategy}.
*
* @author Stephane Nicoll
*/
class SaganInitializrMetadataUpdateStrategyTests {
class SpringIoInitializrMetadataUpdateStrategyTests {
private static final ObjectMapper objectMapper = new ObjectMapper();
@@ -62,19 +62,22 @@ class SaganInitializrMetadataUpdateStrategyTests {
InitializrMetadata metadata = new InitializrMetadataTestBuilder().addBootVersion("0.0.9.RELEASE", true)
.addBootVersion("0.0.8.RELEASE", false).build();
assertThat(metadata.getBootVersions().getDefault().getId()).isEqualTo("0.0.9.RELEASE");
SaganInitializrMetadataUpdateStrategy provider = new SaganInitializrMetadataUpdateStrategy(this.restTemplate,
objectMapper);
expectJson(metadata.getConfiguration().getEnv().getSpringBootMetadataUrl(), "metadata/sagan/spring-boot.json");
SpringIoInitializrMetadataUpdateStrategy provider = new SpringIoInitializrMetadataUpdateStrategy(
this.restTemplate, objectMapper);
expectJson(metadata.getConfiguration().getEnv().getSpringBootMetadataUrl(),
"metadata/springio/spring-boot.json");
InitializrMetadata updatedMetadata = provider.update(metadata);
assertThat(updatedMetadata.getBootVersions()).isNotNull();
List<DefaultMetadataElement> updatedBootVersions = updatedMetadata.getBootVersions().getContent();
assertThat(updatedBootVersions).hasSize(5);
assertBootVersion(updatedBootVersions.get(0), "2.5.0 (M1)", false);
assertBootVersion(updatedBootVersions.get(1), "2.4.1 (SNAPSHOT)", false);
assertBootVersion(updatedBootVersions.get(2), "2.4.0", true);
assertBootVersion(updatedBootVersions.get(3), "2.3.8 (SNAPSHOT)", false);
assertBootVersion(updatedBootVersions.get(4), "2.3.7", false);
assertThat(updatedBootVersions).hasSize(7);
assertBootVersion(updatedBootVersions.get(0), "3.0.2 (SNAPSHOT)", false);
assertBootVersion(updatedBootVersions.get(1), "3.0.1", true);
assertBootVersion(updatedBootVersions.get(2), "2.7.8 (SNAPSHOT)", false);
assertBootVersion(updatedBootVersions.get(3), "2.7.7", false);
assertBootVersion(updatedBootVersions.get(4), "2.6.14", false);
assertBootVersion(updatedBootVersions.get(5), "2.5.14", false);
assertBootVersion(updatedBootVersions.get(6), "2.4.13", false);
}
@Test
@@ -82,20 +85,22 @@ class SaganInitializrMetadataUpdateStrategyTests {
InitializrMetadata metadata = new InitializrMetadataTestBuilder().addBootVersion("0.0.9.RELEASE", true)
.addBootVersion("0.0.8.RELEASE", false).build();
assertThat(metadata.getBootVersions().getDefault().getId()).isEqualTo("0.0.9.RELEASE");
SaganInitializrMetadataUpdateStrategy provider = new SaganInitializrMetadataUpdateStrategy(this.restTemplate,
objectMapper);
SpringIoInitializrMetadataUpdateStrategy provider = new SpringIoInitializrMetadataUpdateStrategy(
this.restTemplate, objectMapper);
expectJson(metadata.getConfiguration().getEnv().getSpringBootMetadataUrl(),
"metadata/sagan/spring-boot-no-default.json");
"metadata/springio/spring-boot-no-default.json");
InitializrMetadata updatedMetadata = provider.update(metadata);
assertThat(updatedMetadata.getBootVersions()).isNotNull();
List<DefaultMetadataElement> updatedBootVersions = updatedMetadata.getBootVersions().getContent();
assertThat(updatedBootVersions).hasSize(5);
assertBootVersion(updatedBootVersions.get(0), "2.5.0 (M1)", true);
assertBootVersion(updatedBootVersions.get(1), "2.4.1 (SNAPSHOT)", false);
assertBootVersion(updatedBootVersions.get(2), "2.4.0", false);
assertBootVersion(updatedBootVersions.get(3), "2.3.8 (SNAPSHOT)", false);
assertBootVersion(updatedBootVersions.get(4), "2.3.7", false);
assertThat(updatedBootVersions).hasSize(7);
assertBootVersion(updatedBootVersions.get(0), "3.0.2 (SNAPSHOT)", true);
assertBootVersion(updatedBootVersions.get(1), "3.0.1", false);
assertBootVersion(updatedBootVersions.get(2), "2.7.8 (SNAPSHOT)", false);
assertBootVersion(updatedBootVersions.get(3), "2.7.7", false);
assertBootVersion(updatedBootVersions.get(4), "2.6.14", false);
assertBootVersion(updatedBootVersions.get(5), "2.5.14", false);
assertBootVersion(updatedBootVersions.get(6), "2.4.13", false);
}
private static void assertBootVersion(DefaultMetadataElement actual, String name, boolean defaultVersion) {

View File

@@ -137,7 +137,7 @@
"version": "1.0.0.RELEASE"
}
},
"springBootMetadataUrl": "https://spring.io/project_metadata/spring-boot"
"springBootMetadataUrl": "https://spring.io/api/projects/spring-boot/releases"
}},
"dependencies": {
"content": [

View File

@@ -1,66 +0,0 @@
{
"id": "spring-boot",
"name": "Spring Boot",
"repoUrl": "https://github.com/spring-projects/spring-boot",
"siteUrl": "https://projects.spring.io/spring-boot",
"category": "active",
"stackOverflowTags": "spring-boot",
"projectReleases": [
{
"releaseStatus": "SNAPSHOT",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.0-M1/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.0-M1/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": {
"id": "spring-milestones",
"name": "Spring Milestones",
"url": "https://repo.spring.io/libs-milestone",
"snapshotsEnabled": false
},
"preRelease": true,
"generalAvailability": false,
"versionDisplayName": "2.5.0-M1",
"current": false,
"snapshot": false,
"version": "2.5.0-M1"
},
{
"releaseStatus": "SNAPSHOT",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.1-SNAPSHOT/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.1-SNAPSHOT/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": {
"id": "spring-snapshots",
"name": "Spring Snapshots",
"url": "https://repo.spring.io/libs-snapshot",
"snapshotsEnabled": true
},
"preRelease": false,
"generalAvailability": false,
"versionDisplayName": "2.4.1",
"current": false,
"snapshot": true,
"version": "Not a valid version"
},
{
"releaseStatus": "GENERAL_AVAILABILITY",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.0/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.0/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": null,
"preRelease": false,
"generalAvailability": true,
"versionDisplayName": "2.4.0",
"current": true,
"snapshot": false,
"version": "2.4.0"
}
],
"aggregator": false,
"stackOverflowTagList": [
"spring-boot"
]
}

View File

@@ -1,99 +0,0 @@
{
"id": "spring-boot",
"name": "Spring Boot",
"repoUrl": "https://github.com/spring-projects/spring-boot",
"siteUrl": "https://projects.spring.io/spring-boot",
"category": "active",
"stackOverflowTags": "spring-boot",
"projectReleases": [
{
"releaseStatus": "SNAPSHOT",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.0-M1/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.0-M1/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": {
"id": "spring-milestones",
"name": "Spring Milestones",
"url": "https://repo.spring.io/libs-milestone",
"snapshotsEnabled": false
},
"preRelease": true,
"generalAvailability": false,
"versionDisplayName": "2.5.0-M1",
"current": false,
"snapshot": false,
"version": "2.5.0-M1"
},
{
"releaseStatus": "SNAPSHOT",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.1-SNAPSHOT/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.1-SNAPSHOT/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": {
"id": "spring-snapshots",
"name": "Spring Snapshots",
"url": "https://repo.spring.io/libs-snapshot",
"snapshotsEnabled": true
},
"preRelease": false,
"generalAvailability": false,
"versionDisplayName": "2.4.1",
"current": false,
"snapshot": true,
"version": "2.4.1-SNAPSHOT"
},
{
"releaseStatus": "GENERAL_AVAILABILITY",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.0/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.0/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": null,
"preRelease": false,
"generalAvailability": true,
"versionDisplayName": "2.4.0",
"current": false,
"snapshot": false,
"version": "2.4.0"
},
{
"releaseStatus": "SNAPSHOT",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.3.8.BUILD-SNAPSHOT/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.3.8.BUILD-SNAPSHOT/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": {
"id": "spring-snapshots",
"name": "Spring Snapshots",
"url": "https://repo.spring.io/libs-snapshot",
"snapshotsEnabled": true
},
"preRelease": false,
"generalAvailability": false,
"versionDisplayName": "2.3.8",
"current": false,
"snapshot": true,
"version": "2.3.8.BUILD-SNAPSHOT"
},
{
"releaseStatus": "GENERAL_AVAILABILITY",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/current/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": null,
"preRelease": false,
"generalAvailability": true,
"versionDisplayName": "2.3.7",
"current": false,
"snapshot": false,
"version": "2.3.7.RELEASE"
}
],
"aggregator": false,
"stackOverflowTagList": [
"spring-boot"
]
}

View File

@@ -1,99 +0,0 @@
{
"id": "spring-boot",
"name": "Spring Boot",
"repoUrl": "https://github.com/spring-projects/spring-boot",
"siteUrl": "https://projects.spring.io/spring-boot",
"category": "active",
"stackOverflowTags": "spring-boot",
"projectReleases": [
{
"releaseStatus": "GENERAL_AVAILABILITY",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.0/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.0/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": null,
"preRelease": false,
"generalAvailability": true,
"versionDisplayName": "2.4.0",
"current": true,
"snapshot": false,
"version": "2.4.0"
},
{
"releaseStatus": "SNAPSHOT",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.0-M1/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.0-M1/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": {
"id": "spring-milestones",
"name": "Spring Milestones",
"url": "https://repo.spring.io/libs-milestone",
"snapshotsEnabled": false
},
"preRelease": true,
"generalAvailability": false,
"versionDisplayName": "2.5.0-M1",
"current": false,
"snapshot": false,
"version": "2.5.0-M1"
},
{
"releaseStatus": "SNAPSHOT",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.1-SNAPSHOT/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.1-SNAPSHOT/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": {
"id": "spring-snapshots",
"name": "Spring Snapshots",
"url": "https://repo.spring.io/libs-snapshot",
"snapshotsEnabled": true
},
"preRelease": false,
"generalAvailability": false,
"versionDisplayName": "2.4.1",
"current": false,
"snapshot": true,
"version": "2.4.1-SNAPSHOT"
},
{
"releaseStatus": "SNAPSHOT",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/2.3.8.BUILD-SNAPSHOT/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.3.8.BUILD-SNAPSHOT/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": {
"id": "spring-snapshots",
"name": "Spring Snapshots",
"url": "https://repo.spring.io/libs-snapshot",
"snapshotsEnabled": true
},
"preRelease": false,
"generalAvailability": false,
"versionDisplayName": "2.3.8",
"current": false,
"snapshot": true,
"version": "2.3.8.BUILD-SNAPSHOT"
},
{
"releaseStatus": "GENERAL_AVAILABILITY",
"refDocUrl": "https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/current/api/",
"groupId": "org.springframework.boot",
"artifactId": "spring-boot",
"repository": null,
"preRelease": false,
"generalAvailability": true,
"versionDisplayName": "2.3.7",
"current": false,
"snapshot": false,
"version": "2.3.7.RELEASE"
}
],
"aggregator": false,
"stackOverflowTagList": [
"spring-boot"
]
}

View File

@@ -0,0 +1,44 @@
{
"_embedded": {
"releases": [
{
"version": "invalid version",
"status": "SNAPSHOT",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/3.0.2-SNAPSHOT/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/3.0.2-SNAPSHOT/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/3.0.2-SNAPSHOT"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-snapshots"
}
}
},
{
"version": "3.0.1",
"status": "GENERAL_AVAILABILITY",
"current": true,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/current/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/current/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/3.0.1"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
}
]
},
"_links": {
"current": {
"href": "https://spring.io/api/projects/spring-boot/releases/current"
},
"project": {
"href": "https://spring.io/api/projects/spring-boot"
}
}
}

View File

@@ -0,0 +1,119 @@
{
"_embedded": {
"releases": [
{
"version": "3.0.2-SNAPSHOT",
"status": "SNAPSHOT",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/3.0.2-SNAPSHOT/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/3.0.2-SNAPSHOT/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/3.0.2-SNAPSHOT"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-snapshots"
}
}
},
{
"version": "3.0.1",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/current/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/current/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/3.0.1"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
},
{
"version": "2.7.8-SNAPSHOT",
"status": "SNAPSHOT",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.7.8-SNAPSHOT/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.7.8-SNAPSHOT/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.7.8-SNAPSHOT"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-snapshots"
}
}
},
{
"version": "2.7.7",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.7.7/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.7.7/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.7.7"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
},
{
"version": "2.6.14",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.6.14/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.6.14/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.6.14"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
},
{
"version": "2.5.14",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.14/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.14/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.5.14"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
},
{
"version": "2.4.13",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.13/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.13/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.4.13"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
}
]
},
"_links": {
"current": {
"href": "https://spring.io/api/projects/spring-boot/releases/current"
},
"project": {
"href": "https://spring.io/api/projects/spring-boot"
}
}
}

View File

@@ -0,0 +1,119 @@
{
"_embedded": {
"releases": [
{
"version": "3.0.2-SNAPSHOT",
"status": "SNAPSHOT",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/3.0.2-SNAPSHOT/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/3.0.2-SNAPSHOT/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/3.0.2-SNAPSHOT"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-snapshots"
}
}
},
{
"version": "3.0.1",
"status": "GENERAL_AVAILABILITY",
"current": true,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/current/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/current/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/3.0.1"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
},
{
"version": "2.7.8-SNAPSHOT",
"status": "SNAPSHOT",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.7.8-SNAPSHOT/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.7.8-SNAPSHOT/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.7.8-SNAPSHOT"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-snapshots"
}
}
},
{
"version": "2.7.7",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.7.7/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.7.7/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.7.7"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
},
{
"version": "2.6.14",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.6.14/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.6.14/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.6.14"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
},
{
"version": "2.5.14",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.14/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.5.14/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.5.14"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
},
{
"version": "2.4.13",
"status": "GENERAL_AVAILABILITY",
"current": false,
"referenceDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.13/reference/html/",
"apiDocUrl": "https://docs.spring.io/spring-boot/docs/2.4.13/api/",
"_links": {
"self": {
"href": "https://spring.io/api/projects/spring-boot/releases/2.4.13"
},
"repository": {
"href": "https://spring.io/api/repositories/spring-releases"
}
}
}
]
},
"_links": {
"current": {
"href": "https://spring.io/api/projects/spring-boot/releases/current"
},
"project": {
"href": "https://spring.io/api/projects/spring-boot"
}
}
}