From 2974dc43c76c44f2fd72fe59139839f1d9645db7 Mon Sep 17 00:00:00 2001 From: FinlayRJW <88376465+FinlayRJW@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:49:44 +0100 Subject: [PATCH] Read repositories on a per project level (#31) * read repos * deps * use repo explorer * require cache key * include a default * spotless * convert to set * Add generated changelog entries * make lifecycle clearer * use streams * remove unneeded comment * working cache in need of fixing * fix tests * listen for refresh * tidy up code * fix log comment * markups --------- Co-authored-by: Finlay Williams Co-authored-by: svc-changelog --- changelog/@unreleased/pr-31.v2.yml | 5 ++ .../build.gradle | 1 + .../versions/intellij/ContentsUtil.java | 6 +- .../intellij/FolderCompletionContributor.java | 26 +++--- .../intellij/GradleCacheExplorer.java | 25 +++--- ...older.java => GroupPartOrPackageName.java} | 6 +- ...InvalidateCacheOnGradleProjectRefresh.java | 65 ++++++++++++++ .../versions/intellij/RepositoryExplorer.java | 55 +++++++----- .../versions/intellij/RepositoryLoader.java | 86 +++++++++++++++++++ .../VersionCompletionContributor.java | 14 ++- .../intellij/GradleCacheExplorerTest.java | 23 ++--- versions.props | 1 - 12 files changed, 239 insertions(+), 74 deletions(-) create mode 100644 changelog/@unreleased/pr-31.v2.yml rename gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/{Folder.java => GroupPartOrPackageName.java} (82%) create mode 100644 gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/InvalidateCacheOnGradleProjectRefresh.java create mode 100644 gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/RepositoryLoader.java diff --git a/changelog/@unreleased/pr-31.v2.yml b/changelog/@unreleased/pr-31.v2.yml new file mode 100644 index 0000000..6bdebaf --- /dev/null +++ b/changelog/@unreleased/pr-31.v2.yml @@ -0,0 +1,5 @@ +type: improvement +improvement: + description: Read repositories on a per project level + links: + - https://github.com/palantir/gradle-consistent-versions-idea-plugin/pull/31 diff --git a/gradle-consistent-versions-idea-plugin/build.gradle b/gradle-consistent-versions-idea-plugin/build.gradle index 916cd87..9261690 100644 --- a/gradle-consistent-versions-idea-plugin/build.gradle +++ b/gradle-consistent-versions-idea-plugin/build.gradle @@ -25,6 +25,7 @@ sourceSets.main.java.srcDirs 'build/generated/sources/intellij-lexer/main/java' dependencies { implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-guava' implementation 'com.github.ben-manes.caffeine:caffeine' annotationProcessor 'org.immutables:value' compileOnly 'org.immutables:value::annotations' diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/ContentsUtil.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/ContentsUtil.java index 4037092..ad341f0 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/ContentsUtil.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/ContentsUtil.java @@ -34,10 +34,6 @@ public final class ContentsUtil { private static final Logger log = LoggerFactory.getLogger(ContentsUtil.class); - private ContentsUtil() { - // Utility class; prevent instantiation - } - public static Optional fetchPageContents(URL pageUrl) { ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); @@ -86,4 +82,6 @@ private static Callable fetchContentTask(URL pageUrl, ProgressIndicator return result.toString(); }; } + + private ContentsUtil() {} } diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/FolderCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/FolderCompletionContributor.java index f3f8019..8b8b81a 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/FolderCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/FolderCompletionContributor.java @@ -22,19 +22,24 @@ import com.intellij.codeInsight.completion.CompletionResultSet; import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager; +import com.intellij.openapi.project.Project; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.util.ProcessingContext; import com.palantir.gradle.versions.intellij.psi.VersionPropsTypes; -import java.util.List; public class FolderCompletionContributor extends CompletionContributor { - private final GradleCacheExplorer gradleCacheExplorer = - new GradleCacheExplorer(List.of("https://repo.maven.apache.org/maven2/")); + private final GradleCacheExplorer gradleCacheExplorer = new GradleCacheExplorer(); + + private final RepositoryExplorer repositoryExplorer = new RepositoryExplorer(); public FolderCompletionContributor() { + // We add listener at this stage so that we can invalidate the cache when the gradle project refreshed + ExternalSystemProgressNotificationManager.getInstance() + .addNotificationListener(new InvalidateCacheOnGradleProjectRefresh(gradleCacheExplorer)); cacheCompletion(VersionPropsTypes.GROUP_PART); cacheCompletion(VersionPropsTypes.NAME_KEY); remoteCompletion(VersionPropsTypes.GROUP_PART); @@ -47,13 +52,12 @@ private void remoteCompletion(IElementType elementType) { protected void addCompletions( CompletionParameters parameters, ProcessingContext context, CompletionResultSet resultSet) { - List repositories = List.of("https://repo.maven.apache.org/maven2/"); - DependencyGroup group = DependencyGroup.groupFromParameters(parameters); - repositories.stream() - .map(RepositoryExplorer::new) - .flatMap(repositoryExplorer -> repositoryExplorer.getFolders(group).stream()) + Project project = parameters.getOriginalFile().getProject(); + + RepositoryLoader.loadRepositories(project).stream() + .flatMap(url -> repositoryExplorer.getGroupPartOrPackageName(group, url).stream()) .map(LookupElementBuilder::create) .forEach(resultSet::addElement); } @@ -68,8 +72,10 @@ protected void addCompletions( DependencyGroup group = DependencyGroup.groupFromParameters(parameters); - gradleCacheExplorer.getCompletions(group).stream() - .map(suggestion -> LookupElementBuilder.create(Folder.of(suggestion))) + Project project = parameters.getOriginalFile().getProject(); + + gradleCacheExplorer.getCompletions(RepositoryLoader.loadRepositories(project), group).stream() + .map(suggestion -> LookupElementBuilder.create(GroupPartOrPackageName.of(suggestion))) .forEach(resultSet::addElement); } }); diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GradleCacheExplorer.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GradleCacheExplorer.java index 4b6fb0e..83e5b6b 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GradleCacheExplorer.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GradleCacheExplorer.java @@ -29,7 +29,6 @@ import java.nio.file.Paths; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; @@ -47,13 +46,11 @@ public class GradleCacheExplorer { private final Cache> cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(); - private final List projectUrls; - - public GradleCacheExplorer(List projectUrls) { - this.projectUrls = projectUrls; + public final void invalidateCache() { + cache.invalidateAll(); } - public final Set getCompletions(DependencyGroup input) { + public final Set getCompletions(Set repoUrls, DependencyGroup input) { String parsedInput = String.join(".", input.parts()); ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator == null) { @@ -61,7 +58,7 @@ public final Set getCompletions(DependencyGroup input) { } try { - Callable> task = () -> extractStrings(indicator); + Callable> task = () -> extractStrings(repoUrls, indicator); Future> future = ApplicationManager.getApplication().executeOnPooledThread(task); Set results = com.intellij.openapi.application.ex.ApplicationUtil.runWithCheckCanceled(future::get, indicator); @@ -82,7 +79,7 @@ public final Set getCompletions(DependencyGroup input) { return Collections.emptySet(); } - private Set extractStrings(ProgressIndicator indicator) { + private Set extractStrings(Set repoUrls, ProgressIndicator indicator) { return cache.get("metadata", key -> { try (Stream allFolders = Files.list(Paths.get(GRADLE_CACHE_PATH))) { @@ -99,8 +96,8 @@ private Set extractStrings(ProgressIndicator indicator) { .map(metadataFolder -> metadataFolder.resolve("resource-at-url.bin")) .filter(Files::exists) .flatMap(this::extractStringsFromBinFile) - .filter(this::isValidResourceUrl) - .map(this::extractGroupAndArtifactFromUrl) + .filter(url -> isValidResourceUrl(repoUrls, url)) + .map(url -> extractGroupAndArtifactFromUrl(repoUrls, url)) .flatMap(Optional::stream) .collect(Collectors.toSet()); } catch (IOException e) { @@ -116,8 +113,8 @@ private Set extractStrings(ProgressIndicator indicator) { }); } - final boolean isValidResourceUrl(String url) { - return projectUrls.stream().anyMatch(url::startsWith) && (url.endsWith(".pom") || url.endsWith(".jar")); + final boolean isValidResourceUrl(Set repoUrls, String url) { + return repoUrls.stream().anyMatch(url::startsWith) && (url.endsWith(".pom") || url.endsWith(".jar")); } final Stream extractStringsFromBinFile(Path binFile) { @@ -160,8 +157,8 @@ final Stream extractStringsFromBinFile(Path binFile) { * @return an {@link Optional} containing a string in the format "group:artifact" if extraction is successful, * or {@link Optional#empty()} if no matching project URL is found or the URL does not have the expected structure. */ - Optional extractGroupAndArtifactFromUrl(String url) { - return projectUrls.stream().filter(url::startsWith).findFirst().flatMap(projectUrl -> { + Optional extractGroupAndArtifactFromUrl(Set repoUrls, String url) { + return repoUrls.stream().filter(url::startsWith).findFirst().flatMap(projectUrl -> { String mavenLayout = url.substring(projectUrl.length()); int lastSlashIndex = mavenLayout.lastIndexOf('/'); diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/Folder.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GroupPartOrPackageName.java similarity index 82% rename from gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/Folder.java rename to gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GroupPartOrPackageName.java index bd9bd0d..9484209 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/Folder.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/GroupPartOrPackageName.java @@ -19,11 +19,11 @@ import org.immutables.value.Value; @Value.Immutable -public abstract class Folder { +public abstract class GroupPartOrPackageName { protected abstract String name(); - public static Folder of(String name) { - return ImmutableFolder.builder().name(name).build(); + public static GroupPartOrPackageName of(String name) { + return ImmutableGroupPartOrPackageName.builder().name(name).build(); } @Override diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/InvalidateCacheOnGradleProjectRefresh.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/InvalidateCacheOnGradleProjectRefresh.java new file mode 100644 index 0000000..93f3a2c --- /dev/null +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/InvalidateCacheOnGradleProjectRefresh.java @@ -0,0 +1,65 @@ +/* + * (c) Copyright 2024 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.gradle.versions.intellij; + +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent; +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener; +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType; +import org.jetbrains.plugins.gradle.util.GradleConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InvalidateCacheOnGradleProjectRefresh implements ExternalSystemTaskNotificationListener { + private static final Logger log = LoggerFactory.getLogger(InvalidateCacheOnGradleProjectRefresh.class); + + private final GradleCacheExplorer gradleCacheExplorer; + + public InvalidateCacheOnGradleProjectRefresh(GradleCacheExplorer gradleCacheExplorer) { + this.gradleCacheExplorer = gradleCacheExplorer; + } + + @Override + public final void onSuccess(ExternalSystemTaskId id) { + if (GradleConstants.SYSTEM_ID.equals(id.getProjectSystemId()) + && id.getType() == ExternalSystemTaskType.RESOLVE_PROJECT) { + log.info("Gradle project refresh finished"); + gradleCacheExplorer.invalidateCache(); + } + } + + @Override + public void onStart(ExternalSystemTaskId id, String workingDir) {} + + @Override + public void onFailure(ExternalSystemTaskId id, Exception exception) {} + + @Override + public void beforeCancel(ExternalSystemTaskId id) {} + + @Override + public void onCancel(ExternalSystemTaskId id) {} + + @Override + public void onStatusChange(ExternalSystemTaskNotificationEvent event) {} + + @Override + public void onTaskOutput(ExternalSystemTaskId id, String text, boolean stdOut) {} + + @Override + public void onEnd(ExternalSystemTaskId id) {} +} diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/RepositoryExplorer.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/RepositoryExplorer.java index 9efb8bb..979ee04 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/RepositoryExplorer.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/RepositoryExplorer.java @@ -21,11 +21,12 @@ import com.github.benmanes.caffeine.cache.Caffeine; import java.net.MalformedURLException; import java.net.URL; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; +import org.immutables.value.Value; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -36,44 +37,41 @@ public class RepositoryExplorer { private static final Logger log = LoggerFactory.getLogger(RepositoryExplorer.class); - private final String baseUrl; - private static final Cache> folderCache = Caffeine.newBuilder() + private final Cache> folderCache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(100) .build(); - public RepositoryExplorer(String baseUrl) { - this.baseUrl = baseUrl; - } - - public final List getFolders(DependencyGroup group) { - List folders = folderCache.get(group, key -> { - List loadedFolders = loadFolders(key); + public final Set getGroupPartOrPackageName(DependencyGroup group, String url) { + CacheKey cacheKey = CacheKey.of(url, group); + Set folders = folderCache.get(cacheKey, key -> { + Set loadedFolders = loadFolders(key.group(), url); return loadedFolders.isEmpty() ? null : loadedFolders; }); - return folders != null ? folders : Collections.emptyList(); + return folders != null ? folders : Collections.emptySet(); } - private List loadFolders(DependencyGroup group) { - String urlString = baseUrl + group.asUrlString(); + private Set loadFolders(DependencyGroup group, String url) { + String urlString = url + group.asUrlString(); Optional content = fetchContent(urlString); if (content.isEmpty()) { log.debug("Page does not exist"); - return new ArrayList<>(); + return Collections.emptySet(); } return fetchFoldersFromContent(content.get()); } - public final List getVersions(DependencyGroup group, DependencyName dependencyPackage) { - String urlString = baseUrl + group.asUrlString() + dependencyPackage.name() + "/maven-metadata.xml"; + public final Set getVersions( + DependencyGroup group, DependencyName dependencyPackage, String url) { + String urlString = url + group.asUrlString() + dependencyPackage.name() + "/maven-metadata.xml"; Optional content = fetchContent(urlString); if (content.isEmpty()) { log.debug("Empty metadata content received"); - return new ArrayList<>(); + return Collections.emptySet(); } return parseVersionsFromContent(content.get()); @@ -89,8 +87,8 @@ private Optional fetchContent(String urlString) { } } - private List fetchFoldersFromContent(String contents) { - List folders = new ArrayList<>(); + private Set fetchFoldersFromContent(String contents) { + Set folders = new HashSet<>(); Document doc = Jsoup.parse(contents); Elements links = doc.select("a[href]"); @@ -98,14 +96,14 @@ private List fetchFoldersFromContent(String contents) { for (Element link : links) { String href = link.attr("href"); if (href.endsWith("/") && !href.contains(".")) { - folders.add(Folder.of(href.substring(0, href.length() - 1))); + folders.add(GroupPartOrPackageName.of(href.substring(0, href.length() - 1))); } } return folders; } - private List parseVersionsFromContent(String content) { - List versions = new ArrayList<>(); + private Set parseVersionsFromContent(String content) { + Set versions = new HashSet<>(); try { XmlMapper xmlMapper = new XmlMapper(); @@ -121,4 +119,15 @@ private List parseVersionsFromContent(String content) { } return versions; } + + @Value.Immutable + interface CacheKey { + String url(); + + DependencyGroup group(); + + static CacheKey of(String url, DependencyGroup group) { + return ImmutableCacheKey.builder().url(url).group(group).build(); + } + } } diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/RepositoryLoader.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/RepositoryLoader.java new file mode 100644 index 0000000..239ad32 --- /dev/null +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/RepositoryLoader.java @@ -0,0 +1,86 @@ +/* + * (c) Copyright 2024 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.gradle.versions.intellij; + +import com.fasterxml.jackson.annotation.JsonRootName; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.datatype.guava.GuavaModule; +import com.intellij.openapi.project.Project; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.immutables.value.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class RepositoryLoader { + private static final Logger log = LoggerFactory.getLogger(RepositoryExplorer.class); + + private static final ObjectMapper XML_MAPPER = new XmlMapper().registerModule(new GuavaModule()); + private static final String MAVEN_REPOSITORIES_FILE_NAME = ".idea/gcv-maven-repositories.xml"; + private static final String DEFAULT = "https://repo.maven.apache.org/maven2/"; + + public static Set loadRepositories(Project project) { + File mavenRepoFile = new File(project.getBasePath(), MAVEN_REPOSITORIES_FILE_NAME); + + if (!mavenRepoFile.exists()) { + return Set.of(DEFAULT); + } + + try { + Repositories repositories = XML_MAPPER.readValue(mavenRepoFile, Repositories.class); + return repositories.repositories().stream() + .map(RepositoryConfig::url) + .collect(Collectors.toSet()); + } catch (IOException e) { + log.error("Failed to load repositories", e); + } + + return Set.of(DEFAULT); + } + + @Value.Immutable + @JsonDeserialize(as = ImmutableRepositoryConfig.class) + @JsonSerialize(as = ImmutableRepositoryConfig.class) + interface RepositoryConfig { + + @Value.Parameter + @JacksonXmlProperty(isAttribute = true) + String url(); + } + + @Value.Immutable + @JsonDeserialize(as = ImmutableRepositories.class) + @JsonSerialize(as = ImmutableRepositories.class) + @JsonRootName("repositories") + interface Repositories { + + @Value.Parameter + @JacksonXmlElementWrapper(useWrapping = false) + @JacksonXmlProperty(localName = "repository") + List repositories(); + } + + private RepositoryLoader() {} +} diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java index ae1b328..90cbf68 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionCompletionContributor.java @@ -23,6 +23,7 @@ import com.intellij.codeInsight.completion.PrioritizedLookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.project.Project; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; @@ -30,13 +31,10 @@ import com.palantir.gradle.versions.intellij.psi.VersionPropsDependencyVersion; import com.palantir.gradle.versions.intellij.psi.VersionPropsProperty; import com.palantir.gradle.versions.intellij.psi.VersionPropsTypes; -import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class VersionCompletionContributor extends CompletionContributor { - private static final Logger log = LoggerFactory.getLogger(VersionCompletionContributor.class); + private static final RepositoryExplorer repositoryExplorer = new RepositoryExplorer(); VersionCompletionContributor() { extend( @@ -58,12 +56,10 @@ public void addCompletions( DependencyName dependencyPackage = DependencyName.of(property.getDependencyName().getText()); - List repositories = List.of("https://repo1.maven.org/maven2/"); + Project project = parameters.getOriginalFile().getProject(); - repositories.stream() - .map(RepositoryExplorer::new) - .flatMap(repositoryExplorer -> - repositoryExplorer.getVersions(group, dependencyPackage).stream()) + RepositoryLoader.loadRepositories(project).stream() + .flatMap(url -> repositoryExplorer.getVersions(group, dependencyPackage, url).stream()) .map(version -> version.isLatest() ? PrioritizedLookupElement.withPriority( LookupElementBuilder.create(version) diff --git a/gradle-consistent-versions-idea-plugin/src/test/java/com/palantir/gradle/versions/intellij/GradleCacheExplorerTest.java b/gradle-consistent-versions-idea-plugin/src/test/java/com/palantir/gradle/versions/intellij/GradleCacheExplorerTest.java index 6070632..0dbe0c1 100644 --- a/gradle-consistent-versions-idea-plugin/src/test/java/com/palantir/gradle/versions/intellij/GradleCacheExplorerTest.java +++ b/gradle-consistent-versions-idea-plugin/src/test/java/com/palantir/gradle/versions/intellij/GradleCacheExplorerTest.java @@ -37,8 +37,7 @@ class GradleCacheExplorerTest { @BeforeEach void beforeEach() { - List projectUrls = List.of("https://repo.maven.apache.org/maven2/", "https://jcenter.bintray.com/"); - explorer = new GradleCacheExplorer(projectUrls); + explorer = new GradleCacheExplorer(); } @Test @@ -46,20 +45,22 @@ void test_gets_valid_urls_only() { Set projectUrls = Set.of("https://repo.maven.apache.org/maven2/", "https://jcenter.bintray.com/"); assertThat(explorer.isValidResourceUrl( - "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.pom")) + projectUrls, "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.pom")) .as("because the URL is from a known valid repository and ends with .pom") .isTrue(); - assertThat(explorer.isValidResourceUrl("https://jcenter.bintray.com/com/example/artifact/1.0/artifact-1.0.jar")) + assertThat(explorer.isValidResourceUrl( + projectUrls, "https://jcenter.bintray.com/com/example/artifact/1.0/artifact-1.0.jar")) .as("because the URL is from a known valid repository and ends with .jar") .isTrue(); - assertThat(explorer.isValidResourceUrl("https://example.com/com/example/artifact/1.0/artifact-1.0.pom")) + assertThat(explorer.isValidResourceUrl( + projectUrls, "https://example.com/com/example/artifact/1.0/artifact-1.0.pom")) .as("because the URL is not from a known valid repository") .isFalse(); assertThat(explorer.isValidResourceUrl( - "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.txt")) + projectUrls, "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.txt")) .as("because the URL ends with an invalid extension") .isFalse(); } @@ -81,26 +82,28 @@ void test_gets_all_strings_from_bin(@TempDir File tempDir) throws IOException { @Test void test_extract_group_artifact_from_url_correctly() { + Set projectUrls = Set.of("https://repo.maven.apache.org/maven2/", "https://jcenter.bintray.com/"); assertThat(explorer.extractGroupAndArtifactFromUrl( + projectUrls, "https://repo.maven.apache.org/maven2/com/example/artifact/1.0/artifact-1.0.pom") .get()) .as("because the URL should be parsed into group and artifact") .isEqualTo("com.example:artifact"); assertThat(explorer.extractGroupAndArtifactFromUrl( - "https://jcenter.bintray.com/com/example/artifact/1.0/artifact-1.0.jar") + projectUrls, "https://jcenter.bintray.com/com/example/artifact/1.0/artifact-1.0.jar") .get()) .as("because the URL should be parsed into group and artifact") .isEqualTo("com.example:artifact"); assertThat(explorer.extractGroupAndArtifactFromUrl( - "https://not.vaild.com/example/artifact/1.0/artifact-1.0.jar")) + projectUrls, "https://not.vaild.com/example/artifact/1.0/artifact-1.0.jar")) .as("Expected the URL to not match any project URL, resulting in an empty Optional") .isEmpty(); - assertThat(explorer.extractGroupAndArtifactFromUrl("https://jcenter.bintray.com/com/example")) + assertThat(explorer.extractGroupAndArtifactFromUrl(projectUrls, "https://jcenter.bintray.com/com/example")) .as("Could not find second to last slash, resulting in an empty Optional") .isEmpty(); - assertThat(explorer.extractGroupAndArtifactFromUrl("")) + assertThat(explorer.extractGroupAndArtifactFromUrl(projectUrls, "")) .as("Empty passed in so empty returned") .isEmpty(); } diff --git a/versions.props b/versions.props index 3ab0539..f9820cf 100644 --- a/versions.props +++ b/versions.props @@ -1,5 +1,4 @@ com.fasterxml.jackson.*:jackson-* = 2.16.0 -com.fasterxml.jackson.core:jackson-databind = 2.16.0 com.google.guava:guava = 33.3.1-jre org.assertj:assertj-core = 3.26.3 org.immutables:value = 2.10.1