Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fowoco.server.auth.application.port.UserAccountRepository;
import com.fowoco.server.company.application.port.CompanyRepository;
import com.fowoco.server.company.application.port.CompanySettingsProvisioner;
import com.fowoco.server.common.security.TenantTransactionExecutor;
import java.time.Clock;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -22,15 +23,17 @@ DemoAuthSeedRunner demoAuthSeedRunner(
CompanySettingsProvisioner companySettingsProvisioner,
UserAccountRepository userAccountRepository,
PasswordEncoder passwordEncoder,
Clock clock
Clock clock,
TenantTransactionExecutor tenantTransactionExecutor
) {
return new DemoAuthSeedRunner(
properties,
companyRepository,
companySettingsProvisioner,
userAccountRepository,
passwordEncoder,
clock
clock,
tenantTransactionExecutor
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fowoco.server.company.application.port.CompanyRepository;
import com.fowoco.server.company.application.port.CompanySettingsProvisioner;
import com.fowoco.server.company.domain.Company;
import com.fowoco.server.common.security.TenantTransactionExecutor;
import java.time.Clock;
import java.time.Instant;
import java.util.List;
Expand All @@ -18,7 +19,6 @@
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;

@Order(0)
class DemoAuthSeedRunner implements ApplicationRunner {
Expand Down Expand Up @@ -58,14 +58,16 @@ class DemoAuthSeedRunner implements ApplicationRunner {
private final UserAccountRepository userAccountRepository;
private final PasswordEncoder passwordEncoder;
private final Clock clock;
private final TenantTransactionExecutor tenantTransactionExecutor;

DemoAuthSeedRunner(
DemoAuthSeedProperties properties,
CompanyRepository companyRepository,
CompanySettingsProvisioner companySettingsProvisioner,
UserAccountRepository userAccountRepository,
PasswordEncoder passwordEncoder,
Clock clock
Clock clock,
TenantTransactionExecutor tenantTransactionExecutor
) {
this.properties = Objects.requireNonNull(properties, "properties must not be null");
this.companyRepository = Objects.requireNonNull(
Expand All @@ -82,24 +84,30 @@ class DemoAuthSeedRunner implements ApplicationRunner {
);
this.passwordEncoder = Objects.requireNonNull(passwordEncoder, "passwordEncoder must not be null");
this.clock = Objects.requireNonNull(clock, "clock must not be null");
this.tenantTransactionExecutor = Objects.requireNonNull(
tenantTransactionExecutor,
"tenantTransactionExecutor must not be null"
);
}

@Override
@Transactional
public void run(ApplicationArguments arguments) {
validateConfiguration();
Instant now = clock.instant();
ensureCompany(properties.companyId(), properties.companyName(), now);
ensureCompany(properties.testCompanyId(), properties.testCompanyName(), now);

seedUser(properties.companyId(), new DemoUser(
properties.adminUserId(),
properties.adminDisplayName(),
properties.adminEmail(),
UserRole.ADMIN
), now);
DEMO_USERS.forEach(user -> seedUser(properties.companyId(), user, now));
TEST_USERS.forEach(user -> seedUser(properties.testCompanyId(), user, now));
tenantTransactionExecutor.execute(properties.companyId(), () -> {
ensureCompany(properties.companyId(), properties.companyName(), now);
seedUser(properties.companyId(), new DemoUser(
properties.adminUserId(),
properties.adminDisplayName(),
properties.adminEmail(),
UserRole.ADMIN
), now);
DEMO_USERS.forEach(user -> seedUser(properties.companyId(), user, now));
});
tenantTransactionExecutor.execute(properties.testCompanyId(), () -> {
ensureCompany(properties.testCompanyId(), properties.testCompanyName(), now);
TEST_USERS.forEach(user -> seedUser(properties.testCompanyId(), user, now));
});
LOGGER.info(
"demo_auth_seed ready company_count={} user_count={}",
2,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.fowoco.server.common.security;

import java.util.Objects;
import java.util.UUID;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;

/**
* Executes work in an independent transaction bound to one trusted tenant.
*/
@Component
public final class TenantTransactionExecutor {

private final TransactionTemplate transactionTemplate;
private final TenantDatabaseContext tenantDatabaseContext;

public TenantTransactionExecutor(
PlatformTransactionManager transactionManager,
TenantDatabaseContext tenantDatabaseContext
) {
this.transactionTemplate = new TransactionTemplate(Objects.requireNonNull(
transactionManager,
"transactionManager must not be null"
));
this.transactionTemplate.setPropagationBehavior(
TransactionDefinition.PROPAGATION_REQUIRES_NEW
);
this.tenantDatabaseContext = Objects.requireNonNull(
tenantDatabaseContext,
"tenantDatabaseContext must not be null"
);
}

public void execute(UUID companyId, Runnable callback) {
Objects.requireNonNull(companyId, "companyId must not be null");
Objects.requireNonNull(callback, "callback must not be null");
transactionTemplate.executeWithoutResult(status -> {
tenantDatabaseContext.setCompanyIdForCurrentTransaction(companyId);
callback.run();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fowoco.server.approval.application.port.ExternalSubmissionRepository;
import com.fowoco.server.approval.application.SafeJsonService;
import com.fowoco.server.auth.infrastructure.seed.DemoAuthSeedProperties;
import com.fowoco.server.common.security.TenantTransactionExecutor;
import com.fowoco.server.document.application.port.DocumentRequestDraftRepository;
import com.fowoco.server.file.application.port.FileStorage;
import com.fowoco.server.file.application.port.StoredFileRepository;
Expand Down Expand Up @@ -48,6 +49,7 @@ DemoOperationalSeedRunner demoOperationalSeedRunner(
JdbcTemplate jdbcTemplate,
ObjectMapper objectMapper,
Clock clock,
TenantTransactionExecutor tenantTransactionExecutor,
@Value("${app.file-storage.local-path}") String localFileStoragePath
) {
if (!(fileStorage instanceof LocalFileStorage)) {
Expand Down Expand Up @@ -121,7 +123,8 @@ DemoOperationalSeedRunner demoOperationalSeedRunner(
evidenceSeeder,
requestDraftSeeder,
auditSeeder,
verifier
verifier,
tenantTransactionExecutor
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.fowoco.server.demo.infrastructure.seed;

import com.fowoco.server.auth.infrastructure.seed.DemoAuthSeedProperties;
import com.fowoco.server.common.security.TenantTransactionExecutor;
import com.fowoco.server.demo.infrastructure.seed.DemoOperationalSeedCatalog.AuditSeed;
import com.fowoco.server.demo.infrastructure.seed.DemoOperationalSeedCatalog.ApprovalSeed;
import com.fowoco.server.demo.infrastructure.seed.DemoOperationalSeedCatalog.ChecklistSeed;
Expand All @@ -21,7 +22,6 @@
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.transaction.annotation.Transactional;

@Order(2)
class DemoOperationalSeedRunner implements ApplicationRunner {
Expand All @@ -44,6 +44,7 @@ class DemoOperationalSeedRunner implements ApplicationRunner {
private final DemoDocumentRequestDraftSeeder requestDraftSeeder;
private final DemoAuditEventSeeder auditSeeder;
private final DemoOperationalSeedVerifier verifier;
private final TenantTransactionExecutor tenantTransactionExecutor;

DemoOperationalSeedRunner(
DemoAuthSeedProperties properties,
Expand All @@ -61,7 +62,8 @@ class DemoOperationalSeedRunner implements ApplicationRunner {
DemoEvidenceSeeder evidenceSeeder,
DemoDocumentRequestDraftSeeder requestDraftSeeder,
DemoAuditEventSeeder auditSeeder,
DemoOperationalSeedVerifier verifier
DemoOperationalSeedVerifier verifier,
TenantTransactionExecutor tenantTransactionExecutor
) {
this.properties = Objects.requireNonNull(properties, "properties must not be null");
this.clock = Objects.requireNonNull(clock, "clock must not be null");
Expand Down Expand Up @@ -94,30 +96,35 @@ class DemoOperationalSeedRunner implements ApplicationRunner {
);
this.auditSeeder = Objects.requireNonNull(auditSeeder, "auditSeeder must not be null");
this.verifier = Objects.requireNonNull(verifier, "verifier must not be null");
this.tenantTransactionExecutor = Objects.requireNonNull(
tenantTransactionExecutor,
"tenantTransactionExecutor must not be null"
);
}

@Override
@Transactional
public void run(ApplicationArguments arguments) {
Instant now = clock.instant();
LocalDate today = LocalDate.now(clock);
DemoOperationalSeedContext demoContext = DemoOperationalSeedContext.demo(properties, today, now);
DemoOperationalSeedContext testContext = DemoOperationalSeedContext.test(properties, today, now);
goldenFlowStateGuard.verifyNoLegacyRows(demoContext);
seedDataset(
catalog.demoTasks(),
catalog.demoStoredFiles(),
catalog.demoDocuments(),
catalog.demoChecklists(),
catalog.demoApprovals(),
catalog.demoTransitions(),
catalog.demoExternalSubmissions(),
catalog.demoEvidence(),
catalog.demoDocumentRequestDrafts(),
catalog.demoAudits(),
demoContext
);
seedDataset(
tenantTransactionExecutor.execute(demoContext.companyId(), () -> {
goldenFlowStateGuard.verifyNoLegacyRows(demoContext);
seedDataset(
catalog.demoTasks(),
catalog.demoStoredFiles(),
catalog.demoDocuments(),
catalog.demoChecklists(),
catalog.demoApprovals(),
catalog.demoTransitions(),
catalog.demoExternalSubmissions(),
catalog.demoEvidence(),
catalog.demoDocumentRequestDrafts(),
catalog.demoAudits(),
demoContext
);
});
tenantTransactionExecutor.execute(testContext.companyId(), () -> seedDataset(
catalog.testTasks(),
List.of(),
catalog.testDocuments(),
Expand All @@ -129,7 +136,7 @@ public void run(ApplicationArguments arguments) {
List.of(),
catalog.testAudits(),
testContext
);
));
LOGGER.info(
"demo_operational_seed ready demo_task_count={} demo_stored_file_count={} "
+ "demo_document_count={} "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fowoco.server.auth.infrastructure.seed.DemoAuthSeedProperties;
import com.fowoco.server.company.application.port.CompanyRepository;
import com.fowoco.server.common.security.TenantTransactionExecutor;
import com.fowoco.server.worker.application.port.WorkerRepository;
import java.time.Clock;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
Expand All @@ -17,13 +18,15 @@ DemoWorkerSeedRunner demoWorkerSeedRunner(
DemoAuthSeedProperties properties,
CompanyRepository companyRepository,
WorkerRepository workerRepository,
Clock clock
Clock clock,
TenantTransactionExecutor tenantTransactionExecutor
) {
return new DemoWorkerSeedRunner(
properties,
companyRepository,
workerRepository,
clock
clock,
tenantTransactionExecutor
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fowoco.server.auth.infrastructure.seed.DemoAuthSeedProperties;
import com.fowoco.server.company.application.port.CompanyRepository;
import com.fowoco.server.company.domain.Company;
import com.fowoco.server.common.security.TenantTransactionExecutor;
import com.fowoco.server.worker.application.port.WorkerRepository;
import com.fowoco.server.worker.domain.Worker;
import com.fowoco.server.worker.infrastructure.seed.DemoWorkerSeedCatalog.WorkerSeed;
Expand All @@ -18,7 +19,6 @@
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.transaction.annotation.Transactional;

@Order(1)
class DemoWorkerSeedRunner implements ApplicationRunner {
Expand All @@ -29,12 +29,14 @@ class DemoWorkerSeedRunner implements ApplicationRunner {
private final WorkerRepository workerRepository;
private final Clock clock;
private final DemoWorkerSeedCatalog catalog;
private final TenantTransactionExecutor tenantTransactionExecutor;

DemoWorkerSeedRunner(
DemoAuthSeedProperties properties,
CompanyRepository companyRepository,
WorkerRepository workerRepository,
Clock clock
Clock clock,
TenantTransactionExecutor tenantTransactionExecutor
) {
this.properties = Objects.requireNonNull(properties, "properties must not be null");
this.companyRepository = Objects.requireNonNull(
Expand All @@ -44,15 +46,24 @@ class DemoWorkerSeedRunner implements ApplicationRunner {
this.workerRepository = Objects.requireNonNull(workerRepository, "workerRepository must not be null");
this.clock = Objects.requireNonNull(clock, "clock must not be null");
this.catalog = new DemoWorkerSeedCatalog();
this.tenantTransactionExecutor = Objects.requireNonNull(
tenantTransactionExecutor,
"tenantTransactionExecutor must not be null"
);
}

@Override
@Transactional
public void run(ApplicationArguments arguments) {
Instant now = clock.instant();
LocalDate today = LocalDate.now(clock);
seedCompanyWorkers(properties.companyId(), catalog.demoWorkers(), today, now);
seedCompanyWorkers(properties.testCompanyId(), catalog.testWorkers(), today, now);
tenantTransactionExecutor.execute(
properties.companyId(),
() -> seedCompanyWorkers(properties.companyId(), catalog.demoWorkers(), today, now)
);
tenantTransactionExecutor.execute(
properties.testCompanyId(),
() -> seedCompanyWorkers(properties.testCompanyId(), catalog.testWorkers(), today, now)
);
LOGGER.info(
"demo_worker_seed ready company_count={} worker_count={}",
2,
Expand Down
Loading
Loading