diff --git a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/AbstractRemoteAddonService.java b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/AbstractRemoteAddonService.java index b07f57086dd..95f4b79f9e4 100644 --- a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/AbstractRemoteAddonService.java +++ b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/AbstractRemoteAddonService.java @@ -20,6 +20,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -29,6 +30,8 @@ import org.openhab.core.OpenHAB; import org.openhab.core.addon.Addon; import org.openhab.core.addon.AddonEventFactory; +import org.openhab.core.addon.AddonInfo; +import org.openhab.core.addon.AddonInfoRegistry; import org.openhab.core.addon.AddonService; import org.openhab.core.addon.AddonType; import org.openhab.core.cache.ExpiringCache; @@ -66,13 +69,15 @@ public abstract class AbstractRemoteAddonService implements AddonService { protected final ConfigurationAdmin configurationAdmin; protected final ExpiringCache> cachedRemoteAddons = new ExpiringCache<>(Duration.ofMinutes(15), this::getRemoteAddons); + protected final AddonInfoRegistry addonInfoRegistry; protected List cachedAddons = List.of(); protected List installedAddons = List.of(); private final Logger logger = LoggerFactory.getLogger(AbstractRemoteAddonService.class); public AbstractRemoteAddonService(EventPublisher eventPublisher, ConfigurationAdmin configurationAdmin, - StorageService storageService, String servicePid) { + StorageService storageService, AddonInfoRegistry addonInfoRegistry, String servicePid) { + this.addonInfoRegistry = addonInfoRegistry; this.eventPublisher = eventPublisher; this.configurationAdmin = configurationAdmin; this.installedAddonStorage = storageService.getStorage(servicePid); @@ -83,6 +88,16 @@ protected BundleVersion getCoreVersion() { return new BundleVersion(FrameworkUtil.getBundle(OpenHAB.class).getVersion().toString()); } + private Addon convertFromStorage(Map.Entry entry) { + Addon storedAddon = Objects.requireNonNull(gson.fromJson(entry.getValue(), Addon.class)); + AddonInfo addonInfo = addonInfoRegistry.getAddonInfo(storedAddon.getType() + "-" + storedAddon.getId()); + if (addonInfo != null && storedAddon.getConfigDescriptionURI().isBlank()) { + return Addon.create(storedAddon).withConfigDescriptionURI(addonInfo.getConfigDescriptionURI()).build(); + } else { + return storedAddon; + } + } + @Override public void refreshSource() { if (!addonHandlers.stream().allMatch(MarketplaceAddonHandler::isReady)) { @@ -92,8 +107,7 @@ public void refreshSource() { } List addons = new ArrayList<>(); try { - installedAddonStorage.stream().map(e -> Objects.requireNonNull(gson.fromJson(e.getValue(), Addon.class))) - .forEach(addons::add); + installedAddonStorage.stream().map(this::convertFromStorage).forEach(addons::add); } catch (JsonSyntaxException e) { List.copyOf(installedAddonStorage.getKeys()).forEach(installedAddonStorage::remove); logger.error( diff --git a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/community/CommunityMarketplaceAddonService.java b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/community/CommunityMarketplaceAddonService.java index 7054b6f94bd..69104bec7e0 100644 --- a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/community/CommunityMarketplaceAddonService.java +++ b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/community/CommunityMarketplaceAddonService.java @@ -36,6 +36,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.addon.Addon; +import org.openhab.core.addon.AddonInfoRegistry; import org.openhab.core.addon.AddonService; import org.openhab.core.addon.AddonType; import org.openhab.core.addon.marketplace.AbstractRemoteAddonService; @@ -115,8 +116,8 @@ public class CommunityMarketplaceAddonService extends AbstractRemoteAddonService @Activate public CommunityMarketplaceAddonService(final @Reference EventPublisher eventPublisher, @Reference ConfigurationAdmin configurationAdmin, @Reference StorageService storageService, - Map config) { - super(eventPublisher, configurationAdmin, storageService, SERVICE_PID); + @Reference AddonInfoRegistry addonInfoRegistry, Map config) { + super(eventPublisher, configurationAdmin, storageService, addonInfoRegistry, SERVICE_PID); modified(config); } @@ -200,10 +201,13 @@ protected List getRemoteAddons() { @Override public @Nullable Addon getAddon(String uid, @Nullable Locale locale) { + String queryId = uid.startsWith(ADDON_ID_PREFIX) ? uid : ADDON_ID_PREFIX + uid; + // check if it is an installed add-on (cachedAddons also contains possibly incomplete results from the remote // side, we need to retrieve them from Discourse) - if (installedAddons.contains(uid)) { - return cachedAddons.stream().filter(e -> uid.equals(e.getUid())).findAny().orElse(null); + + if (installedAddons.contains(queryId)) { + return cachedAddons.stream().filter(e -> queryId.equals(e.getUid())).findAny().orElse(null); } if (!remoteEnabled()) { @@ -437,11 +441,13 @@ private Addon convertTopicToAddon(DiscourseTopicResponseDTO topic) { boolean installed = addonHandlers.stream() .anyMatch(handler -> handler.supports(type, contentType) && handler.isInstalled(uid)); - return Addon.create(uid).withType(type).withId(id).withContentType(contentType).withLabel(topic.title) - .withImageLink(topic.imageUrl).withLink(COMMUNITY_TOPIC_URL + topic.id.toString()) + Addon.Builder builder = Addon.create(uid).withType(type).withId(id).withContentType(contentType) + .withLabel(topic.title).withImageLink(topic.imageUrl) + .withLink(COMMUNITY_TOPIC_URL + topic.id.toString()) .withAuthor(topic.postStream.posts[0].displayUsername).withMaturity(maturity) - .withDetailedDescription(detailedDescription).withInstalled(installed).withProperties(properties) - .build(); + .withDetailedDescription(detailedDescription).withInstalled(installed).withProperties(properties); + + return builder.build(); } private @Nullable String determineIdFromUrl(String url) { diff --git a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/JsonAddonService.java b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/JsonAddonService.java index 626c6852724..0b39fd1460c 100644 --- a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/JsonAddonService.java +++ b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/JsonAddonService.java @@ -30,6 +30,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.addon.Addon; +import org.openhab.core.addon.AddonInfoRegistry; import org.openhab.core.addon.AddonService; import org.openhab.core.addon.marketplace.AbstractRemoteAddonService; import org.openhab.core.addon.marketplace.MarketplaceAddonHandler; @@ -78,8 +79,9 @@ public class JsonAddonService extends AbstractRemoteAddonService { @Activate public JsonAddonService(@Reference EventPublisher eventPublisher, @Reference StorageService storageService, - @Reference ConfigurationAdmin configurationAdmin, Map config) { - super(eventPublisher, configurationAdmin, storageService, SERVICE_PID); + @Reference ConfigurationAdmin configurationAdmin, @Reference AddonInfoRegistry addonInfoRegistry, + Map config) { + super(eventPublisher, configurationAdmin, storageService, addonInfoRegistry, SERVICE_PID); modified(config); } diff --git a/bundles/org.openhab.core.addon.marketplace/src/test/java/org/openhab/core/addon/marketplace/AbstractRemoteAddonServiceTest.java b/bundles/org.openhab.core.addon.marketplace/src/test/java/org/openhab/core/addon/marketplace/AbstractRemoteAddonServiceTest.java index 58004557856..dac9fa3fc09 100644 --- a/bundles/org.openhab.core.addon.marketplace/src/test/java/org/openhab/core/addon/marketplace/AbstractRemoteAddonServiceTest.java +++ b/bundles/org.openhab.core.addon.marketplace/src/test/java/org/openhab/core/addon/marketplace/AbstractRemoteAddonServiceTest.java @@ -43,6 +43,7 @@ import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.openhab.core.addon.Addon; +import org.openhab.core.addon.AddonInfoRegistry; import org.openhab.core.addon.marketplace.test.TestAddonHandler; import org.openhab.core.addon.marketplace.test.TestAddonService; import org.openhab.core.events.Event; @@ -64,6 +65,7 @@ @NonNullByDefault public class AbstractRemoteAddonServiceTest { private @Mock @NonNullByDefault({}) StorageService storageService; + private @Mock @NonNullByDefault({}) AddonInfoRegistry addonInfoRegistry; private @Mock @NonNullByDefault({}) ConfigurationAdmin configurationAdmin; private @Mock @NonNullByDefault({}) EventPublisher eventPublisher; private @Mock @NonNullByDefault({}) Configuration configuration; @@ -82,7 +84,7 @@ public void initialize() throws IOException { addonHandler = new TestAddonHandler(); - addonService = new TestAddonService(eventPublisher, configurationAdmin, storageService); + addonService = new TestAddonService(eventPublisher, configurationAdmin, storageService, addonInfoRegistry); addonService.addAddonHandler(addonHandler); } @@ -93,7 +95,7 @@ public void testSourceNotRefreshedIfAddonHandlerNotReady() { addonHandler = new TestAddonHandler(); addonHandler.setReady(false); - addonService = new TestAddonService(eventPublisher, configurationAdmin, storageService); + addonService = new TestAddonService(eventPublisher, configurationAdmin, storageService, addonInfoRegistry); addonService.addAddonHandler(addonHandler); List addons = addonService.getAddons(null); diff --git a/bundles/org.openhab.core.addon.marketplace/src/test/java/org/openhab/core/addon/marketplace/test/TestAddonService.java b/bundles/org.openhab.core.addon.marketplace/src/test/java/org/openhab/core/addon/marketplace/test/TestAddonService.java index 73ffa5cead0..fe5bc672a5a 100644 --- a/bundles/org.openhab.core.addon.marketplace/src/test/java/org/openhab/core/addon/marketplace/test/TestAddonService.java +++ b/bundles/org.openhab.core.addon.marketplace/src/test/java/org/openhab/core/addon/marketplace/test/TestAddonService.java @@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.addon.Addon; +import org.openhab.core.addon.AddonInfoRegistry; import org.openhab.core.addon.marketplace.AbstractRemoteAddonService; import org.openhab.core.addon.marketplace.BundleVersion; import org.openhab.core.addon.marketplace.MarketplaceAddonHandler; @@ -51,8 +52,8 @@ public class TestAddonService extends AbstractRemoteAddonService { private int remoteCalls = 0; public TestAddonService(EventPublisher eventPublisher, ConfigurationAdmin configurationAdmin, - StorageService storageService) { - super(eventPublisher, configurationAdmin, storageService, SERVICE_PID); + StorageService storageService, AddonInfoRegistry addonInfoRegistry) { + super(eventPublisher, configurationAdmin, storageService, addonInfoRegistry, SERVICE_PID); } @Override diff --git a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/Addon.java b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/Addon.java index 84019c79fb9..e1d06175ae9 100644 --- a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/Addon.java +++ b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/Addon.java @@ -304,6 +304,33 @@ public static Builder create(String uid) { return new Builder(uid); } + public static Builder create(Addon addon) { + Addon.Builder builder = new Builder(addon.uid); + builder.id = addon.id; + builder.label = addon.label; + builder.version = addon.version; + builder.maturity = addon.maturity; + builder.compatible = addon.compatible; + builder.contentType = addon.contentType; + builder.link = addon.link; + builder.author = addon.author; + builder.verifiedAuthor = addon.verifiedAuthor; + builder.installed = addon.installed; + builder.type = addon.type; + builder.description = addon.description; + builder.detailedDescription = addon.detailedDescription; + builder.configDescriptionURI = addon.configDescriptionURI; + builder.keywords = addon.keywords; + builder.countries = addon.countries; + builder.license = addon.license; + builder.connection = addon.connection; + builder.backgroundColor = addon.backgroundColor; + builder.imageLink = addon.imageLink; + builder.properties = addon.properties; + builder.loggerPackages = addon.loggerPackages; + return builder; + } + public static class Builder { private final String uid; private String id;