diff --git a/pom.xml b/pom.xml index f24b03ad..6bc05cf8 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ 9.40 1.9.1 1.4.1 - 1.7.1 + 1.7.2 5.2.3 1.1.6.RELEASE diff --git a/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/api/OpmlService.java b/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/api/OpmlService.java index 7c37e9ce..51667ac1 100644 --- a/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/api/OpmlService.java +++ b/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/api/OpmlService.java @@ -1,13 +1,12 @@ package fr.ght1pc9kc.baywatch.opml.api; -import org.springframework.core.io.buffer.DataBuffer; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.io.InputStream; +import java.util.function.Supplier; public interface OpmlService { Mono opmlExport(); - Mono opmlImport(Flux data); + Mono opmlImport(Supplier inputSupplier); } diff --git a/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/domain/OpmlServiceImpl.java b/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/domain/OpmlServiceImpl.java index 3d4a958c..0d190219 100644 --- a/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/domain/OpmlServiceImpl.java +++ b/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/domain/OpmlServiceImpl.java @@ -1,18 +1,16 @@ package fr.ght1pc9kc.baywatch.opml.domain; import com.machinezoo.noexception.Exceptions; +import fr.ght1pc9kc.baywatch.common.domain.QueryContext; import fr.ght1pc9kc.baywatch.opml.api.OpmlService; import fr.ght1pc9kc.baywatch.security.api.AuthenticationFacade; import fr.ght1pc9kc.baywatch.security.api.model.User; import fr.ght1pc9kc.baywatch.security.domain.exceptions.UnauthenticatedUser; import fr.ght1pc9kc.baywatch.techwatch.api.model.WebFeed; -import fr.ght1pc9kc.baywatch.common.domain.QueryContext; import fr.ght1pc9kc.baywatch.techwatch.infra.persistence.FeedRepository; import fr.ght1pc9kc.entity.api.Entity; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -20,6 +18,7 @@ import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import java.util.function.Supplier; @Slf4j @RequiredArgsConstructor @@ -47,25 +46,17 @@ public Mono opmlExport() { } @Override - public Mono opmlImport(Flux data) { - log.debug("Start importing..."); + public Mono opmlImport(Supplier inputSupplier) { return authFacade.getConnectedUser() .switchIfEmpty(Mono.error(new UnauthenticatedUser("Authentication not found !"))) .flatMapMany(Exceptions.wrap().function(owner -> { - PipedOutputStream pos = new PipedOutputStream(); - PipedInputStream pis = new PipedInputStream(pos); - Flux> feeds = readOpml(pis); - Mono> db = DataBufferUtils.write(data, pos) - .map(DataBufferUtils::release) - .doOnTerminate(Exceptions.wrap().runnable(() -> { - pos.flush(); - pos.close(); - })) - .then(Mono.empty()); - return Flux.merge(db, feeds) + InputStream is = inputSupplier.get(); + return readOpml(is) .buffer(100) .flatMap(f -> feedRepository.persist(f).collectList()) - .flatMap(f -> feedRepository.persistUserRelation(owner.id(), f)); + .flatMap(f -> feedRepository.persistUserRelation(owner.id(), f)) + .doOnTerminate(Exceptions.wrap().runnable(is::close)); + })).then(); } diff --git a/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/infra/OpmlController.java b/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/infra/OpmlController.java index d0b0f6f5..ec72b67e 100644 --- a/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/infra/OpmlController.java +++ b/sandside/src/main/java/fr/ght1pc9kc/baywatch/opml/infra/OpmlController.java @@ -1,11 +1,13 @@ package fr.ght1pc9kc.baywatch.opml.infra; +import com.machinezoo.noexception.Exceptions; import fr.ght1pc9kc.baywatch.opml.api.OpmlService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -20,9 +22,13 @@ import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; @@ -34,6 +40,7 @@ public class OpmlController { private final OpmlService opmlService; + private final Scheduler uploadReader = Schedulers.boundedElastic(); @ResponseBody @GetMapping("/export/baywatch.opml") @@ -51,8 +58,19 @@ public Mono> exportOpml() { } @PostMapping("/import") + @SuppressWarnings("CallingSubscribeInNonBlockingScope") public Mono importOpml(@RequestPart("opml") Mono opmlFilePart) { Flux data = opmlFilePart.flatMapMany(Part::content); - return opmlService.opmlImport(data); + + PipedOutputStream pos = new PipedOutputStream(); + DataBufferUtils.write(data, pos) + .doOnTerminate(Exceptions.wrap().runnable(pos::close)) + .subscribe( + DataBufferUtils.releaseConsumer(), + t -> log.atError().log("STACKTRACE", t) + ); + + return opmlService.opmlImport(Exceptions.wrap().supplier(() -> new PipedInputStream(pos))) + .subscribeOn(uploadReader); } -} +} \ No newline at end of file diff --git a/sandside/src/main/java/fr/ght1pc9kc/baywatch/techwatch/infra/persistence/FeedRepository.java b/sandside/src/main/java/fr/ght1pc9kc/baywatch/techwatch/infra/persistence/FeedRepository.java index 99c2844c..4ae26422 100644 --- a/sandside/src/main/java/fr/ght1pc9kc/baywatch/techwatch/infra/persistence/FeedRepository.java +++ b/sandside/src/main/java/fr/ght1pc9kc/baywatch/techwatch/infra/persistence/FeedRepository.java @@ -94,9 +94,9 @@ public Flux>> getFeedProperties( } if (nonNull(properties)) { if (properties.size() == 1) { - FEEDS_USERS_PROPERTIES.FUPR_PROPERTY_NAME.eq(properties.iterator().next().name()); + query.addConditions(FEEDS_USERS_PROPERTIES.FUPR_PROPERTY_NAME.eq(properties.iterator().next().name())); } else if (!properties.isEmpty()) { - FEEDS_USERS_PROPERTIES.FUPR_PROPERTY_NAME.in(properties.stream().map(FeedProperties::name).toList()); + query.addConditions(FEEDS_USERS_PROPERTIES.FUPR_PROPERTY_NAME.in(properties.stream().map(FeedProperties::name).toList())); } } query.addOrderBy(FEEDS_USERS_PROPERTIES.FUPR_USER_ID, FEEDS_USERS_PROPERTIES.FUPR_FEED_ID); diff --git a/sandside/src/test/java/fr/ght1pc9kc/baywatch/opml/domain/OpmlServiceImplTest.java b/sandside/src/test/java/fr/ght1pc9kc/baywatch/opml/domain/OpmlServiceImplTest.java new file mode 100644 index 00000000..b5b044fb --- /dev/null +++ b/sandside/src/test/java/fr/ght1pc9kc/baywatch/opml/domain/OpmlServiceImplTest.java @@ -0,0 +1,75 @@ +package fr.ght1pc9kc.baywatch.opml.domain; + +import fr.ght1pc9kc.baywatch.common.domain.QueryContext; +import fr.ght1pc9kc.baywatch.opml.api.OpmlService; +import fr.ght1pc9kc.baywatch.security.api.AuthenticationFacade; +import fr.ght1pc9kc.baywatch.security.infra.adapters.SpringAuthenticationContext; +import fr.ght1pc9kc.baywatch.techwatch.api.model.WebFeed; +import fr.ght1pc9kc.baywatch.techwatch.infra.persistence.FeedRepository; +import fr.ght1pc9kc.baywatch.tests.samples.FeedSamples; +import fr.ght1pc9kc.baywatch.tests.samples.UserSamples; +import fr.ght1pc9kc.entity.api.Entity; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.nio.charset.StandardCharsets; +import java.util.Collection; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +class OpmlServiceImplTest { + + private OpmlService tested; + + private AuthenticationFacade authenticationFacadeMock; + private FeedRepository feedRepositoryMock; + + @BeforeEach + void setUp() { + feedRepositoryMock = mock(FeedRepository.class); + doReturn(Flux.just(FeedSamples.JEDI)).when(feedRepositoryMock).list(any(QueryContext.class)); + doAnswer(answer -> Flux.fromIterable(answer.getArgument(0))).when(feedRepositoryMock).persist(any()); + doAnswer(answer -> Flux.fromIterable(answer.getArgument(1))).when(feedRepositoryMock).persistUserRelation(anyString(), any()); + + authenticationFacadeMock = spy(new SpringAuthenticationContext()); + tested = new OpmlServiceImpl(feedRepositoryMock, authenticationFacadeMock, OpmlWriter::new, OpmlReader::new); + } + + @Test + void should_export_opml() { + doReturn(Mono.just(UserSamples.OBIWAN)).when(authenticationFacadeMock).getConnectedUser(); + + StepVerifier.create(tested.opmlExport()) + .assertNext(actual -> Assertions.assertThat(actual).asString(StandardCharsets.UTF_8) + .contains("Obiwan Kenobi") + .contains("Baywatch OPML export") + .containsIgnoringNewLines(""" + + """)) + .verifyComplete(); + } + + @Test + @SuppressWarnings("unchecked") + void should_import_opml() { + doReturn(Mono.just(UserSamples.OBIWAN)).when(authenticationFacadeMock).getConnectedUser(); + + StepVerifier.create(tested.opmlImport(() -> OpmlServiceImplTest.class.getResourceAsStream("okenobi.xml"))) + .verifyComplete(); + + ArgumentCaptor>> captor = ArgumentCaptor.forClass(Collection.class); + verify(feedRepositoryMock).persist(captor.capture()); + Assertions.assertThat(captor.getValue()).hasSize(30); + } +} \ No newline at end of file diff --git a/sandside/src/test/java/fr/ght1pc9kc/baywatch/techwatch/infra/persistence/FeedRepositoryTest.java b/sandside/src/test/java/fr/ght1pc9kc/baywatch/techwatch/infra/persistence/FeedRepositoryTest.java index eeb25ce8..61966765 100644 --- a/sandside/src/test/java/fr/ght1pc9kc/baywatch/techwatch/infra/persistence/FeedRepositoryTest.java +++ b/sandside/src/test/java/fr/ght1pc9kc/baywatch/techwatch/infra/persistence/FeedRepositoryTest.java @@ -4,11 +4,14 @@ import fr.ght1pc9kc.baywatch.common.domain.Hasher; import fr.ght1pc9kc.baywatch.common.domain.QueryContext; import fr.ght1pc9kc.baywatch.dsl.tables.records.FeedsRecord; +import fr.ght1pc9kc.baywatch.dsl.tables.records.FeedsUsersPropertiesRecord; import fr.ght1pc9kc.baywatch.dsl.tables.records.FeedsUsersRecord; import fr.ght1pc9kc.baywatch.techwatch.api.model.WebFeed; import fr.ght1pc9kc.baywatch.techwatch.infra.config.TechwatchMapper; import fr.ght1pc9kc.baywatch.techwatch.infra.model.FeedDeletedResult; +import fr.ght1pc9kc.baywatch.techwatch.infra.model.FeedProperties; import fr.ght1pc9kc.baywatch.tests.samples.infra.FeedRecordSamples; +import fr.ght1pc9kc.baywatch.tests.samples.infra.FeedsUsersPropertiesRecordSample; import fr.ght1pc9kc.baywatch.tests.samples.infra.FeedsUsersRecordSample; import fr.ght1pc9kc.baywatch.tests.samples.infra.NewsRecordSamples; import fr.ght1pc9kc.baywatch.tests.samples.infra.UsersRecordSamples; @@ -19,6 +22,8 @@ import fr.ght1pc9kc.testy.jooq.WithDslContext; import fr.ght1pc9kc.testy.jooq.WithInMemoryDatasource; import fr.ght1pc9kc.testy.jooq.WithSampleDataLoaded; +import org.apache.commons.lang3.stream.Streams; +import org.assertj.core.api.Assertions; import org.assertj.core.api.SoftAssertions; import org.jooq.DSLContext; import org.junit.jupiter.api.BeforeEach; @@ -32,7 +37,9 @@ import java.net.URI; import java.time.Instant; import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Map; import java.util.Set; import static fr.ght1pc9kc.baywatch.common.api.model.EntitiesProperties.COUNT; @@ -40,7 +47,9 @@ import static fr.ght1pc9kc.baywatch.common.api.model.FeedMeta.updated; import static fr.ght1pc9kc.baywatch.dsl.tables.Feeds.FEEDS; import static fr.ght1pc9kc.baywatch.dsl.tables.FeedsUsers.FEEDS_USERS; +import static fr.ght1pc9kc.baywatch.dsl.tables.FeedsUsersProperties.FEEDS_USERS_PROPERTIES; import static fr.ght1pc9kc.baywatch.tests.samples.UserSamples.OBIWAN; +import static fr.ght1pc9kc.baywatch.tests.samples.infra.UsersRecordSamples.LSKYWALKER; import static fr.ght1pc9kc.baywatch.tests.samples.infra.UsersRecordSamples.OKENOBI; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; @@ -57,6 +66,7 @@ class FeedRepositoryTest { .addDataset(NewsRecordSamples.SAMPLE) .addDataset(NewsRecordSamples.NewsFeedsRecordSample.SAMPLE) .addDataset(FeedsUsersRecordSample.SAMPLE) + .addDataset(FeedsUsersPropertiesRecordSample.SAMPLE) .build(); @RegisterExtension @@ -75,7 +85,8 @@ void setUp(DSLContext dslContext) { } @Test - void should_get_user_feed() { + void should_get_user_feed(WithSampleDataLoaded.Tracker tracker) { + tracker.skipNextSampleLoad(); FeedsRecord expected = FeedRecordSamples.SAMPLE.records().getFirst(); Entity actual = tested.get(QueryContext.id(expected.getFeedId())).block(); @@ -244,9 +255,137 @@ void should_delete_feeds_for_user(DSLContext dsl) { } @Test - void should_list_orphan_feed() { + void should_list_orphan_feed(WithSampleDataLoaded.Tracker tracker) { + tracker.skipNextSampleLoad(); List> actuals = tested.list(QueryContext.all(Criteria.property(COUNT).eq(0))).collectList().block(); assertThat(actuals).extracting(Entity::id).containsExactly( FeedRecordSamples.FEEDS_RECORDS.getLast().getFeedId()); } + + @Test + void should_get_feed_user_properties(WithSampleDataLoaded.Tracker tracker) { + tracker.skipNextSampleLoad(); + StepVerifier.create(tested.getFeedProperties( + OKENOBI.getUserId(), + List.of(FeedRecordSamples.JEDI.getFeedId()), + EnumSet.of(FeedProperties.NAME) + )) + .assertNext(actual -> Assertions.assertThat(actual).isEqualTo(Entity.identify( + Map.of(FeedProperties.NAME, "Customized for Obiwan JEDI Name")) + .meta(FeedMeta.createdBy, OKENOBI.getUserId()) + .withId(FeedRecordSamples.JEDI.getFeedId()) + )) + .verifyComplete(); + + StepVerifier.create(tested.getFeedProperties( + OKENOBI.getUserId(), + List.of(FeedRecordSamples.JEDI.getFeedId()), + null + )) + .assertNext(actual -> Assertions.assertThat(actual).isEqualTo(Entity.identify( + Map.of( + FeedProperties.NAME, "Customized for Obiwan JEDI Name", + FeedProperties.TAG, "light,republic" + )) + .meta(FeedMeta.createdBy, OKENOBI.getUserId()) + .withId(FeedRecordSamples.JEDI.getFeedId()) + )) + .verifyComplete(); + + StepVerifier.create(tested.getFeedProperties( + OKENOBI.getUserId(), + List.of(FeedRecordSamples.JEDI.getFeedId()), + EnumSet.of(FeedProperties.NAME, FeedProperties.TAG) + )) + .assertNext(actual -> Assertions.assertThat(actual).isEqualTo(Entity.identify( + Map.of( + FeedProperties.NAME, "Customized for Obiwan JEDI Name", + FeedProperties.TAG, "light,republic" + )) + .meta(FeedMeta.createdBy, OKENOBI.getUserId()) + .withId(FeedRecordSamples.JEDI.getFeedId()) + )) + .verifyComplete(); + + StepVerifier.create(tested.getFeedProperties( + OKENOBI.getUserId(), + List.of(FeedRecordSamples.JEDI.getFeedId()), + EnumSet.noneOf(FeedProperties.class) + )) + .assertNext(actual -> Assertions.assertThat(actual).isEqualTo(Entity.identify( + Map.of( + FeedProperties.NAME, "Customized for Obiwan JEDI Name", + FeedProperties.TAG, "light,republic" + )) + .meta(FeedMeta.createdBy, OKENOBI.getUserId()) + .withId(FeedRecordSamples.JEDI.getFeedId()) + )) + .verifyComplete(); + + StepVerifier.create(tested.getFeedProperties( + OKENOBI.getUserId(), + List.of(FeedRecordSamples.JEDI.getFeedId(), "42"), + EnumSet.of(FeedProperties.NAME) + )) + .assertNext(actual -> Assertions.assertThat(actual).isEqualTo(Entity.identify( + Map.of(FeedProperties.NAME, "Customized for Obiwan JEDI Name")) + .meta(FeedMeta.createdBy, OKENOBI.getUserId()) + .withId(FeedRecordSamples.JEDI.getFeedId()) + )) + .verifyComplete(); + + StepVerifier.create(tested.getFeedProperties( + OKENOBI.getUserId(), + List.of(), + EnumSet.of(FeedProperties.NAME) + )).verifyError(IllegalArgumentException.class); + } + + @Test + void should_set_feed_user_properties(DSLContext dslContext) { + FeedsUsersPropertiesRecord beforeRecord = dslContext.selectFrom(FEEDS_USERS_PROPERTIES).where( + FEEDS_USERS_PROPERTIES.FUPR_FEED_ID.eq(FeedRecordSamples.JEDI.getFeedId()), + FEEDS_USERS_PROPERTIES.FUPR_USER_ID.eq(LSKYWALKER.getUserId()) + ).fetchOne(); + + Assertions.assertThat(beforeRecord).isNull(); + + StepVerifier.create(tested.setFeedProperties(LSKYWALKER.getUserId(), List.of( + Entity.identify(WebFeed.builder() + .location(URI.create("https://jedi.com")) + .name("Customized for Obiwan JEDI Name") + .description("Customized for Obiwan JEDI Description") + .tags(Set.of("light", "good")) + .build()) + .withId(FeedRecordSamples.JEDI.getFeedId()) + ))).verifyComplete(); + + FeedsUsersPropertiesRecord[] actualRecord = dslContext.selectFrom(FEEDS_USERS_PROPERTIES).where( + FEEDS_USERS_PROPERTIES.FUPR_FEED_ID.eq(FeedRecordSamples.JEDI.getFeedId()), + FEEDS_USERS_PROPERTIES.FUPR_USER_ID.eq(LSKYWALKER.getUserId()) + ).fetchArray(); + + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(actualRecord) + .isNotNull() + .hasSize(4); + + softly.assertThat(Streams.of(actualRecord).map(FeedsUsersPropertiesRecord::getFuprPropertyName)) + .containsExactly( + FeedProperties.NAME.name(), + FeedProperties.DESCRIPTION.name(), + FeedProperties.TAG.name(), + FeedProperties.TAG.name() + ); + + softly.assertThat(Streams.of(actualRecord).map(FeedsUsersPropertiesRecord::getFuprPropertyValue)) + .containsExactlyInAnyOrder( + "Customized for Obiwan JEDI Name", + "Customized for Obiwan JEDI Description", + "light", + "good" + ); + }); + + } } \ No newline at end of file diff --git a/sandside/src/test/java/fr/ght1pc9kc/baywatch/tests/samples/infra/FeedsUsersPropertiesRecordSample.java b/sandside/src/test/java/fr/ght1pc9kc/baywatch/tests/samples/infra/FeedsUsersPropertiesRecordSample.java new file mode 100644 index 00000000..23a46bf6 --- /dev/null +++ b/sandside/src/test/java/fr/ght1pc9kc/baywatch/tests/samples/infra/FeedsUsersPropertiesRecordSample.java @@ -0,0 +1,39 @@ +package fr.ght1pc9kc.baywatch.tests.samples.infra; + +import fr.ght1pc9kc.baywatch.dsl.tables.FeedsUsersProperties; +import fr.ght1pc9kc.baywatch.dsl.tables.records.FeedsUsersPropertiesRecord; +import fr.ght1pc9kc.baywatch.techwatch.infra.model.FeedProperties; +import fr.ght1pc9kc.testy.jooq.model.RelationalDataSet; + +import java.util.List; + +public class FeedsUsersPropertiesRecordSample implements RelationalDataSet { + public static final FeedsUsersPropertiesRecordSample SAMPLE = new FeedsUsersPropertiesRecordSample(); + + private static final FeedsUsersPropertiesRecord OBIWAN_JEDI_NAME_PROPERTIES = FeedsUsersProperties.FEEDS_USERS_PROPERTIES.newRecord() + .setFuprFeedId(FeedRecordSamples.JEDI.getFeedId()) + .setFuprUserId(UsersRecordSamples.OKENOBI.getUserId()) + .setFuprPropertyName(FeedProperties.NAME.name()) + .setFuprPropertyValue("Customized for Obiwan JEDI Name"); + + private static final FeedsUsersPropertiesRecord OBIWAN_JEDI_TAG1_PROPERTIES = FeedsUsersProperties.FEEDS_USERS_PROPERTIES.newRecord() + .setFuprFeedId(FeedRecordSamples.JEDI.getFeedId()) + .setFuprUserId(UsersRecordSamples.OKENOBI.getUserId()) + .setFuprPropertyName(FeedProperties.TAG.name()) + .setFuprPropertyValue("light"); + + private static final FeedsUsersPropertiesRecord OBIWAN_JEDI_TAG2_PROPERTIES = FeedsUsersProperties.FEEDS_USERS_PROPERTIES.newRecord() + .setFuprFeedId(FeedRecordSamples.JEDI.getFeedId()) + .setFuprUserId(UsersRecordSamples.OKENOBI.getUserId()) + .setFuprPropertyName(FeedProperties.TAG.name()) + .setFuprPropertyValue("republic"); + + @Override + public List records() { + return List.of( + OBIWAN_JEDI_NAME_PROPERTIES, + OBIWAN_JEDI_TAG1_PROPERTIES, + OBIWAN_JEDI_TAG2_PROPERTIES + ); + } +} diff --git a/sandside/src/test/resources/fr/ght1pc9kc/baywatch/opml/domain/okenobi.xml b/sandside/src/test/resources/fr/ght1pc9kc/baywatch/opml/domain/okenobi.xml new file mode 100644 index 00000000..531402e9 --- /dev/null +++ b/sandside/src/test/resources/fr/ght1pc9kc/baywatch/opml/domain/okenobi.xml @@ -0,0 +1,72 @@ + + + + Baywatch OPML export + Sun, 28 Apr 2024 17:47:47 GMT + Fred + marthym@gmail.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file