Skip to content

feat: add Postgres storage with Scalasql and Hikari; cleanup deprecated code and configs#1164

Open
warcholjakub wants to merge 10 commits intoscalacenter:mainfrom
warcholjakub:database-change
Open

feat: add Postgres storage with Scalasql and Hikari; cleanup deprecated code and configs#1164
warcholjakub wants to merge 10 commits intoscalacenter:mainfrom
warcholjakub:database-change

Conversation

@warcholjakub
Copy link
Copy Markdown
Collaborator

Summary

This PR introduces a new Postgres storage implementation using Scalasql and HikariCP for connection pooling.

Key changes include

  • Added a new Postgres storage using Scalasql and HikariCP for efficient connection pooling.
  • Removed deprecated components such as UsersContainer and related legacy storage tests.
  • Updated MongoDB configuration keys for clarity and consistency.
  • Introduced new configuration options and files for Postgres support.
  • Integrated Flyway for database migrations, including new SQL migration scripts.
  • Implemented custom encoders and decoders for several types to support the new backend.

@warcholjakub warcholjakub marked this pull request as draft October 10, 2025 15:49
@warcholjakub warcholjakub marked this pull request as ready for review November 17, 2025 12:24
@warcholjakub warcholjakub marked this pull request as draft January 22, 2026 18:10
…ed code and configs

- Introduced new Postgres storage implementation using Scalasql and HikariCP connection pool
- Removed deprecated components such as UsersContainer
- Updated and cleaned up MongoDB config keys
- Added configuration files and keys for Postgres support
- Removed deprecated storage tests
- Integrated Flyway for database migrations and added SQL migration scripts
- Added custom encoders/decoders for existing types where needed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a comprehensive Postgres storage implementation for Scastie using Scalasql and HikariCP, while cleaning up deprecated code related to user privacy policy management that was scheduled for removal.

Changes:

  • Added a complete Postgres storage backend with Scalasql ORM, HikariCP connection pooling, and Flyway database migrations
  • Removed deprecated UsersContainer trait and all privacy policy-related user management code across the codebase
  • Renamed Problem.line field to Problem.startLine for consistency and clarity across all usage sites
  • Updated MongoDB configuration keys from scastie.mongodb to org.scastie.mongodb for namespace consistency

Reviewed changes

