Skip to content

Commit

Permalink
Merge pull request #214 from NIAEFEUP/base-seeders
Browse files Browse the repository at this point in the history
Base seeders
  • Loading branch information
rubuy-74 authored Jan 26, 2025
2 parents f5dd084 + 3719b5e commit 8889f3a
Show file tree
Hide file tree
Showing 18 changed files with 849 additions and 0 deletions.
7 changes: 7 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
implementation("ch.qos.logback:logback-core:1.4.8")
implementation("org.slf4j:slf4j-api:2.0.7")
implementation("com.cloudinary:cloudinary:1.0.14")
implementation("net.datafaker:datafaker:2.2.2")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
implementation("org.springframework.boot:spring-boot-starter-validation:3.1.1")
developmentOnly("org.springframework.boot:spring-boot-devtools")
Expand Down Expand Up @@ -134,6 +135,12 @@ tasks.register<Copy>("generateDocs") {
into(File("docs"))
}

tasks.bootRun {
if (project.hasProperty("seed")) {
systemProperties(mapOf("seed" to "true"))
}
}

tasks.register("fixExamples") {
dependsOn(tasks.named("openapi3"))
doLast {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package pt.up.fe.ni.website.backend.hooks

import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.stereotype.Component
import pt.up.fe.ni.website.backend.config.Logging
import pt.up.fe.ni.website.backend.model.seeders.ApplicationSeeder

@Component
class ApplicationStartupHook(
val applicationSeeder: ApplicationSeeder
) : ApplicationRunner, Logging {

@Value("\${app.debug}")
val debug: Boolean = true // false

var seed: Boolean = true // false

fun checkSeedArgument() {
try {
val seedProperty = System.getProperty("seed")
if (seedProperty == "true") {
seed = true
}
} catch (_: NullPointerException) { } catch (_: IllegalArgumentException) {}
}

override fun run(args: ApplicationArguments?) {
logger.info("Running Startup hook...")
// checkSeedArgument()
if (debug && seed) {
logger.info("Running application seeder...")
applicationSeeder.seedDatabase()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package pt.up.fe.ni.website.backend.model.seeders

import net.datafaker.Faker
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.repository.CrudRepository

@Suppress("SpringJavaInjectionPointsAutowiringInspection")
abstract class AbstractSeeder<T, U, P> where T : CrudRepository<U, P> {

protected val faker = Faker()

@Autowired
protected lateinit var repository: T

abstract fun createObjects()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package pt.up.fe.ni.website.backend.model.seeders

import jakarta.persistence.EntityManager
import jakarta.transaction.Transactional
import java.util.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Component
import pt.up.fe.ni.website.backend.config.Logging
import pt.up.fe.ni.website.backend.model.Account
import pt.up.fe.ni.website.backend.repository.AccountRepository
import pt.up.fe.ni.website.backend.repository.CustomWebsiteRepository
import pt.up.fe.ni.website.backend.repository.RoleRepository

@Component @Transactional
class AccountSeeder(
private val encoder: PasswordEncoder,
@Autowired val roleRepository: RoleRepository,
@Autowired val customWebsiteRepository: CustomWebsiteRepository,
@Autowired val entityManager: EntityManager // Inject EntityManager
) : AbstractSeeder<AccountRepository, Account, Long>(), Logging {

override fun createObjects() {
logger.info("Running account seeder...")

// Fetch all roles from the database
val roles = roleRepository.findAll().toList()
if (roles.isEmpty()) {
logger.warn("No roles found in the database. Accounts will be created without roles.")
}

val websites = customWebsiteRepository.findAll().toList()
.map { entityManager.merge(it) }.toMutableList()
if (websites.isEmpty()) {
logger.warn("No Custom websites in database. Accounts will be created without websites")
}

val accounts = listOf(
Account(
name = faker.name().fullName(),
email = faker.internet().emailAddress(),
password = encoder.encode("password123"),
bio = faker.lorem().paragraph(1),
birthDate = Date.from(faker.date().birthday(25, 35).toInstant()),
photo = faker.internet().image(),
github = faker.internet().url(),
websites = websites.shuffled().take(1).toMutableList(),
linkedin = faker.internet().url(),
roles = roles.shuffled().take(2).toMutableList()
),
// Account Without Photo
Account(
name = faker.name().fullName(),
email = faker.internet().emailAddress(),
password = encoder.encode("password123"),
bio = faker.lorem().sentence(),
birthDate = Date.from(faker.date().birthday(30, 40).toInstant()),
photo = null,
github = faker.internet().url(),
linkedin = faker.internet().url(),
roles = roles.shuffled().take(1).toMutableList()
),
// Account Without Bio
Account(
name = faker.name().fullName(),
email = faker.internet().emailAddress(),
password = encoder.encode("password123"),
bio = null,
birthDate = Date.from(faker.date().birthday(18, 28).toInstant()),
photo = faker.internet().image(),
github = faker.internet().url(),
linkedin = faker.internet().url(),
websites = websites.shuffled().take(3).toMutableList(),
roles = roles.shuffled().take(1).toMutableList()
),
// Account Without Bio or Photo
Account(
name = faker.name().fullName(),
email = faker.internet().emailAddress(),
password = encoder.encode("password123"),
bio = null,
birthDate = Date.from(faker.date().birthday(20, 30).toInstant()),
photo = null,
github = null,
linkedin = faker.internet().url(),
websites = websites.shuffled().take(5).toMutableList(),
roles = roles.shuffled().take(1).toMutableList()
),
// Account with Long Bio
Account(
name = faker.name().fullName(),
email = faker.internet().emailAddress(),
password = encoder.encode("password123"),
bio = faker.lorem().paragraph(5),
birthDate = Date.from(faker.date().birthday(35, 45).toInstant()),
photo = faker.internet().image(),
github = faker.internet().url(),
linkedin = faker.internet().url(),
roles = roles.shuffled().take(3).toMutableList()
),
// Account with Single Role
Account(
name = faker.name().fullName(),
email = faker.internet().emailAddress(),
password = encoder.encode("password123"),
bio = "User with only one role.",
birthDate = Date.from(faker.date().birthday(22, 32).toInstant()),
photo = faker.internet().image(),
github = faker.internet().url(),
linkedin = faker.internet().url(),
websites = websites.shuffled().take(2).toMutableList(),
roles = roles.shuffled().take(1).toMutableList()
),
// Account with All Optional Fields Null
Account(
name = faker.name().fullName(),
email = faker.internet().emailAddress(),
password = encoder.encode("password123"),
bio = null,
birthDate = null,
photo = null,
github = null,
linkedin = null,
roles = mutableListOf()
)
)

val repeatedAccounts = mutableListOf<Account>()
for (i in 1..5) {
repeatedAccounts.addAll(accounts)
}

val staticAccounts = listOf( // edge cases
Account(
name = "Zoë Löwe Dinis Canas", // Special characters in name
email = faker.internet().emailAddress(),
password = encoder.encode("specialpass"),
bio = faker.lorem().sentence(),
birthDate = Date.from(faker.date().birthday(20, 40).toInstant()),
photo = faker.internet().image(),
github = faker.internet().url(),
linkedin = faker.internet().url(),
roles = roles.shuffled().take(2).toMutableList()
)
)

val allAccounts = repeatedAccounts + staticAccounts

repository.saveAll(allAccounts)

logger.info("Account data seeded successfully.")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package pt.up.fe.ni.website.backend.model.seeders

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import pt.up.fe.ni.website.backend.config.Logging
import pt.up.fe.ni.website.backend.model.Event
import pt.up.fe.ni.website.backend.repository.ActivityRepository

@Component
class ActivitySeeder(
@Autowired val eventSeeder: EventSeeder,
@Autowired val projectSeeder: ProjectSeeder,
@Autowired val perActivityRoleSeeder: PerActivityRoleSeeder
) : AbstractSeeder<ActivityRepository<Event>, Event, Long>(), Logging {

override fun createObjects() {
// Delegate seeding to the EventSeeder and ProjectSeeder
logger.info("Running Activity Seeder")

// Seed Roles per Activity
perActivityRoleSeeder.createObjects()
// Seed Projects
projectSeeder.createObjects()
// Seed Events
eventSeeder.createObjects()

logger.info("Finished seeding activities (events and projects)")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package pt.up.fe.ni.website.backend.model.seeders

import jakarta.persistence.EntityManager
import javax.sql.DataSource
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
import pt.up.fe.ni.website.backend.service.database.DatabaseService

@Component
class ApplicationSeeder(
private val accountSeeder: AccountSeeder,
private val activitySeeder: ActivitySeeder,
private val postSeeder: PostSeeder,
private val generationSeeder: GenerationSeeder,
private val roleSeeder: RoleSeeder,
private val customWebsiteSeeder: CustomWebsiteSeeder,
@Autowired
private val appContext: ApplicationContext,
private val databaseService: DatabaseService
) {
fun seedDatabase() {
val dataSource = appContext.getBean(DataSource::class.java)
databaseService.cleanDataSource(dataSource)

val entityManager = appContext.getBean(EntityManager::class.java)
databaseService.cleanEntityManager(entityManager)

roleSeeder.createObjects()
customWebsiteSeeder.createObjects()
accountSeeder.createObjects()
activitySeeder.createObjects()
postSeeder.createObjects()
generationSeeder.createObjects()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package pt.up.fe.ni.website.backend.model.seeders

import org.springframework.stereotype.Component
import pt.up.fe.ni.website.backend.config.Logging
import pt.up.fe.ni.website.backend.model.CustomWebsite
import pt.up.fe.ni.website.backend.repository.CustomWebsiteRepository

@Component
class CustomWebsiteSeeder() : AbstractSeeder<CustomWebsiteRepository, CustomWebsite, Long>(), Logging {

override fun createObjects() {
if (repository.count() == 0L) {
logger.info("Running CustomWebsite seeder...")

val customWebsites = (1..15).map {
CustomWebsite(
url = faker.internet().url(),
iconPath = faker.internet().image(),
label = faker.lorem().sentence(3, 5)
)
}

repository.saveAll(customWebsites)
logger.info("CustomWebsite data seeded successfully.")
} else {
logger.info("CustomWebsite data already exists, skipping seeding.")
}
}
}
Loading

0 comments on commit 8889f3a

Please sign in to comment.