/* * Copyright (c) 2019-2020 "Neo4j," * Neo4j Sweden AB [https://neo4j.com] * * This file is part of Neo4j. * * 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 * * https://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 org.neo4j.springframework.data.integration.reactive; import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assumptions.*; import static org.neo4j.springframework.data.test.Neo4jExtension.*; import reactor.test.StepVerifier; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.neo4j.driver.Driver; import org.neo4j.driver.Transaction; import org.neo4j.driver.Values; import org.neo4j.springframework.data.config.AbstractReactiveNeo4jConfig; import org.neo4j.springframework.data.integration.shared.DynamicRelationshipsITBase; import org.neo4j.springframework.data.integration.shared.Person; import org.neo4j.springframework.data.integration.shared.PersonWithStringlyTypedRelatives; import org.neo4j.springframework.data.integration.shared.Pet; import org.neo4j.springframework.data.repository.ReactiveNeo4jRepository; import org.neo4j.springframework.data.repository.config.EnableReactiveNeo4jRepositories; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * @author Michael J. Simons */ @Tag(NEEDS_REACTIVE_SUPPORT) class ReactiveStringlyTypeDynamicRelationshipsIT extends DynamicRelationshipsITBase<PersonWithStringlyTypedRelatives> { @Autowired ReactiveStringlyTypeDynamicRelationshipsIT(Driver driver) { super(driver); } @Test void shouldReadDynamicRelationships(@Autowired PersonWithRelativesRepository repository) { repository.findById(idOfExistingPerson) .as(StepVerifier::create) .consumeNextWith(person -> { assertThat(person).isNotNull(); assertThat(person.getName()).isEqualTo("A"); Map<String, Person> relatives = person.getRelatives(); assertThat(relatives).containsOnlyKeys("HAS_WIFE", "HAS_DAUGHTER"); assertThat(relatives.get("HAS_WIFE").getFirstName()).isEqualTo("B"); assertThat(relatives.get("HAS_DAUGHTER").getFirstName()).isEqualTo("C"); }) .verifyComplete(); } @Test // GH-216 void shouldReadDynamicCollectionRelationships(@Autowired PersonWithRelativesRepository repository) { repository.findById(idOfExistingPerson) .as(StepVerifier::create) .consumeNextWith(person -> { assertThat(person).isNotNull(); assertThat(person.getName()).isEqualTo("A"); Map<String, List<Pet>> pets = person.getPets(); assertThat(pets).containsOnlyKeys("CATS", "DOGS"); assertThat(pets.get("CATS")).extracting(Pet::getName).containsExactlyInAnyOrder("Tom", "Garfield"); assertThat(pets.get("DOGS")).extracting(Pet::getName).containsExactlyInAnyOrder("Benji", "Lassie"); }) .verifyComplete(); } @Test void shouldUpdateDynamicRelationships(@Autowired PersonWithRelativesRepository repository) { repository.findById(idOfExistingPerson) .map(person -> { assumeThat(person).isNotNull(); assumeThat(person.getName()).isEqualTo("A"); Map<String, Person> relatives = person.getRelatives(); assumeThat(relatives).containsOnlyKeys("HAS_WIFE", "HAS_DAUGHTER"); relatives.remove("HAS_WIFE"); Person d = new Person(); ReflectionTestUtils.setField(d, "firstName", "D"); relatives.put("HAS_SON", d); ReflectionTestUtils.setField(relatives.get("HAS_DAUGHTER"), "firstName", "C2"); return person; }) .flatMap(repository::save) .as(StepVerifier::create) .consumeNextWith(person -> { Map<String, Person> relatives = person.getRelatives(); assertThat(relatives).containsOnlyKeys("HAS_DAUGHTER", "HAS_SON"); assertThat(relatives.get("HAS_DAUGHTER").getFirstName()).isEqualTo("C2"); assertThat(relatives.get("HAS_SON").getFirstName()).isEqualTo("D"); }) .verifyComplete(); } @Test // GH-216 void shouldUpdateDynamicCollectionRelationships(@Autowired PersonWithRelativesRepository repository) { repository.findById(idOfExistingPerson) .map(person -> { assumeThat(person).isNotNull(); assumeThat(person.getName()).isEqualTo("A"); Map<String, List<Pet>> pets = person.getPets(); assertThat(pets).containsOnlyKeys("CATS", "DOGS"); pets.remove("DOGS"); pets.get("CATS").add(new Pet("Delilah")); pets.put("FISH", Collections.singletonList(new Pet("Nemo"))); return person; }) .flatMap(repository::save) .as(StepVerifier::create) .consumeNextWith(person -> { Map<String, List<Pet>> pets = person.getPets(); assertThat(pets).containsOnlyKeys("CATS", "FISH"); assertThat(pets.get("CATS")).extracting(Pet::getName).containsExactlyInAnyOrder("Tom", "Garfield", "Delilah"); assertThat(pets.get("FISH")).extracting(Pet::getName).containsExactlyInAnyOrder("Nemo"); }) .verifyComplete(); } @Test void shouldWriteDynamicRelationships(@Autowired PersonWithRelativesRepository repository) { PersonWithStringlyTypedRelatives newPerson = new PersonWithStringlyTypedRelatives("Test"); Person d = new Person(); ReflectionTestUtils.setField(d, "firstName", "R1"); newPerson.getRelatives().put("RELATIVE_1", d); d = new Person(); ReflectionTestUtils.setField(d, "firstName", "R2"); newPerson.getRelatives().put("RELATIVE_2", d); List<PersonWithStringlyTypedRelatives> recorded = new ArrayList<>(); repository.save(newPerson) .as(StepVerifier::create) .recordWith(() -> recorded) .consumeNextWith(personWithRelatives -> { Map<String, Person> relatives = personWithRelatives.getRelatives(); assertThat(relatives).containsOnlyKeys("RELATIVE_1", "RELATIVE_2"); }) .verifyComplete(); try (Transaction transaction = driver.session().beginTransaction()) { long numberOfRelations = transaction.run("" + "MATCH (t:" + labelOfTestSubject + ") WHERE id(t) = $id " + "RETURN size((t)-->(:Person))" + " as numberOfRelations", Values.parameters("id", recorded.get(0).getId())) .single().get("numberOfRelations").asLong(); assertThat(numberOfRelations).isEqualTo(2L); } } @Test // GH-216 void shouldWriteDynamicCollectionRelationships(@Autowired PersonWithRelativesRepository repository) { PersonWithStringlyTypedRelatives newPerson = new PersonWithStringlyTypedRelatives("Test"); Map<String, List<Pet>> pets = newPerson.getPets(); List<Pet> monsters = pets.computeIfAbsent("MONSTERS", s -> new ArrayList<>()); monsters.add(new Pet("Godzilla")); monsters.add(new Pet("King Kong")); List<Pet> fish = pets.computeIfAbsent("FISH", s -> new ArrayList<>()); fish.add(new Pet("Nemo")); List<PersonWithStringlyTypedRelatives> recorded = new ArrayList<>(); repository.save(newPerson) .as(StepVerifier::create) .recordWith(() -> recorded) .consumeNextWith(person -> { Map<String, List<Pet>> writtenPets = person.getPets(); assertThat(writtenPets).containsOnlyKeys("MONSTERS", "FISH"); }) .verifyComplete(); try (Transaction transaction = driver.session().beginTransaction()) { long numberOfRelations = transaction.run("" + "MATCH (t:" + labelOfTestSubject + ") WHERE id(t) = $id " + "RETURN size((t)-->(:Pet))" + " as numberOfRelations", Values.parameters("id", recorded.get(0).getId())) .single().get("numberOfRelations").asLong(); assertThat(numberOfRelations).isEqualTo(3L); } } interface PersonWithRelativesRepository extends ReactiveNeo4jRepository<PersonWithStringlyTypedRelatives, Long> { } @Configuration @EnableTransactionManagement @EnableReactiveNeo4jRepositories(considerNestedRepositories = true) static class Config extends AbstractReactiveNeo4jConfig { @Bean public Driver driver() { return neo4jConnectionSupport.getDriver(); } } }