Copilot reviewed 49 out of 50 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
storage/src/main/scala/org/scastie/storage/postgres/* New Postgres storage implementation with container, converters, and table definitions
storage/src/main/resources/db/migration/*.sql Flyway migration scripts defining the Postgres database schema
storage/src/main/scala/org/scastie/storage/UsersContainer.scala Removed deprecated UsersContainer trait
storage/src/main/scala/org/scastie/storage/*/Filesystem/MongoDB/InMemoryUsersContainer.scala Removed all UsersContainer implementations
storage/src/main/scala/org/scastie/storage/SnippetsContainer.scala Removed readOldSnippet method
storage/src/main/scala/org/scastie/storage/*/FilesystemSnippetsContainer.scala Removed readOldSnippet implementation
api/src/main/scala/org/scastie/api/CompilerInfo.scala Renamed Problem.line to Problem.startLine
api/src/main/scala/org/scastie/api/SnippetId.scala Added fromString method for parsing snippet IDs from URL strings
api/src/main/scala/org/scastie/api/ScalaTarget.scala Changed encoder/decoder to use tagged union format for Postgres storage
runtime-api/src/main/scala/org/scastie/runtime/api/* Added JSON string parsing methods for RuntimeError, Instrumentation, Position, and Render
server/src/main/scala/org/scastie/server/RestApiServer.scala Removed deprecated privacy policy API methods
server/src/main/scala/org/scastie/server/routes/ApiRoutes.scala Removed privacy policy API routes
client/src/main/scala/org/scastie/client/* Removed PrivacyPolicyPrompt component and related state/backend methods
balancer/src/main/scala/org/scastie/balancer/DispatchActor.scala Added Postgres container support; removed privacy policy message handlers
storage/src/test/scala/org/scastie/storage/ContainerTest.scala Updated to support Postgres container testing; removed UsersContainer tests
sbt-runner/src/test/scala/org/scastie/sbt/SbtActorTest.scala Updated test assertions for startLine field rename
scala-cli-runner/src/test/scala/org/scastie/scalacli/ScalaCliRunnerTest.scala Updated test assertions for startLine field rename
sbt-runner/src/main/scala/org/scastie/sbt/* Updated code for startLine field rename
instrumentation/src/main/scala/org/scastie/instrumentation/InstrumentedInputs.scala Updated for startLine field rename
client/src/main/scala/org/scastie/client/components/editor/CodeEditor.scala Updated editor diagnostics for startLine field
server/src/main/resources/reference.conf Updated MongoDB config key and added Postgres configuration
deployment/*.conf Updated to use Postgres storage; added Postgres config template
project/Deployment.scala Added Postgres configuration validation in deployment script
scripts/PostgresMigrationScript.scala Added MongoDB to Postgres migration script
build.sbt Added dependencies for Scalasql, HikariCP, Flyway, and Chimney

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +13 to +40
object Render {
/* Convert JSON string to Render */
/* Example: {"Value":{"v":"some value","className":"some class"}} -> Value("some value", "some class") */
def fromJsonString(json: String): Option[Render] = {
if (json.startsWith("""{"Value":""")) {
val vPattern = """"v":"(.*?)"""".r
val classNamePattern = """"className":"(.*?)"""".r
for {
v <- vPattern.findFirstMatchIn(json).map(_.group(1))
className <- classNamePattern.findFirstMatchIn(json).map(_.group(1))
} yield Value(v, className)
} else if (json.startsWith("""{"Html":""")) {
val aPattern = """"a":"(.*?)"""".r
val foldedPattern = """"folded":(true|false)""".r
for {
a <- aPattern.findFirstMatchIn(json).map(_.group(1))
folded <- foldedPattern.findFirstMatchIn(json).map(_.group(1).toBoolean)
} yield Html(a, folded)
} else if (json.startsWith("""{"AttachedDom":""")) {
val uuidPattern = """"uuid":"(.*?)"""".r
val foldedPattern = """"folded":(true|false)""".r
for {
uuid <- uuidPattern.findFirstMatchIn(json).map(_.group(1))
folded <- foldedPattern.findFirstMatchIn(json).map(_.group(1).toBoolean)
} yield AttachedDom(uuid, folded)
} else None
}
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern (.*?) used to extract JSON values doesn't handle escaped quotes or other special characters correctly. If the JSON values contain escaped quotes (e.g., "value with \" quote"), the regex will incorrectly match up to the first escaped quote. Consider using a proper JSON parser (like Circe which is already a dependency) instead of regex-based parsing for more robust and correct deserialization.

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +34
def fromJsonString(json: String): Option[RuntimeError] = {
val msgPattern = """"message":"(.*?)"""".r
val linePattern = """"line":(null|\d+)""".r
val stackPattern = """"fullStack":"(.*?)"""".r

for {
msgMatch <- msgPattern.findFirstMatchIn(json)
stackMatch <- stackPattern.findFirstMatchIn(json)
lineMatch <- linePattern.findFirstMatchIn(json)
msg = msgMatch.group(1)
stack = stackMatch.group(1)
line = lineMatch.group(1) match {
case "null" => None
case numStr => Some(numStr.toInt)
}
} yield RuntimeError(msg, line, stack)
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the Render.fromJsonString, this regex-based JSON parsing doesn't handle escaped quotes or nested JSON structures correctly. The pattern (.*?) will fail on complex JSON strings. Using a proper JSON parser would be more robust and correct.

Copilot uses AI. Check for mistakes.
Comment thread storage/src/main/scala/org/scastie/storage/postgres/PostgresContainer.scala Outdated
Comment thread api/src/main/scala/org/scastie/api/SnippetId.scala
Comment thread runtime-api/src/main/scala/org/scastie/runtime/api/Instrumentation.scala Outdated
Comment thread storage/src/main/resources/db/migration/V2__create_snippets_table.sql Outdated
Comment thread storage/src/main/resources/db/migration/V3__create_progresses_table.sql Outdated
@warcholjakub warcholjakub marked this pull request as ready for review February 26, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants