-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update Connect on Forge implementation to use mapping table
- Add Slick-based ForgeInstallationRepository implementation - Add unit and integration tests for ForgeInstallationRepository implementation - Update evolutions script to configure new table, indices and foreign key column Fixes #34
- Loading branch information
Showing
17 changed files
with
546 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
.idea | ||
.bsp | ||
target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<configuration> | ||
|
||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> | ||
<!-- encoders are assigned the type | ||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> | ||
<encoder> | ||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<root level="info"> | ||
<appender-ref ref="STDOUT" /> | ||
</root> | ||
</configuration> |
42 changes: 42 additions & 0 deletions
42
src/it/scala/io/toolsplus/atlassian/connect/play/slick/PostgresContainerTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package io.toolsplus.atlassian.connect.play.slick | ||
|
||
import com.dimafeng.testcontainers.PostgreSQLContainer | ||
import com.dimafeng.testcontainers.scalatest.TestContainerForAll | ||
import org.scalatest.{Suite, TestData, TestSuite} | ||
import org.scalatestplus.play.guice.GuiceOneAppPerTest | ||
import org.testcontainers.utility.DockerImageName | ||
import play.api.Application | ||
import play.api.db.DBApi | ||
import play.api.db.evolutions.{ClassLoaderEvolutionsReader, Evolutions} | ||
import play.api.inject.guice.GuiceApplicationBuilder | ||
|
||
trait PostgresContainerTest extends GuiceOneAppPerTest with TestContainerForAll { self: TestSuite => | ||
|
||
val postgresVersion = "15.5" | ||
|
||
override val containerDef: PostgreSQLContainer.Def = | ||
PostgreSQLContainer.Def( | ||
DockerImageName.parse(s"postgres:$postgresVersion"), | ||
databaseName = "intercom", | ||
username = "test", | ||
password = "test", | ||
) | ||
|
||
override def newAppForTest(td: TestData): Application = withContainers { | ||
container => | ||
GuiceApplicationBuilder() | ||
.configure(ContainerDbConfiguration.configuration(container)) | ||
.build() | ||
} | ||
|
||
def dbApi(implicit app: Application): DBApi = | ||
Application.instanceCache[DBApi].apply(app) | ||
|
||
def withEvolutions[T](block: => T): T = | ||
Evolutions.withEvolutions( | ||
dbApi.database("default"), | ||
ClassLoaderEvolutionsReader.forPrefix("evolutions/")) { | ||
block | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
230 changes: 230 additions & 0 deletions
230
.../scala/io/toolsplus/atlassian/connect/play/slick/SlickForgeInstallationRepositoryIt.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
package io.toolsplus.atlassian.connect.play.slick | ||
|
||
import io.toolsplus.atlassian.connect.play.api.models.{ | ||
DefaultAtlassianHost, | ||
DefaultForgeInstallation | ||
} | ||
import io.toolsplus.atlassian.connect.play.slick.fixtures.AtlassianHostFixture | ||
import io.toolsplus.atlassian.connect.play.slick.generators.AtlassianHostGen | ||
import org.scalatest.{BeforeAndAfterEach, DoNotDiscover} | ||
import org.scalatest.concurrent.Eventually | ||
import org.scalatestplus.play.PlaySpec | ||
import play.api.Application | ||
import play.api.test.{DefaultAwaitTimeout, FutureAwaits} | ||
|
||
import scala.collection.immutable | ||
import scala.concurrent.Future | ||
import scala.concurrent.ExecutionContext.Implicits.global | ||
|
||
@DoNotDiscover | ||
class SlickForgeInstallationRepositoryIt | ||
extends PlaySpec | ||
with PostgresContainerTest | ||
with FutureAwaits | ||
with Eventually | ||
with DefaultAwaitTimeout | ||
with AtlassianHostGen { | ||
|
||
val hostA: DefaultAtlassianHost = | ||
atlassianHostGen | ||
.retryUntil(_ => true) | ||
.sample | ||
.get | ||
.copy(installationId = Some("ari-installation-id-a")) | ||
val hostB: DefaultAtlassianHost = | ||
atlassianHostGen | ||
.retryUntil(_ => true) | ||
.sample | ||
.get | ||
.copy(installationId = Some("ari-installation-id-b")) | ||
|
||
val fakeInstallationHostA: DefaultForgeInstallation = | ||
DefaultForgeInstallation("fake-installation-id", hostA.clientKey) | ||
|
||
val fakeInstallationsHostB: Seq[DefaultForgeInstallation] = Seq( | ||
"ari-installation-id-1", | ||
"ari-installation-id-2", | ||
"ari-installation-id-3", | ||
"ari-installation-id-4", | ||
"ari-installation-id-5" | ||
).map(DefaultForgeInstallation(_, hostB.clientKey)) | ||
|
||
def hostRepo(implicit app: Application): SlickAtlassianHostRepository = | ||
Application.instanceCache[SlickAtlassianHostRepository].apply(app) | ||
|
||
def forgeInstallationRepo( | ||
implicit app: Application): SlickForgeInstallationRepository = | ||
Application.instanceCache[SlickForgeInstallationRepository].apply(app) | ||
|
||
"Using a Slick Forge installation repository" when { | ||
|
||
"repository is empty" should { | ||
|
||
"not find any hosts when fetching all" in { | ||
await { | ||
forgeInstallationRepo.all() | ||
} mustEqual immutable.Seq.empty | ||
} | ||
|
||
"return None when trying to find a non existent installation by installation id" in { | ||
await { | ||
forgeInstallationRepo.findByInstallationId("fake-installation-id") | ||
} mustBe None | ||
} | ||
|
||
"return empty Seq when trying to find a installations by client key" in { | ||
await { | ||
forgeInstallationRepo.findByClientKey("fake-client-key") | ||
} mustBe immutable.Seq.empty | ||
} | ||
|
||
} | ||
|
||
"saving a Forge installation to the repository" should { | ||
|
||
"successfully save the installation" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustEqual fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustEqual Seq(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
"find the inserted installation by installation id" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} | ||
|
||
await { | ||
forgeInstallationRepo.findByInstallationId( | ||
fakeInstallationHostA.installationId) | ||
} mustBe Some(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
"find the inserted installation by client key" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} | ||
|
||
await { | ||
forgeInstallationRepo.findByClientKey( | ||
fakeInstallationHostA.clientKey) | ||
} mustBe Seq(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
} | ||
|
||
"saving different Forge installation with the same client key" should { | ||
|
||
"save all installations" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostB)) | ||
await { | ||
Future.sequence( | ||
fakeInstallationsHostB.map(forgeInstallationRepo.save)) | ||
} | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustBe fakeInstallationsHostB | ||
} | ||
} | ||
|
||
} | ||
|
||
"saving the same Forge installation twice" should { | ||
|
||
"not duplicate the installation" in { | ||
await(hostRepo.save(hostA)) | ||
withEvolutions { | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustBe fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustBe fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustBe Seq(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
} | ||
|
||
"updating a Forge installation" should { | ||
|
||
/** | ||
* This scenario appears if there is site import (migration) | ||
* from on Atlassian instance to another. The installation id | ||
* remains the same but the client key changes. | ||
*/ | ||
"successfully store the updated version" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await(hostRepo.save(hostB)) | ||
val updated = | ||
fakeInstallationHostA.copy(clientKey = hostB.clientKey) | ||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustBe fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.save(updated) | ||
} mustBe updated | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustBe Seq(updated) | ||
} | ||
} | ||
|
||
} | ||
|
||
"deleting a Forge installation by client key" should { | ||
|
||
"successfully delete all installations" in { | ||
withEvolutions { | ||
await(hostRepo.save(hostA)) | ||
await(hostRepo.save(hostB)) | ||
await { | ||
Future.sequence( | ||
fakeInstallationsHostB.map(forgeInstallationRepo.save)) | ||
} | ||
|
||
await { | ||
forgeInstallationRepo.save(fakeInstallationHostA) | ||
} mustBe fakeInstallationHostA | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
}.size mustBe (fakeInstallationsHostB ++ Seq(fakeInstallationHostA)).size | ||
|
||
await { | ||
forgeInstallationRepo.deleteByClientKey( | ||
fakeInstallationsHostB.head.clientKey) | ||
} mustBe fakeInstallationsHostB.size | ||
|
||
await { | ||
forgeInstallationRepo.all() | ||
} mustBe Seq(fakeInstallationHostA) | ||
} | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
} |
15 changes: 15 additions & 0 deletions
15
src/it/scala/io/toolsplus/atlassian/connect/play/slick/SlickRepositoryIt.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package io.toolsplus.atlassian.connect.play.slick | ||
|
||
import org.scalatest.Suites | ||
|
||
/** | ||
* Combines the repository tests into a single suite to ensure individual | ||
* repository test suites run sequentially. | ||
* | ||
* If we attempt to run multiple repository suites in parallel, the evolutions | ||
* script will fail. An alternative solution might be to use different db | ||
* configurations for each repository test suite to isolate them. | ||
*/ | ||
class SlickRepositoryIt | ||
extends Suites(new SlickAtlassianHostRepositoryIt, | ||
new SlickForgeInstallationRepositoryIt) |
Oops, something went wrong.