Skip to content

Commit 6b262d9

Browse files
committed
[backend] feat(multitenancy): adding builtin connectors for new tenants (#3505)
1 parent f1d405f commit 6b262d9

5 files changed

Lines changed: 25 additions & 49 deletions

File tree

openaev-api/src/main/java/io/openaev/collectors/expectations_expiration_manager/ExpectationsExpirationManagerJob.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,6 @@ public ExpectationsExpirationManagerJob(
2626
this.collectorService = collectorService;
2727
this.config = config;
2828
this.fakeDetectorService = fakeDetectorService;
29-
try {
30-
collectorService.register(
31-
config.getId(),
32-
FAKE_DETECTOR_COLLECTOR_TYPE,
33-
FAKE_DETECTOR_COLLECTOR_NAME,
34-
false,
35-
0,
36-
null,
37-
getClass().getResourceAsStream("/img/icon-fake-detector.png"));
38-
} catch (Exception e) {
39-
log.error("Error creating expectations expiration manager ", e);
40-
}
4129
}
4230

4331
@Override

openaev-api/src/main/java/io/openaev/executors/ExecutorService.java

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import io.openaev.utils.mapper.CatalogConnectorMapper;
2121
import io.openaev.utils.mapper.ExecutorMapper;
2222
import jakarta.annotation.Resource;
23-
import jakarta.persistence.EntityManager;
2423
import jakarta.transaction.Transactional;
2524
import java.io.InputStream;
2625
import java.util.List;
@@ -41,8 +40,6 @@ public class ExecutorService extends AbstractConnectorService<Executor, Executor
4140

4241
private final ExecutorMapper executorMapper;
4342

44-
private final EntityManager entityManager;
45-
4643
@Autowired
4744
public ExecutorService(
4845
ExecutorRepository executorRepository,
@@ -52,8 +49,7 @@ public ExecutorService(
5249
CatalogConnectorService catalogConnectorService,
5350
ConnectorInstanceService connectorInstanceService,
5451
ExecutorMapper executorMapper,
55-
CatalogConnectorMapper catalogConnectorMapper,
56-
EntityManager entityManager) {
52+
CatalogConnectorMapper catalogConnectorMapper) {
5753
super(
5854
ConnectorType.EXECUTOR,
5955
connectorInstanceConfigurationRepository,
@@ -64,7 +60,6 @@ public ExecutorService(
6460
this.executorRepository = executorRepository;
6561
this.executionTraceRepository = executionTraceRepository;
6662
this.executorMapper = executorMapper;
67-
this.entityManager = entityManager;
6863
}
6964

7065
@Override
@@ -171,8 +166,7 @@ public Executor register(
171166

172167
Executor executor =
173168
executorRepository.findByIdAndTenantId(id, TenantContext.getCurrentTenant()).orElse(null);
174-
boolean isNew = (executor == null);
175-
if (isNew) {
169+
if (executor == null) {
176170
executor = new Executor();
177171
executor.setId(id);
178172
executor.setTenant(new Tenant(TenantContext.getCurrentTenant()));
@@ -184,12 +178,6 @@ public Executor register(
184178
executor.setBackgroundColor(backgroundColor);
185179
executor.setPlatforms(platforms);
186180

187-
// For new entities, use persist() to force INSERT — merge() would find the existing
188-
// Executor from another tenant (same static ID, JPA @Id is only executor_id).
189-
if (isNew) {
190-
entityManager.persist(executor);
191-
return executor;
192-
}
193181
return executorRepository.save(executor);
194182
}
195183

openaev-api/src/main/java/io/openaev/rest/collector/service/CollectorService.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import io.openaev.utils.mapper.CatalogConnectorMapper;
2323
import io.openaev.utils.mapper.CollectorMapper;
2424
import jakarta.annotation.Resource;
25-
import jakarta.persistence.EntityManager;
2625
import jakarta.transaction.Transactional;
2726
import jakarta.validation.constraints.NotNull;
2827
import java.io.InputStream;
@@ -48,8 +47,6 @@ public class CollectorService extends AbstractConnectorService<Collector, Collec
4847

4948
private final CollectorMapper collectorMapper;
5049

51-
private final EntityManager entityManager;
52-
5350
@Autowired
5451
public CollectorService(
5552
CollectorRepository collectorRepository,
@@ -60,8 +57,7 @@ public CollectorService(
6057
ConnectorInstanceService connectorInstanceService,
6158
CatalogConnectorService catalogConnectorService,
6259
CollectorMapper collectorMapper,
63-
CatalogConnectorMapper catalogConnectorMapper,
64-
EntityManager entityManager) {
60+
CatalogConnectorMapper catalogConnectorMapper) {
6561
super(
6662
ConnectorType.COLLECTOR,
6763
connectorInstanceConfigurationRepository,
@@ -73,7 +69,6 @@ public CollectorService(
7369
this.fileService = fileService;
7470
this.collectorMapper = collectorMapper;
7571
this.securityPlatformRepository = securityPlatformRepository;
76-
this.entityManager = entityManager;
7772
}
7873

7974
@Override
@@ -236,11 +231,9 @@ public Collector register(
236231
if (securityPlatform != null) {
237232
newCollector.setSecurityPlatform(securityPlatform);
238233
}
239-
// For new entities, use persist() to force INSERT — merge() would find the existing
240-
// Collector from another tenant (same static ID, JPA @Id is only collector_id).
234+
// For new entities, isNew()=true triggers persist() via Spring Data save().
241235
newCollector.setTenant(new Tenant(TenantContext.getCurrentTenant()));
242-
entityManager.persist(newCollector);
243-
return newCollector;
236+
return collectorRepository.save(newCollector);
244237
}
245238

246239
public List<Collector> collectorsForPayload(String payloadId) {

openaev-api/src/main/java/io/openaev/service/InjectorService.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -561,29 +561,25 @@ private Injector createNewBuiltinInjector(
561561
isPayloads,
562562
dependencies);
563563

564-
// Persistable.isNew()=false → Spring Data uses merge(), which would find the existing
565-
// Injector from another tenant (same static ID, JPA @Id is only injector_id).
566-
// We must use persist() to force an INSERT for the new tenant.
567564
newInjector.setTenant(new Tenant(TenantContext.getCurrentTenant()));
568-
// DB composite PK (id, tenant_id) allows the same static ID per tenant.
569-
entityManager.persist(newInjector);
565+
Injector savedInjector = injectorRepository.save(newInjector);
570566

571567
List<InjectorContract> injectorContracts =
572568
staticContracts.stream()
573569
.map(
574570
contract ->
575571
this.injectorContractService.createBuiltinInjectorContract(
576-
contract, newInjector, isPayloads))
572+
contract, savedInjector, isPayloads))
577573
.toList();
578574
injectorContracts = fromIterable(injectorContractRepository.saveAll(injectorContracts));
579575
// Link managed contracts on the owning side so Hibernate populates the join table.
580576
// We MUST use the instances returned by saveAll() — the originals are detached after merge().
581-
newInjector.getContracts().addAll(injectorContracts);
577+
savedInjector.getContracts().addAll(injectorContracts);
582578
// Flush now so that all inserts are visible before any subsequent query triggers auto-flush
583579
// (e.g. deleteDummyInjectorIfItExists), which would otherwise fail with
584580
// TransientObjectException.
585581
entityManager.flush();
586-
return newInjector;
582+
return savedInjector;
587583
}
588584

589585
private void applyBuiltinInjectorProperties(

openaev-model/src/main/java/io/openaev/database/model/BaseConnectorEntity.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.openaev.database.model;
22

33
import com.fasterxml.jackson.annotation.JsonIgnore;
4+
import jakarta.persistence.PostLoad;
5+
import jakarta.persistence.PostPersist;
46
import jakarta.persistence.Transient;
57
import lombok.Data;
68
import org.springframework.data.domain.Persistable;
@@ -13,8 +15,8 @@
1315
* maps only {@code id} as {@code @Id}. The Hibernate tenant filter scopes all queries to the
1416
* current tenant, and services use {@code findByIdAndTenantId()} for explicit lookups.
1517
*
16-
* <p>Implements {@link Persistable} so that Spring Data JPA knows these entities always have
17-
* assigned IDs and should use {@code merge()} instead of {@code persist()}.
18+
* <p>Implements {@link Persistable} with a transient {@code newEntity} flag so that Spring Data JPA
19+
* correctly uses {@code persist()} for new entities and {@code merge()} for existing ones.
1820
*/
1921
@Data
2022
public abstract class BaseConnectorEntity implements Base, Persistable<String> {
@@ -23,14 +25,23 @@ public abstract class BaseConnectorEntity implements Base, Persistable<String> {
2325
private String type;
2426
private boolean external;
2527

28+
@Transient @JsonIgnore private boolean newEntity = true;
29+
2630
/**
27-
* Connector entities always have an assigned (static) ID, so they are never "new" from JPA's
28-
* perspective. This ensures Spring Data uses {@code merge()} rather than {@code persist()}.
31+
* Returns {@code true} if this entity has not yet been persisted. Spring Data uses this to decide
32+
* between {@code persist()} (INSERT) and {@code merge()} (SELECT + UPDATE).
2933
*/
3034
@Override
3135
@Transient
3236
@JsonIgnore
3337
public boolean isNew() {
34-
return false;
38+
return newEntity;
39+
}
40+
41+
/** Mark as persisted after being loaded from the database. */
42+
@PostLoad
43+
@PostPersist
44+
void markNotNew() {
45+
this.newEntity = false;
3546
}
3647
}

0 commit comments

Comments
 (0)