package de.tobias.playwall.service; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import de.tobias.playwall.infrastructure.ArtifactoryConfigurationProperties; import de.tobias.playwall.model.Plugin; import de.tobias.playwall.model.PluginDescription; import de.tobias.playwall.model.PluginManifest; import de.tobias.playwall.model.artifactory.Folder; import de.tobias.playwall.model.artifactory.Version; import jakarta.annotation.PostConstruct; import lombok.AllArgsConstructor; import lombok.SneakyThrows; import org.springframework.aot.hint.annotation.RegisterReflectionForBinding; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; import java.util.Comparator; import java.util.List; @Service @AllArgsConstructor @RegisterReflectionForBinding({PluginDescription.class, PluginManifest.class, Folder.class, Folder.FolderItem.class, ArtifactoryClient.ArchiveViewSourceRequest.class, ArtifactoryClient.ArchiveViewSourceResponse.class}) public class ArtifactoryClient { private final WebClient webClient; private final ArtifactoryConfigurationProperties configurationProperties; private final VersionTokenizer versionTokenizer; private ObjectMapper mapper; @PostConstruct private void init() { mapper = new ObjectMapper(new YAMLFactory()); mapper.findAndRegisterModules(); } public Version getLatestVersion(PluginDescription plugin) { final ResponseEntity<Folder> folderResponse = webClient.get() .uri("/artifactory/api/storage/%s/%s/%s".formatted(configurationProperties.getRepository(), configurationProperties.getGroupId().replace(".", "/"), plugin.getName())) .retrieve() .toEntity(Folder.class) .block(); if(folderResponse == null || folderResponse.getBody() == null) { return null; } final List<Version> version = folderResponse.getBody().getChildren().stream() .filter(Folder.FolderItem::isFolder) .map(child -> versionTokenizer.getVersion(child.getUri())) .sorted(Comparator.comparing(Version::major).thenComparing(Version::minor).thenComparing(Version::patch)) .toList(); if (version.isEmpty()) { return null; } return version.get(version.size() - 1); } record ArchiveViewSourceRequest(String archivePath, String repoKey, String sourcePath) { } record ArchiveViewSourceResponse(String source) { } @SneakyThrows public PluginManifest getPluginManifest(PluginDescription description, Version version) { final ResponseEntity<ArchiveViewSourceResponse> response = webClient.post() .uri("/ui/api/v1/ui/archiveViewSource") .header("X-Requested-With", "XMLHttpRequest") .bodyValue(new ArchiveViewSourceRequest( "%s/%s/%s/%s-%s.jar".formatted( configurationProperties.getGroupId().replace(".", "/"), description.getName(), version.toVersionString(), description.getName(), version.toVersionString() ), configurationProperties.getRepository(), "plugin.yml" )) .retrieve() .toEntity(ArchiveViewSourceResponse.class) .block(); if(response == null || response.getBody() == null) { return null; } return mapper.readValue(response.getBody().source(), PluginManifest.class); } public Mono<byte[]> downloadArtifact(Plugin plugin) { return webClient.get() .uri("/artifactory/%s/%s/%s/%s/%s-%s.jar".formatted( configurationProperties.getRepository(), configurationProperties.getGroupId().replace(".", "/"), plugin.getName(), plugin.getVersion(), plugin.getName(), plugin.getVersion() )) .retrieve() .bodyToMono(byte[].class); } }