Skip to content

Commit

Permalink
Add check sources command
Browse files Browse the repository at this point in the history
  • Loading branch information
charphi committed Sep 12, 2024
1 parent 522a88d commit e8415fb
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 129 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

- ![API] Add confidentiality property to WebSource [#518](https://github.com/nbbrd/sdmx-dl/issues/518)
- ![API] Add missing properties methods in extensions points [#781](https://github.com/nbbrd/sdmx-dl/issues/781)
- ![CLI] Add check sources command [#515](https://github.com/nbbrd/sdmx-dl/issues/515)

### Changed

Expand Down
61 changes: 30 additions & 31 deletions sdmx-dl-api/src/main/java/sdmxdl/web/SdmxWebManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,45 +70,60 @@ public class SdmxWebManager extends SdmxManager<WebSource> {
}

@lombok.Singular
@NonNull List<Driver> drivers;
@NonNull
List<Driver> drivers;

@lombok.Singular
@NonNull List<Monitor> monitors;
@NonNull
List<Monitor> monitors;

@lombok.Builder.Default
@NonNull Networking networking = Networking.getDefault();
@NonNull
Networking networking = Networking.getDefault();

@lombok.Builder.Default
@NonNull WebCaching caching = WebCaching.noOp();
@NonNull
WebCaching caching = WebCaching.noOp();

@Nullable EventListener<? super WebSource> onEvent;
@Nullable
EventListener<? super WebSource> onEvent;

@Nullable ErrorListener<? super WebSource> onError;
@Nullable
ErrorListener<? super WebSource> onError;

@lombok.Singular
@NonNull List<Persistence> persistences;
@NonNull
List<Persistence> persistences;

@lombok.Singular
@NonNull List<Authenticator> authenticators;
@NonNull
List<Authenticator> authenticators;

@lombok.Builder.Default
@NonNull Registry registry = Registry.noOp();
@NonNull
Registry registry = Registry.noOp();

@Nullable Consumer<CharSequence> onRegistryEvent;
@Nullable
Consumer<CharSequence> onRegistryEvent;

@Nullable BiConsumer<CharSequence, IOException> onRegistryError;
@Nullable
BiConsumer<CharSequence, IOException> onRegistryError;

@lombok.Getter(lazy = true)
@NonNull List<WebSource> customSources = initLazyCustomSources(getRegistry(), getPersistences(), getOnRegistryEvent(), getOnRegistryError());
@NonNull
List<WebSource> customSources = initLazyCustomSources(getRegistry(), getPersistences(), getOnRegistryEvent(), getOnRegistryError());

@lombok.Getter(lazy = true)
@NonNull List<WebSource> defaultSources = initLazyDefaultSources(getDrivers());
@NonNull
List<WebSource> defaultSources = initLazyDefaultSources(getDrivers());

@lombok.Getter(lazy = true)
@NonNull SortedMap<String, WebSource> sources = initLazySourceMap(getCustomSources(), getDefaultSources());
@NonNull
SortedMap<String, WebSource> sources = initLazySourceMap(getCustomSources(), getDefaultSources());

@lombok.Getter(lazy = true, value = AccessLevel.PRIVATE)
@NonNull WebContext context = initLazyContext();
@NonNull
WebContext context = initLazyContext();

public @NonNull Connection getConnection(@NonNull String name, @NonNull Languages languages) throws IOException {
WebSource source = lookupSource(name)
Expand All @@ -122,8 +137,6 @@ public class SdmxWebManager extends SdmxManager<WebSource> {
Driver driver = lookupDriverById(source.getDriver())
.orElseThrow(() -> new IOException("Failed to find a suitable driver for '" + source + "'"));

checkSourceProperties(source, driver);

return driver.connect(source, languages, getContext());
}

Expand All @@ -147,20 +160,6 @@ public class SdmxWebManager extends SdmxManager<WebSource> {
return monitor.getReport(source, getContext());
}

private void checkSourceProperties(WebSource source, Driver driver) {
if (onEvent != null) {
Collection<String> expected = new ArrayList<>();
expected.addAll(driver.getDriverProperties());
expected.addAll(networking.getNetworkingProperties());
expected.addAll(caching.getWebCachingProperties());
Collection<String> found = source.getProperties().keySet();
String diff = found.stream().filter(item -> !expected.contains(item)).sorted().collect(Collectors.joining(","));
if (!diff.isEmpty()) {
onEvent.accept(source, "WEB_MANAGER", "Unexpected properties [" + diff + "]");
}
}
}

private Optional<WebSource> lookupSource(String name) {
return Optional.ofNullable(getSources().get(name));
}
Expand Down
31 changes: 0 additions & 31 deletions sdmx-dl-api/src/test/java/sdmxdl/web/SdmxWebManagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@

import java.io.IOException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

import static org.assertj.core.api.Assertions.*;
import static sdmxdl.Languages.ANY;
Expand Down Expand Up @@ -239,35 +237,6 @@ public void testGetConnectionOfSource() {
assertThatCode(() -> manager.getConnection(sampleSource.toBuilder().id("other").build(), ANY).close()).doesNotThrowAnyException();
}

@SuppressWarnings("EmptyTryBlock")
@Test
public void testInvalidSourceProperties() throws IOException {
List<String> events = new ArrayList<>();

SdmxWebManager manager = SdmxWebManager
.builder()
.driver(sampleDriver)
.onEvent((source, marker, event) -> events.add(source.getId() + ":" + event))
.build();

WebSource noProp = sampleSource.toBuilder().id("noProp").clearProperties().build();
try (Connection ignored = manager.getConnection(noProp, ANY)) {
}
assertThat(events).isEmpty();

WebSource validProp = sampleSource.toBuilder().id("validProp").build();
try (Connection ignored = manager.getConnection(validProp, ANY)) {
}
assertThat(events).isEmpty();

WebSource invalidProp = sampleSource.toBuilder().id("invalidProp").property("boom", "123").build();
try (Connection ignored = manager.getConnection(invalidProp, ANY)) {
}
assertThat(events).singleElement(as(STRING))
.contains(invalidProp.getId())
.contains("boom");
}

private final DataRepository sample = DataRepository.builder().name("repo").build();
private final WebSource sampleSource = WebSource
.builder()
Expand Down
79 changes: 79 additions & 0 deletions sdmx-dl-cli/src/main/java/internal/sdmxdl/cli/Plugin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package internal.sdmxdl.cli;

import lombok.NonNull;
import sdmxdl.ext.Persistence;
import sdmxdl.file.spi.FileCaching;
import sdmxdl.web.SdmxWebManager;
import sdmxdl.web.spi.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@lombok.Value
public class Plugin {

public enum Type {
DRIVER,
AUTHENTICATOR,
MONITOR,
PERSISTENCE,
REGISTRY,
WEB_CACHING,
FILE_CACHING,
NETWORKING
}

public static List<Plugin> allOf(SdmxWebManager manager) {
List<Plugin> result = new ArrayList<>();
manager.getDrivers().stream().map(Plugin::of).forEach(result::add);
manager.getAuthenticators().stream().map(Plugin::of).forEach(result::add);
manager.getMonitors().stream().map(Plugin::of).forEach(result::add);
manager.getPersistences().stream().map(Plugin::of).forEach(result::add);
result.add(Plugin.of(manager.getRegistry()));
result.add(Plugin.of(manager.getCaching()));
result.add(Plugin.of(manager.getNetworking()));
return result;
}

static Plugin of(Driver o) {
return new Plugin(Type.DRIVER, o.getDriverId(), o.getDriverProperties());
}

static Plugin of(Authenticator o) {
return new Plugin(Type.AUTHENTICATOR, o.getAuthenticatorId(), o.getAuthenticatorProperties());
}

static Plugin of(Monitor o) {
return new Plugin(Type.MONITOR, o.getMonitorId(), o.getMonitorProperties());
}

static Plugin of(Persistence o) {
return new Plugin(Type.PERSISTENCE, o.getPersistenceId(), o.getPersistenceProperties());
}

static Plugin of(Registry o) {
return new Plugin(Type.REGISTRY, o.getRegistryId(), o.getRegistryProperties());
}

static Plugin of(WebCaching o) {
return new Plugin(Type.WEB_CACHING, o.getWebCachingId(), o.getWebCachingProperties());
}

static Plugin of(FileCaching o) {
return new Plugin(Type.FILE_CACHING, o.getFileCachingId(), o.getFileCachingProperties());
}

static Plugin of(Networking o) {
return new Plugin(Type.NETWORKING, o.getNetworkingId(), o.getNetworkingProperties());
}

@NonNull
Type type;

@NonNull
String id;

@NonNull
Collection<String> Properties;
}
1 change: 1 addition & 0 deletions sdmx-dl-cli/src/main/java/sdmxdl/cli/CheckCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
CheckStatusCommand.class,
CheckAccessCommand.class,
CheckConfigCommand.class,
CheckSourcesCommand.class,
CheckRulesCommand.class
}
)
Expand Down
96 changes: 96 additions & 0 deletions sdmx-dl-cli/src/main/java/sdmxdl/cli/CheckSourcesCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright 2018 National Bank of Belgium
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
package sdmxdl.cli;

import internal.sdmxdl.cli.Plugin;
import internal.sdmxdl.cli.WebSourcesOptions;
import internal.sdmxdl.cli.ext.CsvTable;
import internal.sdmxdl.cli.ext.RFC4180OutputOptions;
import picocli.CommandLine;
import sdmxdl.web.SdmxWebManager;
import sdmxdl.web.WebSource;
import sdmxdl.web.spi.Driver;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

/**
* @author Philippe Charles
*/
@CommandLine.Command(name = "sources")
@SuppressWarnings("FieldMayBeFinal")
public final class CheckSourcesCommand implements Callable<Void> {

@CommandLine.Mixin
private WebSourcesOptions web;

@CommandLine.Mixin
private final RFC4180OutputOptions csv = new RFC4180OutputOptions();

@Override
public Void call() throws Exception {
getTable().write(csv, getRows());
return null;
}

private CsvTable<SourceIssue> getTable() {
return CsvTable
.builderOf(SourceIssue.class)
.columnOf("ID", SourceIssue::getId)
.columnOf("Issue", SourceIssue::getIssue)
.build();
}

private Stream<SourceIssue> getRows() throws IOException {
SdmxWebManager manager = web.loadManager();
Stream<String> sources = web.isAllSources() ? WebSourcesOptions.getAllSourceNames(manager) : web.getSources().stream();
List<Plugin> plugins = Plugin.allOf(manager);
return web.applyParallel(sources).map(source -> check(source, manager, plugins));
}

private SourceIssue check(String sourceID, SdmxWebManager manager, List<Plugin> plugins) {
WebSource webSource = manager.getSources().get(sourceID);
if (webSource == null) {
return new SourceIssue(sourceID, "Source not found");
}
Optional<Driver> driver = manager.getDrivers().stream().filter(o -> o.getDriverId().equals(webSource.getDriver())).findFirst();
if (!driver.isPresent()) {
return new SourceIssue(sourceID, "Driver not found");
}
Collection<String> expected = plugins.stream()
.filter(plugin -> !plugin.getType().equals(Plugin.Type.DRIVER) || plugin.getId().equals(driver.get().getDriverId()))
.map(Plugin::getProperties)
.flatMap(Collection::stream)
.collect(toList());
Collection<String> found = webSource.getProperties().keySet();
String result = found.stream().filter(item -> !expected.contains(item)).sorted().collect(Collectors.joining(","));
return new SourceIssue(sourceID, result.isEmpty() ? "No problem" : ("Unknown properties: " + result));
}

@lombok.Value
private static class SourceIssue {
String id;
String issue;
}
}
Loading

0 comments on commit e8415fb

Please sign in to comment.