Skip to content

Commit

Permalink
Avoid setting the tenant id if one is preset
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov committed May 17, 2024
1 parent b21030f commit d7423c6
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.micronaut.data.jdbc.h2.multitenancy;

import io.micronaut.core.annotation.Nullable;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;
import io.micronaut.data.annotation.TenantId;
import io.micronaut.serde.annotation.Serdeable;

@Serdeable // <1>
@MappedEntity // <2>
public record TenancyBook(@Nullable
@Id // <3>
@GeneratedValue // <4>
Long id,
String title,
@TenantId // <5>
String framework) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.micronaut.data.jdbc.h2.multitenancy;

import io.micronaut.context.annotation.Requires;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;

import java.util.List;

@Requires(property = "spec.name", value = "TenancyBookControllerSpec")
@Controller("/books") // <1>
class TenancyBookController {
private final TenancyBookRepository bookRepository;

TenancyBookController(TenancyBookRepository bookRepository) { // <2>
this.bookRepository = bookRepository;
}

@ExecuteOn(TaskExecutors.BLOCKING) // <3>
@Get
// <4>
List<TenancyBook> index() {
return bookRepository.findAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package io.micronaut.data.jdbc.h2.multitenancy

import io.micronaut.context.annotation.Property
import io.micronaut.core.type.Argument
import io.micronaut.core.util.StringUtils
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import io.micronaut.http.client.BlockingHttpClient
import io.micronaut.http.client.HttpClient
import io.micronaut.http.client.annotation.Client
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import spock.lang.Specification

import static org.junit.Assert.assertEquals

@Property(name = "datasources.default.schema-generate", value = "CREATE_DROP")
// <1>
@Property(name = "datasources.default.url", value = "jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
@Property(name = "datasources.default.username", value = "sa")
@Property(name = "datasources.default.password", value = "")
@Property(name = "datasources.default.dialect", value = "H2")
@Property(name = "datasources.default.driver-class-name", value = "org.h2.Driver")
@Property(name = "micronaut.multitenancy.tenantresolver.httpheader.enabled", value = StringUtils.TRUE)
@Property(name = "spec.name", value = "TenancyBookControllerSpec")
@MicronautTest(transactional = false)
// <2>
class TenancyBookControllerSpec extends Specification {

@Inject
@Client("/")
HttpClient httpClient

@Inject
TenancyBookRepository bookRepository

def multitenancyRequest() {

given:
BlockingHttpClient client = httpClient.toBlocking()
save(bookRepository, client, "Building Microservices with Micronaut", "micronaut")
save(bookRepository, client, "Introducing Micronaut", "micronaut")
save(bookRepository, client, "Grails 3 - Step by Step", "grails")
save(bookRepository, client, "Falando de Grail", "grails")
save(bookRepository, client, "Grails Goodness Notebook", "grails")

when:
List<TenancyBook> books = fetchBooks(client, "micronaut")
then:
books
books.size() == 2

when:
books = fetchBooks(client, "grails")
then:
books
books.size() == 3

cleanup:
bookRepository.deleteAll()
}

List<TenancyBook> fetchBooks(BlockingHttpClient client, String framework) {
HttpRequest<?> request = HttpRequest.GET("/books").header("tenantId", framework)
Argument<List<TenancyBook>> responseArgument = Argument.listOf(TenancyBook.class)
HttpResponse<List<TenancyBook>> response = client.exchange(request, responseArgument)
assertEquals(HttpStatus.OK, response.getStatus())
return response.body()
}

void save(TenancyBookRepository bookRepository, BlockingHttpClient client, String title, String framework) {
bookRepository.save(new TenancyBook(null, title, framework))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.micronaut.data.jdbc.h2.multitenancy;

import io.micronaut.context.annotation.Requires;
import io.micronaut.data.annotation.WithoutTenantId;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;

@Requires(property = "spec.name", value = "TenancyBookControllerSpec")
@JdbcRepository(dialect = Dialect.H2) // <1>
public interface TenancyBookRepository extends CrudRepository<TenancyBook, Long> { // <2>
Long save(String title);

@WithoutTenantId
@Override
void deleteAll();
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ protected Predicate<RuntimePersistentProperty<Object>> getPropertyPredicate() {
public boolean prePersist(@NonNull EntityEventContext<Object> context) {
for (RuntimePersistentProperty<Object> property : getApplicableProperties(context.getPersistentEntity())) {
if (property.getAnnotationMetadata().hasStereotype(TenantId.class)) {
if (property.getProperty().get(context.getEntity()) != null) {
// Skip existing value
return true;
}
Argument<Object> argument = property.getArgument();
Object newValue = tenantResolver.resolveTenantIdentifier();
if (!argument.isInstance(newValue)) {
Expand Down

0 comments on commit d7423c6

Please sign in to comment.