Skip to content

Commit 0b518c8

Browse files
authored
Merge pull request #31 from PaperMC/feat/version-check
feat: version check
2 parents 686885b + b13624d commit 0b518c8

File tree

6 files changed

+242
-0
lines changed

6 files changed

+242
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2024 PaperMC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.papermc.fill.controller;
17+
18+
import com.github.benmanes.caffeine.cache.Caffeine;
19+
import com.github.benmanes.caffeine.cache.LoadingCache;
20+
import io.papermc.fill.model.request.VersionCheckRequest;
21+
import io.papermc.fill.model.response.VersionCheckResponse;
22+
import io.papermc.fill.service.VersionCheckService;
23+
import io.papermc.fill.util.http.Responses;
24+
import io.swagger.v3.oas.annotations.Hidden;
25+
import java.time.Duration;
26+
import org.jspecify.annotations.NullMarked;
27+
import org.springframework.beans.factory.annotation.Autowired;
28+
import org.springframework.http.MediaType;
29+
import org.springframework.http.ResponseEntity;
30+
import org.springframework.web.bind.annotation.CrossOrigin;
31+
import org.springframework.web.bind.annotation.PostMapping;
32+
import org.springframework.web.bind.annotation.RequestBody;
33+
import org.springframework.web.bind.annotation.RequestMethod;
34+
import org.springframework.web.bind.annotation.RestController;
35+
36+
@Hidden
37+
@NullMarked
38+
@RestController
39+
public class VersionCheckController {
40+
private final LoadingCache<VersionCheckRequest, VersionCheckResponse> cache;
41+
42+
@Autowired
43+
public VersionCheckController(
44+
final VersionCheckService service
45+
) {
46+
this.cache = Caffeine.newBuilder()
47+
.expireAfterAccess(Duration.ofMinutes(30))
48+
.build(service::check);
49+
}
50+
51+
@CrossOrigin(methods = RequestMethod.POST)
52+
@PostMapping(
53+
consumes = MediaType.APPLICATION_JSON_VALUE,
54+
path = "/version-check"
55+
)
56+
public ResponseEntity<?> check(
57+
@RequestBody
58+
final VersionCheckRequest request
59+
) {
60+
return Responses.ok(this.cache.get(request));
61+
}
62+
}

src/main/java/io/papermc/fill/database/FamilyRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.bson.types.ObjectId;
2121
import org.jspecify.annotations.NullMarked;
2222
import org.springframework.data.mongodb.repository.MongoRepository;
23+
import org.springframework.data.mongodb.repository.Query;
2324
import org.springframework.stereotype.Repository;
2425

2526
@NullMarked
@@ -29,6 +30,7 @@ default Stream<FamilyEntity> findAllByProject(final ProjectEntity project) {
2930
return this.findAllByProject(project._id());
3031
}
3132

33+
@Query(sort = "{'createdAt': -1}")
3234
Stream<FamilyEntity> findAllByProject(final ObjectId project);
3335

