-
Notifications
You must be signed in to change notification settings - Fork 198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support discriminator multitenancy #2876
Conversation
*/ | ||
@Deprecated(since = "4.8", forRemoval = true) | ||
boolean updateable() default true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe use @AliasFor
so if someone sets this it sets the new one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then, we would need to check for both values to have it backward compatible. I would just use the old value and switch to the new one in M5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are telling people to use the new method updatable
in the javadoc. Both methods should work undateable
and updatable`.
would just use the old value and switch to the new one in M5
If only the deprecated one updateable
is going to work, then we should not add updatable
until the next major version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would just use the old value
- I mean we alias the new one to the old one for now
@Documented | ||
@Experimental | ||
@AutoPopulated(updatable = false) | ||
public @interface TenantId { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should the target support METHOD and PARAMETER to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would need to add more tests to support update(..., @TenantId String some)
data-model/src/main/java/io/micronaut/data/intercept/annotation/DataMethodQueryParameter.java
Outdated
Show resolved
Hide resolved
@@ -72,7 +72,7 @@ public class MethodMatchContext extends MatchContext { | |||
@NonNull Map<ClassElement, FindInterceptorDef> findInterceptors) { | |||
super(queryBuilder, repositoryClass, visitorContext, methodElement, typeRoles, returnType, parameters, findInterceptors); | |||
this.entity = entity; | |||
this.parametersInRole = Collections.unmodifiableMap(parametersInRole); | |||
this.parametersInRole = new HashMap<>(parametersInRole); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why? Are we now mutating this somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
Object value = p.getValue(); | ||
if (value != null) { | ||
if (value instanceof String expression) { | ||
// TODO: Support adding an expression annotation value in Core |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you create an issue for this in core and link here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember to add docs to this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we add the annotations @TenantId
, @WithTenantId
, @WithoutTenantId
to the Micronaut Multitenancy module instead of adding them in Micronaut Data.
Not sure they will be helpful to outside of Micronaut Data, |
I'm thinking that |
...ime/src/main/java/io/micronaut/data/runtime/event/listeners/TenantIdEntityEventListener.java
Outdated
Show resolved
Hide resolved
...ime/src/main/java/io/micronaut/data/runtime/event/listeners/TenantIdEntityEventListener.java
Outdated
Show resolved
Hide resolved
...ime/src/main/java/io/micronaut/data/runtime/event/listeners/TenantIdEntityEventListener.java
Outdated
Show resolved
Hide resolved
@WithTenantId("foo") | ||
List<AccountRecord> findAll$withTenantFoo(); | ||
|
||
@WithTenantId("#{this.barTenant()}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can another method parameter be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this moment it will fail trying to resolve parameter as an entity property filter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no documentation about @WithoutTenantId
or @WithTenant
*/ | ||
@Deprecated(since = "4.8", forRemoval = true) | ||
boolean updateable() default true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are telling people to use the new method updatable
in the javadoc. Both methods should work undateable
and updatable`.
would just use the old value and switch to the new one in M5
If only the deprecated one updateable
is going to work, then we should not add updatable
until the next major version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given a domain such as:
package example.micronaut;
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 Book(@Nullable
@Id // <3>
@GeneratedValue // <4>
Long id,
String title,
@TenantId // <5>
String framework) {
}
and a controller such as:
package example.micronaut;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.*;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;
import java.util.List;
@Controller("/books") // <1>
class BookController {
private final BookRepository bookRepository;
BookController(BookRepository bookRepository) { // <2>
this.bookRepository = bookRepository;
}
@ExecuteOn(TaskExecutors.BLOCKING) // <3>
@Get // <4>
List<Book> index() {
return bookRepository.findAll();
}
}
and a repository such as:
package example.micronaut;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;
@JdbcRepository(dialect = Dialect.H2) // <1>
public interface BookRepository extends CrudRepository<Book, Long> { // <2>
Long save(String title);
}
I expected this test to pass:
package example.micronaut;
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.junit5.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@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)
@MicronautTest(transactional = false) // <2>
class BookControllerTest {
@Test
void multitenancyRequest(@Client("/") HttpClient httpClient, // <3>
BookRepository bookRepository) {
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");
List<Book> books = fetchBooks(client, "micronaut");
assertNotNull(books);
assertEquals(2, books.size());
books = fetchBooks(client, "grails");
assertNotNull(books);
assertEquals(3, books.size());
bookRepository.deleteAll();
}
List<Book> fetchBooks(BlockingHttpClient client, String framework) {
HttpRequest<?> request = HttpRequest.GET("/books").header("tenantId", framework);
Argument<List<Book>> responseArgument = Argument.listOf(Book.class);
HttpResponse<List<Book>> response = assertDoesNotThrow(() -> client.exchange(request, responseArgument));
assertEquals(HttpStatus.OK, response.getStatus());
return response.body();
}
void save(BookRepository bookRepository, BlockingHttpClient client, String title, String framework) {
bookRepository.save(new Book(null, title, framework));
}
}
However, the usage of repository methods such as deleteAll()
or save with an entity containing a populated property with @TenantId does not work.
@sdelamo Both |
There is in |
@sdelamo Added your test and skipped filling the tenant id if one is preset |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should remove DISCRIMINATOR mode as it currently does not do anything: #2916
@dstepanov I created a PR to show an example in the docs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is a bug #2918 or I am missing something obvious in my test.
Quality Gate failedFailed conditions See analysis details on SonarCloud Catch issues before they fail your Quality Gate with our IDE extension SonarLint |
No description provided.