Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d7423c6

Browse files
committedMay 17, 2024·
Avoid setting the tenant id if one is preset
1 parent b21030f commit d7423c6

File tree

5 files changed

+141
-0
lines changed

5 files changed

+141
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.micronaut.data.jdbc.h2.multitenancy;
2+
3+
import io.micronaut.core.annotation.Nullable;
4+
import io.micronaut.data.annotation.GeneratedValue;
5+
import io.micronaut.data.annotation.Id;
6+
import io.micronaut.data.annotation.MappedEntity;
7+
import io.micronaut.data.annotation.TenantId;
8+
import io.micronaut.serde.annotation.Serdeable;
9+
10+
@Serdeable // <1>
11+
@MappedEntity // <2>
12+
public record TenancyBook(@Nullable
13+
@Id // <3>
14+
@GeneratedValue // <4>
15+
Long id,
16+
String title,
17+
@TenantId // <5>
18+
String framework) {
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.micronaut.data.jdbc.h2.multitenancy;
2+
3+
import io.micronaut.context.annotation.Requires;
4+
import io.micronaut.http.annotation.Controller;
5+
import io.micronaut.http.annotation.Get;
6+
import io.micronaut.scheduling.TaskExecutors;
7+
import io.micronaut.scheduling.annotation.ExecuteOn;
8+
9+
import java.util.List;
10+
11+
@Requires(property = "spec.name", value = "TenancyBookControllerSpec")
12+
@Controller("/books") // <1>
13+
class TenancyBookController {
14+
private final TenancyBookRepository bookRepository;
15+
16+
TenancyBookController(TenancyBookRepository bookRepository) { // <2>
17+
this.bookRepository = bookRepository;
18+
}
19+
20+
@ExecuteOn(TaskExecutors.BLOCKING) // <3>
21+
@Get
22+
// <4>
23+
List<TenancyBook> index() {
24+
return bookRepository.findAll();
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package io.micronaut.data.jdbc.h2.multitenancy
2+
3+
import io.micronaut.context.annotation.Property
4+
import io.micronaut.core.type.Argument
5+
import io.micronaut.core.util.StringUtils
6+
import io.micronaut.http.HttpRequest
7+
import io.micronaut.http.HttpResponse
8+
import io.micronaut.http.HttpStatus
9+
import io.micronaut.http.client.BlockingHttpClient
10+
import io.micronaut.http.client.HttpClient
11+
import io.micronaut.http.client.annotation.Client
12+
import io.micronaut.test.extensions.spock.annotation.MicronautTest
13+
import jakarta.inject.Inject
14+
import spock.lang.Specification
15+
16+
import static org.junit.Assert.assertEquals
17+
18+
@Property(name = "datasources.default.schema-generate", value = "CREATE_DROP")
19+
// <1>
20+
@Property(name = "datasources.default.url", value = "jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
21+
@Property(name = "datasources.default.username", value = "sa")
22+
@Property(name = "datasources.default.password", value = "")
23+
@Property(name = "datasources.default.dialect", value = "H2")
24+
@Property(name = "datasources.default.driver-class-name", value = "org.h2.Driver")
25+
@Property(name = "micronaut.multitenancy.tenantresolver.httpheader.enabled", value = StringUtils.TRUE)
26+
@Property(name = "spec.name", value = "TenancyBookControllerSpec")
27+
@MicronautTest(transactional = false)
28+
// <2>
29+
class TenancyBookControllerSpec extends Specification {
30+
31+
@Inject
32+
@Client("/")
33+
HttpClient httpClient
34+
35+
@Inject
36+
TenancyBookRepository bookRepository
37+
38+
def multitenancyRequest() {
39+
40+
given:
41+
BlockingHttpClient client = httpClient.toBlocking()
42+
save(bookRepository, client, "Building Microservices with Micronaut", "micronaut")
43+
save(bookRepository, client, "Introducing Micronaut", "micronaut")
44+
save(bookRepository, client, "Grails 3 - Step by Step", "grails")
45+
save(bookRepository, client, "Falando de Grail", "grails")
46+
save(bookRepository, client, "Grails Goodness Notebook", "grails")
47+
48+
when:
49+
List<TenancyBook> books = fetchBooks(client, "micronaut")
50+
then:
51+
books
52+
books.size() == 2
53+
54+
when:
55+
books = fetchBooks(client, "grails")
56+
then:
57+
books
58+
books.size() == 3
59+
60+
cleanup:
61+
bookRepository.deleteAll()
62+
}
63+
64+
List<TenancyBook> fetchBooks(BlockingHttpClient client, String framework) {
65+
HttpRequest<?> request = HttpRequest.GET("/books").header("tenantId", framework)
66+
Argument<List<TenancyBook>> responseArgument = Argument.listOf(TenancyBook.class)
67+
HttpResponse<List<TenancyBook>> response = client.exchange(request, responseArgument)
68+
assertEquals(HttpStatus.OK, response.getStatus())
69+
return response.body()
70+
}
71+
72+
void save(TenancyBookRepository bookRepository, BlockingHttpClient client, String title, String framework) {
73+
bookRepository.save(new TenancyBook(null, title, framework))
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.micronaut.data.jdbc.h2.multitenancy;
2+
3+
import io.micronaut.context.annotation.Requires;
4+
import io.micronaut.data.annotation.WithoutTenantId;
5+
import io.micronaut.data.jdbc.annotation.JdbcRepository;
6+
import io.micronaut.data.model.query.builder.sql.Dialect;
7+
import io.micronaut.data.repository.CrudRepository;
8+
9+
@Requires(property = "spec.name", value = "TenancyBookControllerSpec")
10+
@JdbcRepository(dialect = Dialect.H2) // <1>
11+
public interface TenancyBookRepository extends CrudRepository<TenancyBook, Long> { // <2>
12+
Long save(String title);
13+
14+
@WithoutTenantId
15+
@Override
16+
void deleteAll();
17+
}

‎data-runtime/src/main/java/io/micronaut/data/runtime/event/listeners/TenantIdEntityEventListener.java

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ protected Predicate<RuntimePersistentProperty<Object>> getPropertyPredicate() {
7676
public boolean prePersist(@NonNull EntityEventContext<Object> context) {
7777
for (RuntimePersistentProperty<Object> property : getApplicableProperties(context.getPersistentEntity())) {
7878
if (property.getAnnotationMetadata().hasStereotype(TenantId.class)) {
79+
if (property.getProperty().get(context.getEntity()) != null) {
80+
// Skip existing value
81+
return true;
82+
}
7983
Argument<Object> argument = property.getArgument();
8084
Object newValue = tenantResolver.resolveTenantIdentifier();
8185
if (!argument.isInstance(newValue)) {

0 commit comments

Comments
 (0)
Please sign in to comment.