diff --git a/backend/app/build.gradle.kts b/backend/app/build.gradle.kts index ecd86cf..b4e7370 100644 --- a/backend/app/build.gradle.kts +++ b/backend/app/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { val flyway_version = "10.6.0" val hikari_version = "5.1.0" val postgres_version = "42.7.1" - val exposed_version = "0.41.1" + val exposed_version = "0.49.0" val h2_version = "2.1.214" val kotlinx_datetime_version = "0.2.1" diff --git a/backend/app/src/main/kotlin/backend/App.kt b/backend/app/src/main/kotlin/backend/App.kt index 38407ed..e7920b8 100644 --- a/backend/app/src/main/kotlin/backend/App.kt +++ b/backend/app/src/main/kotlin/backend/App.kt @@ -1,11 +1,7 @@ package backend -import backend.config.configureAuth -import backend.config.configureRouting -import backend.config.configureSerialization -import backend.config.defaultClient +import backend.config.* import com.inventy.plugins.DatabaseFactory -import io.ktor.client.* import io.ktor.server.application.* @@ -25,6 +21,3 @@ fun Application.module() { configureAuth() configureRouting() } - - - diff --git a/backend/app/src/main/kotlin/backend/config/DatabaseFactory.kt b/backend/app/src/main/kotlin/backend/config/DatabaseFactory.kt index 2859980..adbf2dd 100644 --- a/backend/app/src/main/kotlin/backend/config/DatabaseFactory.kt +++ b/backend/app/src/main/kotlin/backend/config/DatabaseFactory.kt @@ -1,13 +1,16 @@ package com.inventy.plugins import com.zaxxer.hikari.HikariDataSource -import java.util.concurrent.TimeUnit.MINUTES -import org.jetbrains.exposed.sql.* import kotlinx.coroutines.* import org.flywaydb.core.Flyway -import org.postgresql.ds.PGSimpleDataSource import org.h2.jdbcx.JdbcDataSource +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction +import org.jetbrains.exposed.sql.transactions.experimental.withSuspendTransaction +import org.postgresql.ds.PGSimpleDataSource +import java.util.concurrent.TimeUnit.MINUTES + class DatabaseFactory( private val dbHost: String, @@ -16,11 +19,16 @@ class DatabaseFactory( private val dbPassword: String, private val databaseName: String, private val embedded: Boolean = false - ) { +) { companion object { suspend fun dbQuery(block: suspend () -> T): T = - newSuspendedTransaction(Dispatchers.IO) { block() } + withContext(Dispatchers.IO) { + TransactionManager.currentOrNull() + ?.let { it.withSuspendTransaction { block() } } + ?: newSuspendedTransaction { block() } + } + } fun init() { @@ -63,5 +71,5 @@ class DatabaseFactory( } - } + diff --git a/backend/app/src/main/kotlin/backend/config/Routing.kt b/backend/app/src/main/kotlin/backend/config/Routing.kt index 9352d53..513e61b 100644 --- a/backend/app/src/main/kotlin/backend/config/Routing.kt +++ b/backend/app/src/main/kotlin/backend/config/Routing.kt @@ -1,9 +1,11 @@ package backend.config import backend.repository.AdminRepository +import backend.repository.SpeakerRepository import backend.repository.UserRepository import backend.repository.WorkshopRepository import backend.route.* +import backend.service.WorkshopService import io.ktor.server.application.* import io.ktor.server.auth.* import io.ktor.server.response.* @@ -13,10 +15,12 @@ fun Application.configureRouting() { val userRepository = UserRepository() val adminRepository = AdminRepository() val workshopRepository = WorkshopRepository() + val speakerRepository = SpeakerRepository() + val workshopService = WorkshopService(environment.config) configureAuth0Route(userRepository) configureUserRoutes(userRepository) - configureWorkshopRoutes(workshopRepository) + configureWorkshopRoutes(workshopRepository, speakerRepository, workshopService) configureAdminRoutes(adminRepository) configureApiRoutes() diff --git a/backend/app/src/main/kotlin/backend/config/Serialization.kt b/backend/app/src/main/kotlin/backend/config/Serialization.kt index 32eecdf..f4d479b 100644 --- a/backend/app/src/main/kotlin/backend/config/Serialization.kt +++ b/backend/app/src/main/kotlin/backend/config/Serialization.kt @@ -6,6 +6,12 @@ import io.ktor.server.application.* import io.ktor.server.plugins.contentnegotiation.* import io.ktor.server.response.* import io.ktor.server.routing.* +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.modules.SerializersModule +import java.time.Instant fun Application.configureSerialization() { install(ContentNegotiation) { diff --git a/backend/app/src/main/kotlin/backend/dto/SpeakerDTO.kt b/backend/app/src/main/kotlin/backend/dto/SpeakerDTO.kt new file mode 100644 index 0000000..b524c3d --- /dev/null +++ b/backend/app/src/main/kotlin/backend/dto/SpeakerDTO.kt @@ -0,0 +1,10 @@ +package backend.dto + +import kotlinx.serialization.Serializable + +@Serializable +data class SpeakerDTO( + val name: String, + val bio: String, + val twitter: String, +) diff --git a/backend/app/src/main/kotlin/backend/dto/WorkshopDTO.kt b/backend/app/src/main/kotlin/backend/dto/WorkshopDTO.kt index 4f2257b..6be52b0 100644 --- a/backend/app/src/main/kotlin/backend/dto/WorkshopDTO.kt +++ b/backend/app/src/main/kotlin/backend/dto/WorkshopDTO.kt @@ -9,6 +9,8 @@ data class WorkshopDTO( val description: String, val startTime: Instant, val endTime: Instant, - val capacity: Int + val capacity: Int, + val active: Boolean, + val speakers: List ) diff --git a/backend/app/src/main/kotlin/backend/dto/WorkshopListImportDTO.kt b/backend/app/src/main/kotlin/backend/dto/WorkshopListImportDTO.kt new file mode 100644 index 0000000..00bbe7b --- /dev/null +++ b/backend/app/src/main/kotlin/backend/dto/WorkshopListImportDTO.kt @@ -0,0 +1,39 @@ +package backend.dto + +import kotlinx.serialization.Serializable + +@Serializable +data class WorkshopListImportDTO( + val sessions: List, +) + +@Serializable +data class WorkshopImport( + val intendedAudience: String, + val length: String, + val format: String, + val language: String, + val abstract: String, + val title: String, + val room: String, + val startTime: String, + val endTime: String, + val video: String? = "", + val startTimeZulu: String, + val endTimeZulu: String, + val id: String, + val sessionId: String, + val conferenceId: String, + val startSlot: String, + val startSlotZulu: String, + val speakers: List, + val workshopPrerequisites: String? = "", + val registerLoc: String? = "", +) + +@Serializable +data class Speaker( + val name: String, + val twitter: String? = "", + val bio: String, +) diff --git a/backend/app/src/main/kotlin/backend/repository/SpeakerRepository.kt b/backend/app/src/main/kotlin/backend/repository/SpeakerRepository.kt new file mode 100644 index 0000000..abf9f62 --- /dev/null +++ b/backend/app/src/main/kotlin/backend/repository/SpeakerRepository.kt @@ -0,0 +1,63 @@ +package backend.repository + +import backend.dto.SpeakerDTO +import com.inventy.plugins.DatabaseFactory.Companion.dbQuery +import org.jetbrains.exposed.sql.* + +data class Speaker( + val name: String, + val bio: String, + val twitter: String, + val workshopId: String, + val index: Int, +) { + fun toDTO() = SpeakerDTO(name, bio, twitter) +} + +class SpeakerRepository { + + internal object SpeakerTable : Table() { + val name = varchar("NAME", 256) + val bio = varchar("bio", 2048) + val twitter = varchar("twitter", 256) + val workshopId = reference("workshop_id", WorkshopRepository.WorkshopTable.id) + val index = integer("index"); + + override val primaryKey = PrimaryKey(arrayOf(workshopId, index), "id") + + fun toModel(it: ResultRow) = Speaker( + name = it[name], + bio = it[bio], + twitter = it[twitter], + workshopId = it[workshopId], + index = it[index] + ) + } + + suspend fun list(): List = dbQuery { + SpeakerTable.selectAll() + .map(SpeakerTable::toModel) + } + + + private suspend fun create(speakers: List) = dbQuery { + SpeakerTable.batchInsert(speakers) { speaker -> + this[SpeakerTable.name] = speaker.name + this[SpeakerTable.bio] = speaker.bio + this[SpeakerTable.twitter] = speaker.twitter + this[SpeakerTable.workshopId] = speaker.workshopId + this[SpeakerTable.index] = speaker.index + } + } + + + + suspend fun replace(speakers: List) { + + dbQuery { + SpeakerTable.deleteAll() + create(speakers) + + } + } +} diff --git a/backend/app/src/main/kotlin/backend/repository/WorkshopRegistrationRepository.kt b/backend/app/src/main/kotlin/backend/repository/WorkshopRegistrationRepository.kt index c8904f6..8798bb4 100644 --- a/backend/app/src/main/kotlin/backend/repository/WorkshopRegistrationRepository.kt +++ b/backend/app/src/main/kotlin/backend/repository/WorkshopRegistrationRepository.kt @@ -3,10 +3,8 @@ package backend.repository import com.inventy.plugins.DatabaseFactory.Companion.dbQuery import kotlinx.datetime.Instant import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ResultRow -import org.jetbrains.exposed.sql.insertAndGetId +import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.kotlin.datetime.timestamp -import org.jetbrains.exposed.sql.select enum class WorkshopRegistrationState { @@ -16,7 +14,7 @@ enum class WorkshopRegistrationState { class WorkshopRegistration( override val id: Int, val userId: Int, - val workshopId: Int, + val workshopId: String, val createdAt: Instant, val updatedAt: Instant, var state: WorkshopRegistrationState = WorkshopRegistrationState.PENDING, @@ -33,7 +31,7 @@ class WorkshopRegistrationRepository(userId: Int) : UserOwnedRepository(userId) fun toModel(it: ResultRow) = WorkshopRegistration( it[id].value, it[userId].value, - it[workshopId].value, + it[workshopId], it[createdAt], it[updatedAt], it[state], @@ -51,7 +49,7 @@ class WorkshopRegistrationRepository(userId: Int) : UserOwnedRepository(userId) } suspend fun list(): List = dbQuery { - WorkshopRegistrationTable.select{ WorkshopRegistrationTable.userId eq userId } + WorkshopRegistrationTable.selectAll().where { WorkshopRegistrationTable.userId eq userId } .map(WorkshopRegistrationTable::toModel) } diff --git a/backend/app/src/main/kotlin/backend/repository/WorkshopRepository.kt b/backend/app/src/main/kotlin/backend/repository/WorkshopRepository.kt index 4aa077f..8252084 100644 --- a/backend/app/src/main/kotlin/backend/repository/WorkshopRepository.kt +++ b/backend/app/src/main/kotlin/backend/repository/WorkshopRepository.kt @@ -1,43 +1,101 @@ package backend.repository +import backend.dto.SpeakerDTO import backend.dto.WorkshopDTO +import backend.dto.WorkshopImport +import backend.dto.WorkshopListImportDTO import com.inventy.plugins.DatabaseFactory.Companion.dbQuery import kotlinx.datetime.Instant import org.jetbrains.exposed.dao.id.IntIdTable -import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.kotlin.datetime.timestamp -import org.jetbrains.exposed.sql.selectAll -class Workshop( - override val id: Int, +data class Workshop( + val id: String, val title: String, val description: String, val startTime: Instant, val endTime: Instant, - val capacity: Int -) : Model { - fun toDTO() = WorkshopDTO(title, description, startTime, endTime, capacity) + val capacity: Int, + val active: Boolean +) { + fun toDTO(speakerDTOS: List?) = WorkshopDTO(title, description, startTime, endTime, capacity, active, speakerDTOS ?: listOf()) } class WorkshopRepository { - internal object WorkshopTable : IntIdTable() { - val title = varchar("title", 128) - val description = varchar("description", 256) + + companion object { + private const val WORKSHOP_CAPACITY = 30 + } + + internal object WorkshopTable : Table() { + + val id = varchar("id", 64) + val title = varchar("title", 256) + val description = varchar("description", 2048) val startTime = timestamp("start_time") val endTime = timestamp("end_time") val capacity = integer("capacity") + val active = bool("active") + + override val primaryKey = PrimaryKey(arrayOf(id), "id") fun toModel(it: ResultRow) = Workshop( - it[id].value, - it[title], - it[description], - it[startTime], - it[endTime], - it[capacity] + id = it[id], + title = it[title], + description = it[description], + startTime = it[startTime], + endTime = it[endTime], + capacity = it[capacity], + active = it[active], ) } + suspend fun list(): List = dbQuery { WorkshopTable.selectAll() .map(WorkshopTable::toModel) } + + + private suspend fun upsertActive(workshops: List) = dbQuery { + WorkshopTable.batchUpsert(workshops) { workshop -> + this[WorkshopTable.id] = workshop.id + this[WorkshopTable.title] = workshop.title + this[WorkshopTable.description] = + workshop.abstract.take(250) + if (workshop.abstract.length > 250) " ..." else "" + this[WorkshopTable.startTime] = Instant.parse(workshop.startTimeZulu) + this[WorkshopTable.endTime] = Instant.parse(workshop.endTimeZulu) + this[WorkshopTable.capacity] = WORKSHOP_CAPACITY + this[WorkshopTable.active] = true + } + } + + private suspend fun upsert(workshops: List) = dbQuery { + WorkshopTable.batchUpsert(workshops) { workshop -> + this[WorkshopTable.id] = workshop.id + this[WorkshopTable.title] = workshop.title + this[WorkshopTable.description] = workshop.description + this[WorkshopTable.startTime] = workshop.startTime + this[WorkshopTable.endTime] = workshop.endTime + this[WorkshopTable.capacity] = WORKSHOP_CAPACITY + this[WorkshopTable.active] = workshop.active + } + } + + private suspend fun setWorkshopsToDisabled(activeWorkshops: List) { + val activeWorkshopsIds = activeWorkshops.map { it.id } + val allDisabledList = list() + .filterNot { it.id in activeWorkshopsIds } + .map { it.copy(active = false) } + val t = upsert(allDisabledList) + } + + suspend fun update(workshops: List) { + + dbQuery { + setWorkshopsToDisabled(workshops) + upsertActive(workshops) + + } + } } diff --git a/backend/app/src/main/kotlin/backend/route/WorkshopRoute.kt b/backend/app/src/main/kotlin/backend/route/WorkshopRoute.kt index 115af11..5aa0020 100644 --- a/backend/app/src/main/kotlin/backend/route/WorkshopRoute.kt +++ b/backend/app/src/main/kotlin/backend/route/WorkshopRoute.kt @@ -1,24 +1,42 @@ package backend.route -import backend.repository.Workshop +import backend.service.WorkshopService +import backend.repository.SpeakerRepository import backend.repository.WorkshopRepository +import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.auth.* import io.ktor.server.response.* import io.ktor.server.routing.* -fun Application.configureWorkshopRoutes(workshopRepository: WorkshopRepository) { +fun Application.configureWorkshopRoutes(workshopRepository: WorkshopRepository, + speakerRepository: SpeakerRepository, + workshopService: WorkshopService +) { routing { - workshopRoute(workshopRepository) + workshopRoute(workshopRepository, speakerRepository, workshopService) } } -fun Routing.workshopRoute(workshopRepository: WorkshopRepository) { - authenticate ("auth0-user"){ +fun Routing.workshopRoute(workshopRepository: WorkshopRepository, + speakerRepository: SpeakerRepository, + workshopService: WorkshopService +) { + authenticate("auth0-user") { get("/workshop") { - call.respond(workshopRepository.list().map(Workshop::toDTO)) + val speakers = speakerRepository.list() + .groupBy ({ it.workshopId }, {it.toDTO()} ) + + val workshops = workshopRepository.list().map { it.toDTO(speakers[it.id]) } + + call.respond(workshops) + } + } + authenticate("auth0-admin") { + post("/update-workshop") { + workshopService.workshopDatabaseUpdate() + call.respond(HttpStatusCode.OK) } } - } diff --git a/backend/app/src/main/kotlin/backend/service/WorkshopService.kt b/backend/app/src/main/kotlin/backend/service/WorkshopService.kt new file mode 100644 index 0000000..5090fb1 --- /dev/null +++ b/backend/app/src/main/kotlin/backend/service/WorkshopService.kt @@ -0,0 +1,54 @@ +package backend.service + +import backend.config.defaultClient +import backend.dto.WorkshopListImportDTO +import backend.repository.Speaker +import backend.repository.SpeakerRepository +import backend.repository.WorkshopRepository +import com.inventy.plugins.DatabaseFactory.Companion.dbQuery +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.server.config.* +import org.slf4j.LoggerFactory + +class WorkshopService(private val config: ApplicationConfig) { + private val log = LoggerFactory.getLogger(WorkshopService::class.java) + + suspend fun workshopDatabaseUpdate() { + + val workshopRepository = WorkshopRepository() + val speakerRepository = SpeakerRepository() + + val client = HttpClient { + defaultClient() + } + + val response: WorkshopListImportDTO = + client.get { url(config.property("workshopDatabase.workshopDataUrl").getString()) }.body() + + log.info(response.toString()) + + val speakers = extractSpeakers(response) + + dbQuery { + workshopRepository.update(response.sessions) + speakerRepository.replace(speakers) + } + + } + + private fun extractSpeakers(response: WorkshopListImportDTO): List { + return response.sessions.flatMap { workshop -> + workshop.speakers.withIndex().map { (index, speaker) -> + Speaker( + name = speaker.name, + twitter = speaker.twitter ?: "", + bio = speaker.bio, + workshopId = workshop.id, + index = index, + ) + } + } + } +} \ No newline at end of file diff --git a/backend/app/src/main/resources/application.yaml b/backend/app/src/main/resources/application.yaml index ca7fe36..b9fa4f9 100644 --- a/backend/app/src/main/resources/application.yaml +++ b/backend/app/src/main/resources/application.yaml @@ -14,6 +14,10 @@ database: user: workshop password: workshop +workshopDatabase: + workshopDataUrl: 'https://sleepingpill.javazone.no/public/allSessions/javazone_2023' + workshopDataUpdateIntervalMinutes: 1 + auth0: issuer: 'https://test-javabin.eu.auth0.com/' diff --git a/backend/app/src/main/resources/db/migration/V1__initial.sql b/backend/app/src/main/resources/db/migration/V1__initial.sql index fdc984d..67835fc 100644 --- a/backend/app/src/main/resources/db/migration/V1__initial.sql +++ b/backend/app/src/main/resources/db/migration/V1__initial.sql @@ -14,29 +14,57 @@ VALUES create table workshop ( - id int GENERATED ALWAYS AS IDENTITY primary key, + id varchar(64) primary key, title varchar not null, description varchar, start_time timestamp with time zone not null, end_time timestamp with time zone not null, - capacity int not null + capacity int not null, + active boolean not null +); + +INSERT INTO workshop (id, title, description, start_time, end_time, capacity, active) +VALUES ('18aaf407-ff02-4f49-b11d-29877a9de906', 'Moderne brukergrensesnitt i en legacy kodebase', + 'NAV sin pensjonsløsning, Pesys, har i mange år vært blant Norges største og mest komplekse kodebaser. Opprinnelig skrevet med teknologi, som i dag, ville fått utviklere til å gråte. Vi skal i dette foredraget fortelle hvordan vi har modernisert det grafiske brukergrensesnittet. Vi skal rett og slett fortelle deg en ærlig historie om hvordan vår erfaring med dette var. Vi kommer til å snakke om den moderne retningen som premissgiver, hvor vi måtte ta tak i både menneskelige og tekniske utfordringer. På veien skal vi også prate om hvordan vi organiserte teamene og folkene, og fortelle hvordan vi tok tak i arkitekturen og det sosiotekniske aspektet av det. Du skal på den måten lære hvorfor vi mener dette økte selvsikkerheten og eierskapet til alle involverte. Videre deler vi hvordan vi har løst de tekniske utfordringene omkring implementasjon; integrasjon og sikkerhet ved å blant annet bruke api gateways og micro-frontends. Etter å ha hørt foredraget vil du være i stand til å bedre ta tak i modernisering av legacy-kodebase i din organisasjon.', + '2023-09-06T10:20', '2023-09-06T11:20', 30, true), + ('37cdf4dd-4f9a-4d93-ad9f-eb4994cb2f52', 'A software developer''s guide to licenses and other legalities', + 'In the fast-paced world of software development, it is critical for developers to have a clear understanding of the legal landscape surrounding the software that they develop. This presentation will look into the different branches of software licenses and open source. In this context, the presentation will discuss common terms of commercial agreements and what pitfalls to avoid. Finally, this presentation will look into the OpenJDK project and its formal organization. Attendees will gain a solid understanding of the legal and technical considerations of choosing licensed software, enabling them to make informed decisions about their projects.', + '2023-09-06T13:00', '2023-09-06T14:00', 100, true), + ('3ba51b2d-c986-4678-8b74-c40257576cb6', 'Free enterprise search with OpenSearch', + 'Elasticsearch is nowdays a preferred solution for enterprise search. You may be aware that not all of its features are available for free but did you know there is a solid open source alternative created by Amazon called OpenSearch ? And in fact this free alternative is based on a fork of Elasticsearch ? There are however certain pitfalls if you prefer to go the OpenSearch route. In this session we will make a comparison between traditional Elasticsearch and OpenSearch to understand is going completely free and open source a save path.', + '2023-09-07T09:00', '2023-09-07T09:45', 30, true), + ('88d67125-288c-499c-85af-e0b6bab2316f', 'Welcome to the Jungle - A safari through the JVM landscape', + 'OpenJDK with it’s Java Virtual Machine is great but there is not only one flavour but many. There is Oracle OpenJDK, Eclipse Temurin, IBM Semeru, Amazon Corretto, Azul Zulu, Alibaba Dragonwell, Huawei Bi Sheng, Tencent Kona and many more. Did you ever ask yourself which one is better, faster, free or something similar? Or do you want to know where the differences are in those distributions, well then this session might bring some answers to your questions. It will give you an idea about what the JVM is and will cover all the available distributions not only of OpenJDK but also of GraalVM and will try to explain the differences and features of the available distributions. It will also try to give you an idea what JVM to use for specific use cases.', + '2023-09-06T13:00', '2023-09-06T14:00', 30, true), + ('3a06f863-c1ee-4d15-be2e-3cc53ae473a9', 'Stop breaking stuff all the time!', + 'Verifying behaviors of the cloud-native applications and ensuring that all of the services in the system work correctly together is both crucial and challenging. Manually maintaining environments to test the correctness of the entire system is undevops-like and fragile.\n\nLuckily, modern tools can help you to build automated, reliable test pipelines, and in this session, we explore how using Spring Cloud Contract and Testcontainers together can improve your testing and deployment processes.\n\nSpring Cloud Contract is an implementation of Consumer-Driven Contracts, an approach that provides a way to easily describe and verify APIs, at the same time allowing building API backward compatibility verification into the deployment process.\n\nTestcontainers lets developers programmatically build test environments consisting of real services running in lightweight and disposable containers. It turns the process of integration testing into a seamless, unit-test-like experience.\n\nIn this presentation, we’ll show how contract and integration tests complement each other and explore one of the most natural and reliable approaches to service evolution with contract testing. We’ll discuss why in Spring Cloud Contract, we’ve decided to switch to using Testcontainers as the solution for Kafka and AMQP messaging verification and demonstrate practical use-cases and code examples of how to set up both types of tests in your applications and deployment pipelines.', + '2023-09-07T15:40', '2023-09-07T16:40', 30, true), + ('6f9c99a8-2aab-4473-a848-34b4492acb8f', 'Building a fishy cloud datawarehouse in 2023', + 'At Br Karlsen we make, buy and sell fish of all kinds, here are our experience in deploying the modern data stack , built on Airbyte, dbt , BigQuery and Apache Superset. Why we chose the components we did, and what our experience has been. When did we choose the managed version vs the open source version of a product? ', + '2023-09-07T15:40', '2023-09-07T16:25', 30, true), + ('6f9c99a8-2aab-4473-a84f', 'Should be inactive', + 'At Br Karlsen we make, buy and sell fish of all kinds, here are our experience in deploying the modern data stack , built on Airbyte, dbt , BigQuery and Apache Superset. Why we chose the components we did, and what our experience has been. When did we choose the managed version vs the open source version of a product? ', + '2023-09-07T15:40', '2023-09-07T16:25', 30, true); + +create table speaker +( + index int, + name varchar, + workshop_id varchar(64), + bio varchar, + twitter varchar, + PRIMARY KEY (workshop_id, index), + + constraint speaker_fk_workshop foreign key (workshop_id) references workshop (id) ); -INSERT INTO workshop (title, description, start_time, end_time, capacity) -VALUES - ('Moderne brukergrensesnitt i en legacy kodebase', 'NAV sin pensjonsløsning, Pesys, har i mange år vært blant Norges største og mest komplekse kodebaser. Opprinnelig skrevet med teknologi, som i dag, ville fått utviklere til å gråte. Vi skal i dette foredraget fortelle hvordan vi har modernisert det grafiske brukergrensesnittet. Vi skal rett og slett fortelle deg en ærlig historie om hvordan vår erfaring med dette var. Vi kommer til å snakke om den moderne retningen som premissgiver, hvor vi måtte ta tak i både menneskelige og tekniske utfordringer. På veien skal vi også prate om hvordan vi organiserte teamene og folkene, og fortelle hvordan vi tok tak i arkitekturen og det sosiotekniske aspektet av det. Du skal på den måten lære hvorfor vi mener dette økte selvsikkerheten og eierskapet til alle involverte. Videre deler vi hvordan vi har løst de tekniske utfordringene omkring implementasjon; integrasjon og sikkerhet ved å blant annet bruke api gateways og micro-frontends. Etter å ha hørt foredraget vil du være i stand til å bedre ta tak i modernisering av legacy-kodebase i din organisasjon.', '2023-09-06T10:20', '2023-09-06T11:20', 30), - ('A software developer''s guide to licenses and other legalities', 'In the fast-paced world of software development, it is critical for developers to have a clear understanding of the legal landscape surrounding the software that they develop. This presentation will look into the different branches of software licenses and open source. In this context, the presentation will discuss common terms of commercial agreements and what pitfalls to avoid. Finally, this presentation will look into the OpenJDK project and its formal organization. Attendees will gain a solid understanding of the legal and technical considerations of choosing licensed software, enabling them to make informed decisions about their projects.', '2023-09-06T13:00', '2023-09-06T14:00', 100), - ('Free enterprise search with OpenSearch', 'Elasticsearch is nowdays a preferred solution for enterprise search. You may be aware that not all of its features are available for free but did you know there is a solid open source alternative created by Amazon called OpenSearch ? And in fact this free alternative is based on a fork of Elasticsearch ? There are however certain pitfalls if you prefer to go the OpenSearch route. In this session we will make a comparison between traditional Elasticsearch and OpenSearch to understand is going completely free and open source a save path.', '2023-09-07T09:00', '2023-09-07T09:45', 30), - ('Welcome to the Jungle - A safari through the JVM landscape', 'OpenJDK with it’s Java Virtual Machine is great but there is not only one flavour but many. There is Oracle OpenJDK, Eclipse Temurin, IBM Semeru, Amazon Corretto, Azul Zulu, Alibaba Dragonwell, Huawei Bi Sheng, Tencent Kona and many more. Did you ever ask yourself which one is better, faster, free or something similar? Or do you want to know where the differences are in those distributions, well then this session might bring some answers to your questions. It will give you an idea about what the JVM is and will cover all the available distributions not only of OpenJDK but also of GraalVM and will try to explain the differences and features of the available distributions. It will also try to give you an idea what JVM to use for specific use cases.', '2023-09-06T13:00', '2023-09-06T14:00', 30), - ('Stop breaking stuff all the time!', 'Verifying behaviors of the cloud-native applications and ensuring that all of the services in the system work correctly together is both crucial and challenging. Manually maintaining environments to test the correctness of the entire system is undevops-like and fragile.\n\nLuckily, modern tools can help you to build automated, reliable test pipelines, and in this session, we explore how using Spring Cloud Contract and Testcontainers together can improve your testing and deployment processes.\n\nSpring Cloud Contract is an implementation of Consumer-Driven Contracts, an approach that provides a way to easily describe and verify APIs, at the same time allowing building API backward compatibility verification into the deployment process.\n\nTestcontainers lets developers programmatically build test environments consisting of real services running in lightweight and disposable containers. It turns the process of integration testing into a seamless, unit-test-like experience.\n\nIn this presentation, we’ll show how contract and integration tests complement each other and explore one of the most natural and reliable approaches to service evolution with contract testing. We’ll discuss why in Spring Cloud Contract, we’ve decided to switch to using Testcontainers as the solution for Kafka and AMQP messaging verification and demonstrate practical use-cases and code examples of how to set up both types of tests in your applications and deployment pipelines.', '2023-09-07T15:40', '2023-09-07T16:40', 30), - ('Building a fishy cloud datawarehouse in 2023', 'At Br Karlsen we make, buy and sell fish of all kinds, here are our experience in deploying the modern data stack , built on Airbyte, dbt , BigQuery and Apache Superset. Why we chose the components we did, and what our experience has been. When did we choose the managed version vs the open source version of a product? ', '2023-09-07T15:40', '2023-09-07T16:25', 30); create table workshop_registration ( id int GENERATED ALWAYS AS IDENTITY primary key, user_id int, - workshop_id int, + workshop_id varchar(64), state varchar, created_at timestamp with time zone not null default current_timestamp, updated_at timestamp with time zone not null default current_timestamp, @@ -46,6 +74,5 @@ create table workshop_registration ); INSERT INTO workshop_registration (user_id, workshop_id, state, created_at, updated_at) -VALUES - ('1', '1', 0, '2023-09-06T10:20', '2023-09-06T10:20'), - ('1', '2', 0, '2023-09-06T13:00', '2023-09-06T13:00'); +VALUES ('1', '18aaf407-ff02-4f49-b11d-29877a9de906', 0, '2023-09-06T10:20', '2023-09-06T10:20'), + ('1', '37cdf4dd-4f9a-4d93-ad9f-eb4994cb2f52', 0, '2023-09-06T13:00', '2023-09-06T13:00'); diff --git a/backend/app/src/main/resources/logback.xml b/backend/app/src/main/resources/logback.xml index 12f0afe..22d0fab 100644 --- a/backend/app/src/main/resources/logback.xml +++ b/backend/app/src/main/resources/logback.xml @@ -10,4 +10,5 @@ +