Fix marketplace add-ons missing config description URI (#3688)

Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
J-N-K 2023-07-06 08:26:33 +02:00 committed by GitHub
parent 6a0b268c3e
commit abcfe54678
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 17 deletions

View File

@ -20,6 +20,7 @@ import java.util.Dictionary;
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.eclipse.jdt.annotation.Nullable;
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<List<Addon>> cachedRemoteAddons = new ExpiringCache<>(Duration.ofMinutes(15),
this::getRemoteAddons);
protected final AddonInfoRegistry addonInfoRegistry;
protected List<Addon> cachedAddons = List.of();
protected List<String> 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 @@ public abstract class AbstractRemoteAddonService implements AddonService {
return new BundleVersion(FrameworkUtil.getBundle(OpenHAB.class).getVersion().toString());
}
private Addon convertFromStorage(Map.Entry<String, @Nullable String> 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 abstract class AbstractRemoteAddonService implements AddonService {
}
List<Addon> 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(

View File

@ -36,6 +36,7 @@ import java.util.stream.Stream;
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<String, Object> config) {
super(eventPublisher, configurationAdmin, storageService, SERVICE_PID);
@Reference AddonInfoRegistry addonInfoRegistry, Map<String, Object> config) {
super(eventPublisher, configurationAdmin, storageService, addonInfoRegistry, SERVICE_PID);
modified(config);
}
@ -200,10 +201,13 @@ public class CommunityMarketplaceAddonService extends AbstractRemoteAddonService
@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 @@ public class CommunityMarketplaceAddonService extends AbstractRemoteAddonService
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) {

View File

@ -30,6 +30,7 @@ import java.util.stream.Collectors;
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<String, Object> config) {
super(eventPublisher, configurationAdmin, storageService, SERVICE_PID);
@Reference ConfigurationAdmin configurationAdmin, @Reference AddonInfoRegistry addonInfoRegistry,
Map<String, Object> config) {
super(eventPublisher, configurationAdmin, storageService, addonInfoRegistry, SERVICE_PID);
modified(config);
}

View File

@ -43,6 +43,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
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 @@ import org.osgi.service.cm.ConfigurationAdmin;
@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 class AbstractRemoteAddonServiceTest {
addonHandler = new TestAddonHandler();
addonService = new TestAddonService(eventPublisher, configurationAdmin, storageService);
addonService = new TestAddonService(eventPublisher, configurationAdmin, storageService, addonInfoRegistry);
addonService.addAddonHandler(addonHandler);
}
@ -93,7 +95,7 @@ public class AbstractRemoteAddonServiceTest {
addonHandler = new TestAddonHandler();
addonHandler.setReady(false);
addonService = new TestAddonService(eventPublisher, configurationAdmin, storageService);
addonService = new TestAddonService(eventPublisher, configurationAdmin, storageService, addonInfoRegistry);
addonService.addAddonHandler(addonHandler);
List<Addon> addons = addonService.getAddons(null);

View File

@ -21,6 +21,7 @@ import java.util.stream.Collectors;
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

View File

@ -304,6 +304,33 @@ public class Addon {
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;