From 80c7229ddc711fd606dd0137649a3606d7b66713 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 4 Nov 2020 14:20:09 +0100 Subject: [PATCH] #589 - Add Cassandra reactive SpEL example. --- .../spel/ApplicationConfiguration.java | 86 +++++++++++++++++++ .../springdata/cassandra/spel/Employee.java | 33 +++++++ .../cassandra/spel/EmployeeRepository.java | 31 +++++++ .../spel/ExpressionIntegrationTests.java | 72 ++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 cassandra/reactive/src/main/java/example/springdata/cassandra/spel/ApplicationConfiguration.java create mode 100644 cassandra/reactive/src/main/java/example/springdata/cassandra/spel/Employee.java create mode 100644 cassandra/reactive/src/main/java/example/springdata/cassandra/spel/EmployeeRepository.java create mode 100644 cassandra/reactive/src/test/java/example/springdata/cassandra/spel/ExpressionIntegrationTests.java diff --git a/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/ApplicationConfiguration.java b/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/ApplicationConfiguration.java new file mode 100644 index 00000000..1b68f7f1 --- /dev/null +++ b/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/ApplicationConfiguration.java @@ -0,0 +1,86 @@ +/* + * Copyright 2020 the original author or authors. + * + * 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 + * + * http://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 example.springdata.cassandra.spel; + +import lombok.RequiredArgsConstructor; +import lombok.Value; +import reactor.core.publisher.Mono; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.data.spel.spi.EvaluationContextExtension; +import org.springframework.data.spel.spi.ReactiveEvaluationContextExtension; + +/** + * Simple configuration for reactive Cassandra SpEL support. + * + * @author Mark Paluch + */ +@SpringBootApplication +class ApplicationConfiguration { + + @Bean + ReactiveTenantExtension tenantExtension() { + return ReactiveTenantExtension.INSTANCE; + } + + /** + * Extension that looks up a {@link Tenant} from the {@link reactor.util.context.Context}. + */ + enum ReactiveTenantExtension implements ReactiveEvaluationContextExtension { + + INSTANCE; + + @Override + public Mono getExtension() { + return Mono.deferContextual(contextView -> Mono.just(new TenantExtension(contextView.get(Tenant.class)))); + } + + @Override + public String getExtensionId() { + return "my-reactive-tenant-extension"; + } + } + + /** + * Actual extension providing access to the {@link Tenant} value object. + */ + @RequiredArgsConstructor + static class TenantExtension implements EvaluationContextExtension { + + private final Tenant tenant; + + @Override + public String getExtensionId() { + return "my-tenant-extension"; + } + + @Override + public Tenant getRootObject() { + return tenant; + } + } + + /** + * The root object. + */ + @Value + public static class Tenant { + + String tenantId; + + } +} diff --git a/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/Employee.java b/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/Employee.java new file mode 100644 index 00000000..d19c1100 --- /dev/null +++ b/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/Employee.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020 the original author or authors. + * + * 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 + * + * http://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 example.springdata.cassandra.spel; + +import lombok.Value; + +import org.springframework.data.cassandra.core.cql.PrimaryKeyType; +import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn; +import org.springframework.data.cassandra.core.mapping.Table; + +/** + * @author Mark Paluch + */ +@Value +@Table +public class Employee { + + @PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED) String tenantId; + @PrimaryKeyColumn(type = PrimaryKeyType.CLUSTERED) String name; +} diff --git a/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/EmployeeRepository.java b/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/EmployeeRepository.java new file mode 100644 index 00000000..d6accc28 --- /dev/null +++ b/cassandra/reactive/src/main/java/example/springdata/cassandra/spel/EmployeeRepository.java @@ -0,0 +1,31 @@ +/* + * Copyright 2020 the original author or authors. + * + * 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 + * + * http://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 example.springdata.cassandra.spel; + +import reactor.core.publisher.Flux; + +import org.springframework.data.cassandra.repository.Query; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; + +/** + * @author Mark Paluch + */ +public interface EmployeeRepository extends ReactiveCrudRepository { + + @Query("SELECT * FROM employee WHERE tenantId = :#{getTenantId()} AND name = :name") + Flux findAllByName(String name); + +} diff --git a/cassandra/reactive/src/test/java/example/springdata/cassandra/spel/ExpressionIntegrationTests.java b/cassandra/reactive/src/test/java/example/springdata/cassandra/spel/ExpressionIntegrationTests.java new file mode 100644 index 00000000..a0dd7ec4 --- /dev/null +++ b/cassandra/reactive/src/test/java/example/springdata/cassandra/spel/ExpressionIntegrationTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2020 the original author or authors. + * + * 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 + * + * http://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 example.springdata.cassandra.spel; + +import static org.assertj.core.api.Assertions.*; + +import example.springdata.cassandra.spel.ApplicationConfiguration.Tenant; +import example.springdata.cassandra.util.CassandraKeyspace; +import reactor.test.StepVerifier; +import reactor.util.context.Context; + +import java.util.Arrays; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration tests showing the SpEL context extension in action. + * + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@DataCassandraTest +public class ExpressionIntegrationTests { + + @ClassRule public final static CassandraKeyspace CASSANDRA_KEYSPACE = CassandraKeyspace.onLocalhost(); + + @Autowired EmployeeRepository employeeRepository; + + @Before + public void before() { + + employeeRepository.deleteAll().as(StepVerifier::create).verifyComplete(); + + employeeRepository + .saveAll(Arrays.asList(new Employee("breaking-bad", "Walter"), new Employee("breaking-bad", "Hank"), + new Employee("south-park", "Hank"))) // + .as(StepVerifier::create) // + .expectNextCount(3) // + .verifyComplete(); + } + + @Test + public void shouldFindByTenantIdAndName() { + + employeeRepository.findAllByName("Walter") // + .contextWrite(Context.of(Tenant.class, new Tenant("breaking-bad"))).as(StepVerifier::create) // + .assertNext(actual -> { + assertThat(actual.getTenantId()).isEqualTo("breaking-bad"); + }).verifyComplete(); + + } +}