Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid rate limit exceeded error #32

Merged
merged 5 commits into from
Jan 25, 2021
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ app.properties
.bloop/
.ammonite/
metals.sbt

data.db
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ Projects with custom fields are supported only for Redmine 2.4 and later.
| Custom field | 2.4 |
| Priority | 2.2 |

### Others

- If the space of Backlog is a free plan, it cannot be migrated due to API rate limiting.
- This tool cannot be used in parallel as it can exceed the API rate limit when run in parallel.

## Re-importing

When the project key in Backlog and Redmine matches, they will be considered as the same project and data will be imported as follows.
Expand Down Expand Up @@ -547,6 +552,11 @@ Backlogで **プレミアムプラン以上** のプランを契約している
|カスタムフィールド|2.4|
|優先度|2.2|

### その他

- Backlog のスペースがフリープランの場合はAPIのレート制限により移行できません。
- 本移行ツールの並列実行は、APIのレート制限を超える可能性があるため動作を保証できません。

## 再インポートの仕様

Backlog側にRedmineに対応するプロジェクトキーがある場合同一プロジェクトとみなし、以下の仕様でインポートされます。
Expand Down
2 changes: 1 addition & 1 deletion common
Submodule common updated 43 files
+3 −0 .gitignore
+2 −2 build.sbt
+0 −3 core/src/main/scala/com/nulabinc/backlog/migration/common/conf/BacklogPaths.scala
+0 −8 core/src/main/scala/com/nulabinc/backlog/migration/common/convert/BacklogUnmarshaller.scala
+3 −15 core/src/main/scala/com/nulabinc/backlog/migration/common/convert/writes/CustomFieldSettingNameWrites.scala
+13 −7 core/src/main/scala/com/nulabinc/backlog/migration/common/domain/BacklogStatus.scala
+2 −1 core/src/main/scala/com/nulabinc/backlog/migration/common/domain/Id.scala
+2 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/dsl/ConsoleDSL.scala
+2 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/dsl/StorageDSL.scala
+14 −8 core/src/main/scala/com/nulabinc/backlog/migration/common/dsl/StoreDSL.scala
+16 −6 core/src/main/scala/com/nulabinc/backlog/migration/common/formatters/BacklogJsonProtocol.scala
+5 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/interpreters/JansiConsoleDSL.scala
+8 −2 core/src/main/scala/com/nulabinc/backlog/migration/common/interpreters/LocalStorageDSL.scala
+65 −39 core/src/main/scala/com/nulabinc/backlog/migration/common/interpreters/SQLiteStoreDSL.scala
+56 −7 core/src/main/scala/com/nulabinc/backlog/migration/common/messages/ConsoleMessages.scala
+0 −12 core/src/main/scala/com/nulabinc/backlog/migration/common/persistence/sqlite/ops/AllTableOps.scala
+0 −31 core/src/main/scala/com/nulabinc/backlog/migration/common/persistence/sqlite/ops/BaseTableOps.scala
+0 −13 core/src/main/scala/com/nulabinc/backlog/migration/common/persistence/sqlite/ops/StatusTableOps.scala
+7 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/persistence/store/ReadQuery.scala
+7 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/persistence/store/WriteQuery.scala
+81 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/persistence/store/sqlite/ops/BacklogStatusOps.scala
+7 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/persistence/store/sqlite/ops/BaseTableOps.scala
+112 −0 .../main/scala/com/nulabinc/backlog/migration/common/persistence/store/sqlite/ops/ExportedStatusTableOps.scala
+11 −13 core/src/main/scala/com/nulabinc/backlog/migration/common/service/CommentServiceImpl.scala
+3 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/service/CustomFieldSettingServiceImpl.scala
+3 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/service/GroupServiceImpl.scala
+3 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/service/IssueCategoryServiceImpl.scala
+3 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/service/IssueTypeServiceImpl.scala
+5 −1 core/src/main/scala/com/nulabinc/backlog/migration/common/service/ProjectUserServiceImpl.scala
+1 −1 core/src/main/scala/com/nulabinc/backlog/migration/common/service/PropertyResolver.scala
+4 −2 core/src/main/scala/com/nulabinc/backlog/migration/common/service/PropertyResolverImpl.scala
+8 −3 core/src/main/scala/com/nulabinc/backlog/migration/common/service/StatusService.scala
+6 −5 core/src/main/scala/com/nulabinc/backlog/migration/common/service/StatusServiceImpl.scala
+3 −0 core/src/main/scala/com/nulabinc/backlog/migration/common/service/VersionServiceImpl.scala
+2 −2 core/src/test/scala/com/nulabinc/backlog/migration/SimpleFixture.scala
+5 −4 core/src/test/scala/com/nulabinc/backlog/migration/TestModule.scala
+80 −0 core/src/test/scala/com/nulabinc/backlog/migration/common/interpreters/SQLiteStoreDSL.scala
+0 −1 core/src/test/scala/com/nulabinc/backlog/migration/service/CommentServiceImplSpec.scala
+13 −9 importer/src/main/scala/com/nulabinc/backlog/migration/importer/core/Boot.scala
+17 −0 importer/src/main/scala/com/nulabinc/backlog/migration/importer/core/RetryUtil.scala
+26 −16 importer/src/main/scala/com/nulabinc/backlog/migration/importer/service/IssuesImporter.scala
+116 −96 importer/src/main/scala/com/nulabinc/backlog/migration/importer/service/ProjectImporter.scala
+1 −1 project/build.properties
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version = 1.4.4
sbt.version = 1.4.6
2 changes: 1 addition & 1 deletion src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ application {
version = "0.16.0-SNAPSHOT"
title = ${application.name} ${application.version} (c) nulab.inc
product = "redmine"
backlog4jVersion = "2.3.0"
backlog4jVersion = "2.4.0"
export-limit-at-once = 100
language=default
akka.mailbox-pool = 100
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/com/nulabinc/backlog/r2b/AppError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ sealed trait AppError

case class ValidationError(errors: Seq[String]) extends AppError
case class MappingError(inner: MappingFileError) extends AppError
case class UnknownError(e: Throwable) extends AppError
case object OperationCanceled extends AppError
117 changes: 68 additions & 49 deletions src/main/scala/com/nulabinc/backlog/r2b/Main.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.nulabinc.backlog.r2b

import java.nio.file.Paths
import java.util.Locale

import akka.actor.ActorSystem
Expand All @@ -8,16 +9,16 @@ import com.nulabinc.backlog.migration.common.conf.{
BacklogConfiguration,
ExcludeOption
}
import com.nulabinc.backlog.migration.common.dsl.{ConsoleDSL, StorageDSL}
import com.nulabinc.backlog.migration.common.errors.{MappingFileNotFound, MappingValidationError}
import com.nulabinc.backlog.migration.common.interpreters.{
AkkaHttpDSL,
JansiConsoleDSL,
LocalStorageDSL
LocalStorageDSL,
SQLiteStoreDSL
}
import com.nulabinc.backlog.migration.common.messages.ConsoleMessages
import com.nulabinc.backlog.migration.common.services.GitHubReleaseCheckService
import com.nulabinc.backlog.migration.common.utils.{ConsoleOut, Logging}
import com.nulabinc.backlog.migration.common.utils.Logging
import com.nulabinc.backlog.r2b.cli.R2BCli
import com.nulabinc.backlog.r2b.conf._
import com.nulabinc.backlog.r2b.messages.RedmineMessages
Expand All @@ -34,22 +35,29 @@ import scala.concurrent.duration.Duration

object R2B extends BacklogConfiguration with Logging {

private implicit val system: ActorSystem = ActorSystem("main")
private implicit val exc: Scheduler = monix.execution.Scheduler.Implicits.global
private implicit val storageDSL: StorageDSL[Task] = LocalStorageDSL()
private implicit val consoleDSL: ConsoleDSL[Task] = JansiConsoleDSL()
private implicit val httpDSL: AkkaHttpDSL = new AkkaHttpDSL()
private val dbPath = Paths.get("./backlog/data.db")

private implicit val system: ActorSystem = ActorSystem("main")
private implicit val exc: Scheduler = monix.execution.Scheduler.Implicits.global
private implicit val storageDSL = LocalStorageDSL()
private implicit val consoleDSL = JansiConsoleDSL()
private implicit val storeDSL = SQLiteStoreDSL(dbPath)
private implicit val httpDSL: AkkaHttpDSL = new AkkaHttpDSL()

def main(args: Array[String]): Unit = {
ConsoleOut.println(s"""|${applicationName}
consoleDSL
.println(s"""|${applicationName}
|--------------------------------------------------""".stripMargin)
.runSyncUnsafe()
AnsiConsole.systemInstall()
setLang()
DisableSSLCertificateCheckUtil.disableChecks()
if (!ClassVersion.isValid()) {
ConsoleOut.error(
Messages("cli.require_java8", System.getProperty("java.specification.version"))
)
consoleDSL
.errorln(
Messages("cli.require_java8", System.getProperty("java.specification.version"))
)
.runSyncUnsafe()
AnsiConsole.systemUninstall()
System.exit(1)
}
Expand All @@ -63,23 +71,27 @@ object R2B extends BacklogConfiguration with Logging {
case Right(_) =>
Task(Right(()))
case Left(error: ValidationError) =>
ConsoleDSL[Task].errorln(RedmineMessages.validationError(error.errors))
consoleDSL.errorln(RedmineMessages.validationError(error.errors))
case Left(error: MappingError) =>
error.inner match {
case _: MappingFileNotFound =>
ConsoleDSL[Task].errorln(ConsoleMessages.Mappings.needsSetup)
consoleDSL.errorln(ConsoleMessages.Mappings.needsSetup)
case e: MappingValidationError[_] =>
ConsoleDSL[Task].errorln(ConsoleMessages.Mappings.validationError(e))
consoleDSL.errorln(ConsoleMessages.Mappings.validationError(e))
case e =>
ConsoleDSL[Task].errorln(e.toString)
consoleDSL.errorln(e.toString)
}
case Left(UnknownError(error)) =>
consoleDSL.errorln(error.getStackTrace.mkString("\n"))
case Left(OperationCanceled) =>
ConsoleDSL[Task].errorln(RedmineMessages.cancel)
consoleDSL.errorln(RedmineMessages.cancel)
}
} yield ()

val f = asyncResult
.flatMap(_ => Task.deferFuture(system.terminate()))
.flatMap { _ =>
Task.deferFuture(system.terminate()).map(_ => ())
}
.onErrorRecover { ex =>
logger.error(ex.getMessage, ex)
exit(1, ex)
Expand All @@ -102,25 +114,30 @@ object R2B extends BacklogConfiguration with Logging {
System.exit(exitCode)

private def exit(exitCode: Int, error: Throwable): Unit = {
ConsoleOut.error(
"ERROR: " + error.getMessage + "\n" + error.printStackTrace()
)
consoleDSL
.errorln(
"ERROR: " + error.getMessage + "\n" + error.printStackTrace()
)
.runSyncUnsafe()
exit(exitCode)
}

private def execute(cli: CommandLineInterface): Task[Either[AppError, Unit]] =
cli.subcommand match {
case Some(cli.execute) if cli.execute.importOnly() =>
R2BCli.doImport(getConfiguration(cli))
case Some(cli.execute) =>
R2BCli.migrate(getConfiguration(cli))
case Some(cli.init) =>
R2BCli.init(getConfiguration(cli))
case _ =>
R2BCli.help()
}
for {
conf <- getConfiguration(cli)
result <- cli.subcommand match {
case Some(cli.execute) if cli.execute.importOnly() =>
R2BCli.doImport(conf)
case Some(cli.execute) =>
R2BCli.migrate(conf)
case Some(cli.init) =>
R2BCli.init(conf)
case _ =>
R2BCli.help()
}
} yield result

private[this] def getConfiguration(cli: CommandLineInterface) = {
private def getConfiguration(cli: CommandLineInterface): Task[AppConfiguration] = {
val keys = cli.execute.projectKey().split(":")
val redmine = keys(0)
val backlog = if (keys.length == 2) keys(1) else keys(0).toUpperCase.replaceAll("-", "_")
Expand All @@ -135,7 +152,8 @@ object R2B extends BacklogConfiguration with Logging {
}
.getOrElse(ExcludeOption.default)

ConsoleOut.println(s"""--------------------------------------------------
consoleDSL
.println(s"""--------------------------------------------------
|${Messages("common.src")} ${Messages("common.url")}[${cli.execute.redmineUrl()}]
|${Messages("common.src")} ${Messages("common.access_key")}[${cli.execute.redmineKey()}]
|${Messages("common.src")} ${Messages("common.project_key")}[${redmine}]
Expand All @@ -151,22 +169,23 @@ object R2B extends BacklogConfiguration with Logging {
|https.proxyPassword[${Option(System.getProperty("https.proxyPassword")).getOrElse("")}]
|--------------------------------------------------
|""".stripMargin)

AppConfiguration(
redmineConfig = RedmineApiConfiguration(
url = cli.execute.redmineUrl(),
key = cli.execute.redmineKey(),
projectKey = redmine
),
backlogConfig = BacklogApiConfiguration(
url = cli.execute.backlogUrl(),
key = cli.execute.backlogKey(),
projectKey = backlog
),
exclude = exclude,
importOnly = cli.execute.importOnly(),
retryCount = retryCount
)
.map { _ =>
AppConfiguration(
redmineConfig = RedmineApiConfiguration(
url = cli.execute.redmineUrl(),
key = cli.execute.redmineKey(),
projectKey = redmine
),
backlogConfig = BacklogApiConfiguration(
url = cli.execute.backlogUrl(),
key = cli.execute.backlogKey(),
projectKey = backlog
),
exclude = exclude,
importOnly = cli.execute.importOnly(),
retryCount = retryCount
)
}
}

private[this] def setLang(): Unit =
Expand Down
Loading