3436
default Optional<FamilyEntity> findByProjectAndKey(
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2024 PaperMC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.papermc.fill.model.request;
17+
18+
import io.papermc.fill.model.BuildChannel;
19+
import org.jspecify.annotations.NullMarked;
20+
21+
@NullMarked
22+
public record VersionCheckRequest(
23+
String project,
24+
String version,
25+
int build,
26+
BuildChannel channel
27+
) {
28+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2024 PaperMC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.papermc.fill.model.response;
17+
18+
import org.jspecify.annotations.NullMarked;
19+
import org.jspecify.annotations.Nullable;
20+
21+
@NullMarked
22+
public record VersionCheckResponse(
23+
Status status,
24+
@Nullable BehindBy behindBy
25+
) {
26+
@NullMarked
27+
public record BehindBy(
28+
int families,
29+
int versions,
30+
int builds
31+
) {
32+
}
33+
34+
@NullMarked
35+
public enum Status {
36+
UP_TO_DATE,
37+
OUT_OF_DATE,
38+
}
39+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2024 PaperMC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.papermc.fill.service;
17+
18+
import io.papermc.fill.model.request.VersionCheckRequest;
19+
import io.papermc.fill.model.response.VersionCheckResponse;
20+
import org.jspecify.annotations.NullMarked;
21+
22+
@NullMarked
23+
public interface VersionCheckService {
24+
VersionCheckResponse check(final VersionCheckRequest request);
25+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2024 PaperMC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.papermc.fill.service;
17+
18+
import io.papermc.fill.database.BuildEntity;
19+
import io.papermc.fill.database.BuildRepository;
20+
import io.papermc.fill.database.FamilyEntity;
21+
import io.papermc.fill.database.FamilyRepository;
22+
import io.papermc.fill.database.ProjectEntity;
23+
import io.papermc.fill.database.ProjectRepository;
24+
import io.papermc.fill.database.VersionEntity;
25+
import io.papermc.fill.database.VersionRepository;
26+
import io.papermc.fill.exception.BuildNotFoundException;
27+
import io.papermc.fill.exception.FamilyNotFoundException;
28+
import io.papermc.fill.exception.ProjectNotFoundException;
29+
import io.papermc.fill.exception.VersionNotFoundException;
30+
import io.papermc.fill.model.request.VersionCheckRequest;
31+
import io.papermc.fill.model.response.VersionCheckResponse;
32+
import java.util.List;
33+
import java.util.function.Function;
34+
import org.jspecify.annotations.NullMarked;
35+
import org.springframework.beans.factory.annotation.Autowired;
36+
import org.springframework.stereotype.Service;
37+
38+
@NullMarked
39+
@Service
40+
public class VersionCheckServiceImpl implements VersionCheckService {
41+
private static final VersionCheckResponse UP_TO_DATE = new VersionCheckResponse(VersionCheckResponse.Status.UP_TO_DATE, null);
42+
private final ProjectRepository projects;
43+
private final FamilyRepository families;
44+
private final VersionRepository versions;
45+
private final BuildRepository builds;
46+
47+
@Autowired
48+
public VersionCheckServiceImpl(
49+
final ProjectRepository projects,
50+
final FamilyRepository families,
51+
final VersionRepository versions,
52+
final BuildRepository builds
53+
) {
54+
this.projects = projects;
55+
this.families = families;
56+
this.versions = versions;
57+
this.builds = builds;
58+
}
59+
60+
@Override
61+
public VersionCheckResponse check(final VersionCheckRequest request) {
62+
final ProjectEntity project = this.projects.findByKey(request.project()).orElseThrow(ProjectNotFoundException::new);
63+
final VersionEntity version = this.versions.findByProjectAndKey(project, request.version()).orElseThrow(VersionNotFoundException::new);
64+
final FamilyEntity family = this.families.findById(version.family()).orElseThrow(FamilyNotFoundException::new);
65+
final BuildEntity build = this.builds.findByVersionAndNumber(version, request.build()).orElseThrow(BuildNotFoundException::new);
66+
67+
final int distanceFamily = findDistance(this.families.findAllByProject(project).toList(), family.key(), FamilyEntity::key);
68+
final int distanceVersion = findDistance(this.versions.findAllByFamily(family).toList(), version.key(), VersionEntity::key);
69+
final int distanceBuild = findDistance(this.builds.findAllByVersion(version).toList(), build.number(), BuildEntity::number);
70+
71+
if (distanceFamily > 0 || distanceVersion > 0 || distanceBuild > 0) {
72+
return new VersionCheckResponse(VersionCheckResponse.Status.OUT_OF_DATE, new VersionCheckResponse.BehindBy(distanceFamily, distanceVersion, distanceBuild));
73+
}
74+
75+
return UP_TO_DATE;
76+
}
77+
78+
private static <T, K> int findDistance(final List<T> items, final K target, final Function<T, K> getter) {
79+
for (int i = 0; i < items.size(); i++) {
80+
if (getter.apply(items.get(i)).equals(target)) {
81+
return i;
82+
}
83+
}
84+
return Integer.MIN_VALUE;
85+
}
86+
}

0 commit comments

Comments
 (0)