diff --git a/docs/ApiToken.md b/docs/ApiToken.md index c25a8e84..c59256ba 100644 --- a/docs/ApiToken.md +++ b/docs/ApiToken.md @@ -9,6 +9,7 @@ Classic scopes: - `read:jira-work` - `write:jira-work` - `read:jira-user` +- `manage:jira-project` ## Cloud ID @@ -103,4 +104,9 @@ Scopes (classic): `read:jira-work` ### [POST /issue/{issueIdOrKey}/transitions](https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-issueidorkey-transitions-post) -Scopes (classic): `write:jira-work` \ No newline at end of file +Scopes (classic): `write:jira-work` + + +### [PUT /version/{id}](https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-project-versions/#api-rest-api-2-version-id-put) + +Scopes (classic): `manage:jira-project` \ No newline at end of file diff --git a/src/main/kotlin/io/github/mojira/arisa/apiclient/JiraClient.kt b/src/main/kotlin/io/github/mojira/arisa/apiclient/JiraClient.kt index d1079333..d91e84e1 100644 --- a/src/main/kotlin/io/github/mojira/arisa/apiclient/JiraClient.kt +++ b/src/main/kotlin/io/github/mojira/arisa/apiclient/JiraClient.kt @@ -16,6 +16,7 @@ import io.github.mojira.arisa.apiclient.models.Project import io.github.mojira.arisa.apiclient.models.SearchResults import io.github.mojira.arisa.apiclient.models.Transitions import io.github.mojira.arisa.apiclient.models.User +import io.github.mojira.arisa.apiclient.models.Version import io.github.mojira.arisa.apiclient.models.Visibility import io.github.mojira.arisa.apiclient.requestModels.AddCommentBody import io.github.mojira.arisa.apiclient.requestModels.CreateIssueLinkBody @@ -24,6 +25,7 @@ import io.github.mojira.arisa.apiclient.requestModels.JiraSearchRequest import io.github.mojira.arisa.apiclient.requestModels.TransitionIssueBody import io.github.mojira.arisa.apiclient.requestModels.UpdateCommentBody import io.github.mojira.arisa.apiclient.requestModels.UpdateCommentQueryParams +import io.github.mojira.arisa.apiclient.requestModels.UpdateVersionBody import io.github.mojira.arisa.log import kotlinx.serialization.json.Json import okhttp3.HttpUrl.Companion.toHttpUrl @@ -149,6 +151,12 @@ interface JiraApi { @Path("issueIdOrKey") issueIdOrKey: String, @Body body: TransitionIssueBody ): Call + + @PUT("version/{id}") + fun updateVersion( + @Path("id") versionId: String, + @Body body: UpdateVersionBody + ): Call } /** @@ -335,4 +343,8 @@ class JiraClient( fun performTransition(issueIdOrKey: String, body: TransitionIssueBody) { jiraApi.performTransition(issueIdOrKey, body).executeOrThrow() } + + fun updateVersion(versionId: String, body: UpdateVersionBody): Version { + return jiraApi.updateVersion(versionId, body).executeOrThrow() + } } diff --git a/src/main/kotlin/io/github/mojira/arisa/apiclient/models/IssueFields.kt b/src/main/kotlin/io/github/mojira/arisa/apiclient/models/IssueFields.kt index a4fd9d8f..5922c3c8 100644 --- a/src/main/kotlin/io/github/mojira/arisa/apiclient/models/IssueFields.kt +++ b/src/main/kotlin/io/github/mojira/arisa/apiclient/models/IssueFields.kt @@ -32,7 +32,7 @@ data class IssueFields( val statuscategorychangedate: String? = null, val subtasks: List? = null, val summary: String? = null, - val versions: List = emptyList(), + val versions: List = emptyList(), val timeoriginalestimate: JsonElement? = null, val timespent: Long? = null, @Serializable(with = OffsetDateTimeSerializer::class) diff --git a/src/main/kotlin/io/github/mojira/arisa/domain/IssueUpdateContext.kt b/src/main/kotlin/io/github/mojira/arisa/domain/IssueUpdateContext.kt index 56d4dadd..535fdbce 100644 --- a/src/main/kotlin/io/github/mojira/arisa/domain/IssueUpdateContext.kt +++ b/src/main/kotlin/io/github/mojira/arisa/domain/IssueUpdateContext.kt @@ -19,5 +19,9 @@ data class IssueUpdateContext( var transitionName: String? = null, val otherOperations: MutableList<() -> Either> = emptyList<() -> Either>().toMutableList(), + val preOperations: MutableList<() -> Either> = + emptyList<() -> Either>().toMutableList(), + val postOperations: MutableList<() -> Either> = + emptyList<() -> Either>().toMutableList(), var triggeredBy: String? = null ) diff --git a/src/main/kotlin/io/github/mojira/arisa/domain/cloud/CloudIssue.kt b/src/main/kotlin/io/github/mojira/arisa/domain/cloud/CloudIssue.kt index a7291763..6f968179 100644 --- a/src/main/kotlin/io/github/mojira/arisa/domain/cloud/CloudIssue.kt +++ b/src/main/kotlin/io/github/mojira/arisa/domain/cloud/CloudIssue.kt @@ -6,6 +6,7 @@ import io.github.mojira.arisa.domain.Comment import io.github.mojira.arisa.domain.CommentOptions import io.github.mojira.arisa.domain.Project import io.github.mojira.arisa.domain.User +import io.github.mojira.arisa.domain.Version import java.io.File import java.time.Instant @@ -30,7 +31,7 @@ data class CloudIssue( // val platform: String?, // val dungeonsPlatform: String?, // val legendsPlatform: String?, -// val affectedVersions: List, + val affectedVersions: List, // val fixVersions: List, val attachments: List, val comments: List, @@ -50,7 +51,9 @@ data class CloudIssue( // val updateLegendsPlatform: (String) -> Unit, // val updateLinked: (Double) -> Unit, val setPrivate: () -> Unit, -// val addAffectedVersionById: (id: String) -> Unit, + val addAffectedVersionById: (id: String) -> Unit, + val unarchiveVersion: (id: String) -> Unit, + val archiveVersion: (id: String) -> Unit, // val addAffectedVersion: (version: Version) -> Unit, // val removeAffectedVersion: (version: Version) -> Unit, // val createLink: (type: String, key: String, outwards: Boolean) -> Unit, diff --git a/src/main/kotlin/io/github/mojira/arisa/infrastructure/jira/Functions.kt b/src/main/kotlin/io/github/mojira/arisa/infrastructure/jira/Functions.kt index c767f6b8..4d773263 100644 --- a/src/main/kotlin/io/github/mojira/arisa/infrastructure/jira/Functions.kt +++ b/src/main/kotlin/io/github/mojira/arisa/infrastructure/jira/Functions.kt @@ -4,8 +4,20 @@ package io.github.mojira.arisa.infrastructure.jira import arrow.core.Either import arrow.syntax.function.partially1 +import io.github.mojira.arisa.apiclient.JiraClient +import io.github.mojira.arisa.apiclient.builders.FluentObjectBuilder +import io.github.mojira.arisa.apiclient.builders.string +import io.github.mojira.arisa.apiclient.exceptions.ClientErrorException +import io.github.mojira.arisa.apiclient.exceptions.JiraClientException +import io.github.mojira.arisa.apiclient.models.IssueTransition +import io.github.mojira.arisa.apiclient.models.Visibility +import io.github.mojira.arisa.apiclient.requestModels.EditIssueBody +import io.github.mojira.arisa.apiclient.requestModels.TransitionIssueBody +import io.github.mojira.arisa.apiclient.requestModels.UpdateCommentBody +import io.github.mojira.arisa.apiclient.requestModels.UpdateVersionBody import io.github.mojira.arisa.domain.IssueUpdateContext import io.github.mojira.arisa.infrastructure.CommentCache +import io.github.mojira.arisa.jiraClient import io.github.mojira.arisa.log import io.github.mojira.arisa.modules.FailedModuleResponse import io.github.mojira.arisa.modules.ModuleResponse @@ -13,32 +25,21 @@ import io.github.mojira.arisa.modules.tryRunAll import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import io.github.mojira.arisa.apiclient.JiraClient as MojiraClient -import io.github.mojira.arisa.apiclient.models.AttachmentBean as MojiraAttachment -import io.github.mojira.arisa.apiclient.models.Comment as MojiraComment +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import net.rcarz.jiraclient.Field -import io.github.mojira.arisa.apiclient.models.IssueLink as MojiraIssueLink import net.rcarz.jiraclient.JiraException import net.rcarz.jiraclient.RestException -import io.github.mojira.arisa.apiclient.exceptions.ClientErrorException -import io.github.mojira.arisa.apiclient.models.Version as MojiraVersion import org.apache.http.HttpStatus import java.io.File import java.io.InputStream import java.time.Instant import java.time.temporal.ChronoField -import io.github.mojira.arisa.apiclient.JiraClient -import io.github.mojira.arisa.apiclient.builders.FluentObjectBuilder -import io.github.mojira.arisa.apiclient.builders.string -import io.github.mojira.arisa.apiclient.exceptions.JiraClientException -import io.github.mojira.arisa.apiclient.models.IssueTransition -import io.github.mojira.arisa.apiclient.models.Visibility -import io.github.mojira.arisa.apiclient.requestModels.EditIssueBody -import io.github.mojira.arisa.apiclient.requestModels.TransitionIssueBody -import io.github.mojira.arisa.apiclient.requestModels.UpdateCommentBody -import io.github.mojira.arisa.jiraClient -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.put +import io.github.mojira.arisa.apiclient.JiraClient as MojiraClient +import io.github.mojira.arisa.apiclient.models.AttachmentBean as MojiraAttachment +import io.github.mojira.arisa.apiclient.models.Comment as MojiraComment +import io.github.mojira.arisa.apiclient.models.IssueLink as MojiraIssueLink +import io.github.mojira.arisa.apiclient.models.Version as MojiraVersion /** * Get a list of tickets matching a JQL query. @@ -87,7 +88,11 @@ fun updateCHK(context: Lazy, chkField: String) { ) } -fun updateConfirmation(context: Lazy, confirmationField: String, value: String) { +fun updateConfirmation( + context: Lazy, + confirmationField: String, + value: String +) { context.value.hasUpdates = true context.value.update.field(confirmationField) { subField("value", value) @@ -101,14 +106,22 @@ fun updatePriority(context: Lazy, priorityField: String, val } } -fun updateDungeonsPlatform(context: Lazy, dungeonsPlatformField: String, value: String) { +fun updateDungeonsPlatform( + context: Lazy, + dungeonsPlatformField: String, + value: String +) { context.value.hasEdits = true context.value.edit.field(dungeonsPlatformField) { subField("value", value) } } -fun updateLegendsPlatform(context: Lazy, legendsPlatformField: String, value: String) { +fun updateLegendsPlatform( + context: Lazy, + legendsPlatformField: String, + value: String +) { context.value.hasEdits = true context.value.edit.field(legendsPlatformField) { subField("value", value) @@ -154,9 +167,7 @@ fun removeAffectedVersion(context: Lazy, version: MojiraVers fun addAffectedVersionById(context: Lazy, id: String) { context.value.hasEdits = true - context.value.edit.field("versions") { - subField("id", id) - } + context.value.edit.add("versions", buildJsonObject { put("id", id) }) } fun removeAffectedVersionById(context: Lazy, id: String) { @@ -175,7 +186,18 @@ fun updateDescription(context: Lazy, description: String) { } fun applyIssueChanges(context: IssueUpdateContext): Either { - val functions = context.otherOperations.toMutableList() + val functions = mutableListOf<() -> Either>() + + // Build execution list in reverse order for transitions/updates/edits + // since they're added at index 0 + + // Step 6: Add postOperations last (will run after everything) + functions.addAll(context.postOperations) + + // Step 5: Add otherOperations (will run after edits) + functions.addAll(0, context.otherOperations) + + // Step 4: Add edits (will run after updates) if (context.hasEdits) { functions.add( 0, @@ -184,6 +206,8 @@ fun applyIssueChanges(context: IssueUpdateContext): Either log.error(checkResult.a.message) is Either.Right -> { val issueId = context.value.jiraIssue.id - val visibility = Visibility(value = restrictionLevel, type = Visibility.Type.Group.value) + val visibility = + Visibility(value = restrictionLevel, type = Visibility.Type.Group.value) context.value.jiraClient.addRestrictedComment(issueId, comment, visibility) } } @@ -421,6 +458,58 @@ fun deleteLink(context: Lazy, link: MojiraIssueLink) { } } +fun unarchiveVersion(context: Lazy, versionId: String) { + context.value.preOperations.add { + runBlocking { + Either.catch { + withContext(Dispatchers.IO) { + try { + context.value.jiraClient.updateVersion( + versionId, + UpdateVersionBody(archived = false) + ) + } catch (e: ClientErrorException) { + if (e.code == HttpStatus.SC_NOT_FOUND || + e.code >= HttpStatus.SC_INTERNAL_SERVER_ERROR + ) { + log.warn("Failed to unarchive version $versionId") + } else { + throw e + } + } + } + Unit + } + } + } +} + +fun archiveVersion(context: Lazy, versionId: String) { + context.value.postOperations.add { + runBlocking { + Either.catch { + withContext(Dispatchers.IO) { + try { + context.value.jiraClient.updateVersion( + versionId, + UpdateVersionBody(archived = true) + ) + } catch (e: ClientErrorException) { + if (e.code == HttpStatus.SC_NOT_FOUND || + e.code >= HttpStatus.SC_INTERNAL_SERVER_ERROR + ) { + log.warn("Failed to archive version $versionId") + } else { + throw e + } + } + } + Unit + } + } + } +} + fun updateCommentBody(context: Lazy, comment: MojiraComment, body: String) { context.value.otherOperations.add { runBlocking { diff --git a/src/main/kotlin/io/github/mojira/arisa/infrastructure/jira/Mappers.kt b/src/main/kotlin/io/github/mojira/arisa/infrastructure/jira/Mappers.kt index 5e4859cb..4c8b9c22 100644 --- a/src/main/kotlin/io/github/mojira/arisa/infrastructure/jira/Mappers.kt +++ b/src/main/kotlin/io/github/mojira/arisa/infrastructure/jira/Mappers.kt @@ -39,23 +39,25 @@ import io.github.mojira.arisa.apiclient.models.User as MojiraUser import io.github.mojira.arisa.apiclient.models.UserDetails as MojiraUserDetails import io.github.mojira.arisa.apiclient.models.Version as MojiraVersion -fun MojiraAttachment.toDomain(jiraClient: MojiraClient, issue: MojiraIssue, config: Config) = Attachment( - id = id, - name = filename, - created = getCreationDate(issue, id, issue.fields.created?.toInstant() ?: Instant.now()), - mimeType = mimeType, - remove = ::deleteAttachment.partially1(issue.getUpdateContext(jiraClient)).partially1(this), - openContentStream = { openAttachmentStream(jiraClient, this) }, - // Cache attachment content once it has been downloaded - getContent = lazy { jiraClient.downloadAttachment(id) }::value, - uploader = author?.toDomain(jiraClient, config) -) +fun MojiraAttachment.toDomain(jiraClient: MojiraClient, issue: MojiraIssue, config: Config) = + Attachment( + id = id, + name = filename, + created = getCreationDate(issue, id, issue.fields.created?.toInstant() ?: Instant.now()), + mimeType = mimeType, + remove = ::deleteAttachment.partially1(issue.getUpdateContext(jiraClient)).partially1(this), + openContentStream = { openAttachmentStream(jiraClient, this) }, + // Cache attachment content once it has been downloaded + getContent = lazy { jiraClient.downloadAttachment(id) }::value, + uploader = author?.toDomain(jiraClient, config) + ) -fun getCreationDate(issue: MojiraIssue, id: String, default: Instant) = (issue.changelog.histories as List) - .filter { changeLogEntry -> changeLogEntry.items.any { it.field == "Attachment" && it.to == id } } - .maxByOrNull { it.created } - ?.created - ?.toInstant() ?: default +fun getCreationDate(issue: MojiraIssue, id: String, default: Instant) = + (issue.changelog.histories as List) + .filter { changeLogEntry -> changeLogEntry.items.any { it.field == "Attachment" && it.to == id } } + .maxByOrNull { it.created } + ?.created + ?.toInstant() ?: default fun MojiraProject.getSecurityLevelId(config: Config) = config[Arisa.PrivateSecurityLevel.default] @@ -111,7 +113,7 @@ fun MojiraIssue.toDomain( // getPlatform(config), // getDungeonsPlatform(config), // getLegendsPlatform(config), -// mapVersions(), + mapVersions(), // mapFixVersions(), attachments = mapAttachments(jiraClient, config), comments = mapComments(jiraClient, config), @@ -130,8 +132,11 @@ fun MojiraIssue.toDomain( // ::updateDungeonsPlatform.partially1(context).partially1(config[Arisa.CustomFields.dungeonsPlatformField]), // ::updateLegendsPlatform.partially1(context).partially1(config[Arisa.CustomFields.legendsPlatformField]), // ::updateLinked.partially1(context).partially1(config[Arisa.CustomFields.linked]), - setPrivate = ::updateSecurity.partially1(context).partially1(project.getSecurityLevelId(config)), -// ::addAffectedVersionById.partially1(context), + setPrivate = ::updateSecurity.partially1(context) + .partially1(project.getSecurityLevelId(config)), + ::addAffectedVersionById.partially1(context), + ::unarchiveVersion.partially1(context), + ::archiveVersion.partially1(context), // { version -> addAffectedVersionById(context, version.id) }, // { version -> removeAffectedVersionById(context, version.id) }, // ::createLink.partially1(context).partially1(::getOtherUpdateContext.partially1(jiraClient)), @@ -226,12 +231,22 @@ fun MojiraComment.toDomain( author = author?.toDomain(jiraClient, config), updateAuthor = updateAuthor?.toDomain(jiraClient, config), getAuthorGroups = { getGroups(jiraClient, author!!.accountId).fold({ null }, { it }) }, - getUpdateAuthorGroups = { if (updateAuthor == null) emptyList() else getGroups(jiraClient, updateAuthor.accountId).fold({ null }, { it }) }, + getUpdateAuthorGroups = { + if (updateAuthor == null) { + emptyList() + } else { + getGroups( + jiraClient, + updateAuthor.accountId + ).fold({ null }, { it }) + } + }, created = created!!.toInstant(), updated = updated!!.toInstant(), visibilityType = visibility?.type, visibilityValue = visibility?.value, - restrict = ::restrictCommentToGroup.partially1(context).partially1(this).partially1("staff"), + restrict = ::restrictCommentToGroup.partially1(context).partially1(this) + .partially1("staff"), update = ::updateCommentBody.partially1(context).partially1(this), remove = ::deleteComment.partially1(issue.getUpdateContext(jiraClient)).partially1(this) ) @@ -316,14 +331,15 @@ private fun MojiraIssue.mapLinks( } private fun MojiraIssue.mapComments(jiraClient: MojiraClient, config: Config) = - fields.comment?.comments?.map { it.toDomain(jiraClient, this, config) }?.sortedBy { it.created } ?: emptyList() + fields.comment?.comments?.map { it.toDomain(jiraClient, this, config) }?.sortedBy { it.created } + ?: emptyList() private fun MojiraIssue.mapAttachments(jiraClient: MojiraClient, config: Config) = fields.attachment.map { it.toDomain(jiraClient, this, config) }.sortedBy { it.created } -// private fun MojiraIssue.mapVersions() = -// fields.versions.map { it.toDomain() } -// +private fun MojiraIssue.mapVersions() = + fields.versions.map { it.toDomain() } + // private fun MojiraIssue.mapFixVersions() = // fixVersions.map { it.toDomain() } diff --git a/src/main/kotlin/io/github/mojira/arisa/modules/CommandModule.kt b/src/main/kotlin/io/github/mojira/arisa/modules/CommandModule.kt index a2d74dc0..3cf166e6 100644 --- a/src/main/kotlin/io/github/mojira/arisa/modules/CommandModule.kt +++ b/src/main/kotlin/io/github/mojira/arisa/modules/CommandModule.kt @@ -7,7 +7,7 @@ import arrow.syntax.function.partially1 import arrow.syntax.function.partially2 import com.mojang.brigadier.CommandDispatcher import io.github.mojira.arisa.domain.Comment -import io.github.mojira.arisa.domain.Issue +import io.github.mojira.arisa.domain.cloud.CloudIssue import io.github.mojira.arisa.infrastructure.AttachmentUtils import io.github.mojira.arisa.modules.commands.CommandSource import io.github.mojira.arisa.modules.commands.getCommandDispatcher @@ -29,7 +29,7 @@ class CommandModule( attachmentUtils: AttachmentUtils, private val getDispatcher: (String) -> CommandDispatcher = ::getCommandDispatcher.partially2(attachmentUtils) -) : Module { +) : CloudModule { /** * This is the command dispatcher. * It's not initialized initially because it relies on `jiraClient` in `ArisaMain`, which is lateinit too. @@ -37,37 +37,38 @@ class CommandModule( */ private lateinit var commandDispatcher: CommandDispatcher - override fun invoke(issue: Issue, lastRun: Instant): Either = Either.fx { - if (!::commandDispatcher.isInitialized) { - commandDispatcher = getDispatcher(prefix) - } + override fun invoke(issue: CloudIssue, lastRun: Instant): Either = + Either.fx { + if (!::commandDispatcher.isInitialized) { + commandDispatcher = getDispatcher(prefix) + } - with(issue) { - val staffComments = comments - .filter(::isUpdatedAfterLastRun.partially1(lastRun)) - .filter(::isStaffRestricted) - .filter(::userIsVolunteer) - assertNotEmpty(staffComments).bind() + with(issue) { + val staffComments = comments + .filter(::isUpdatedAfterLastRun.partially1(lastRun)) + .filter(::isStaffRestricted) + .filter(::userIsVolunteer) + assertNotEmpty(staffComments).bind() - val commands = staffComments - .map { comment -> - comment to extractCommands(issue, comment) - } - .filter { it.second.isNotEmpty() } - .onEach { invocation -> - val commandResults = invocation.second.associate { command -> - val result = executeCommand(command) - logCommandExecutionResult(issue, invocation.first, command, result) - command.source.line to result + val commands = staffComments + .map { comment -> + comment to extractCommands(issue, comment) } + .filter { it.second.isNotEmpty() } + .onEach { invocation -> + val commandResults = invocation.second.associate { command -> + val result = executeCommand(command) + logCommandExecutionResult(issue, invocation.first, command, result) + command.source.line to result + } + + editInvocationComment(invocation.first, commandResults) + } + assertNotEmpty(commands).bind() - editInvocationComment(invocation.first, commandResults) - } - assertNotEmpty(commands).bind() - - ModuleResponse.right().bind() + ModuleResponse.right().bind() + } } - } private fun executeCommand(command: Command): CommandResult { return runBlocking { @@ -77,12 +78,13 @@ class CommandModule( } } - private fun isUpdatedAfterLastRun(lastRun: Instant, comment: Comment) = comment.updated.isAfter(lastRun) + private fun isUpdatedAfterLastRun(lastRun: Instant, comment: Comment) = + comment.updated.isAfter(lastRun) /** * Extracts all commands from a comment. */ - private fun extractCommands(issue: Issue, comment: Comment) = + private fun extractCommands(issue: CloudIssue, comment: Comment) = comment.body?.lines().orEmpty() .mapIndexed { lineNr, line -> lineNr to line.trim() } .filter { (_, line) -> line.startsWith("${prefix}_") } @@ -91,7 +93,7 @@ class CommandModule( } private fun logCommandExecutionResult( - issue: Issue, + issue: CloudIssue, comment: Comment, command: Command, commandResult: CommandResult @@ -166,7 +168,8 @@ class CommandModule( return false } - return comment.getAuthorGroups()?.any { it == "helper" || it == "global-moderators" || it == "staff" } ?: false + return comment.getAuthorGroups() + ?.any { it == "helper" || it == "global-moderators" || it == "staff" } ?: false } private fun isStaffRestricted(comment: Comment) = diff --git a/src/main/kotlin/io/github/mojira/arisa/modules/commands/AddVersionCommand.kt b/src/main/kotlin/io/github/mojira/arisa/modules/commands/AddVersionCommand.kt index 8366874a..9a24b0c3 100644 --- a/src/main/kotlin/io/github/mojira/arisa/modules/commands/AddVersionCommand.kt +++ b/src/main/kotlin/io/github/mojira/arisa/modules/commands/AddVersionCommand.kt @@ -1,17 +1,27 @@ package io.github.mojira.arisa.modules.commands -import io.github.mojira.arisa.domain.Issue +import io.github.mojira.arisa.domain.cloud.CloudIssue class AddVersionCommand { - operator fun invoke(issue: Issue, version: String): Int { + operator fun invoke(issue: CloudIssue, version: String): Int { if (issue.affectedVersions.any { it.name == version }) { throw CommandExceptions.VERSION_ALREADY_AFFECTED.create(version) } - if (issue.project.versions.none { it.name == version }) { - throw CommandExceptions.NO_SUCH_VERSION.create(version) + val projectVersion = issue.project.versions.firstOrNull { it.name == version } + ?: throw CommandExceptions.NO_SUCH_VERSION.create(version) + + val id = projectVersion.id + + // If version is archived, we need to unarchive it, add it, then re-archive + if (projectVersion.archived) { + issue.unarchiveVersion(id) + issue.addAffectedVersionById(id) + issue.archiveVersion(id) + } else { + // Normal case: just add the version + issue.addAffectedVersionById(id) } - val id = issue.project.versions.first { it.name == version }.id - issue.addAffectedVersionById(id) + return 1 } } diff --git a/src/main/kotlin/io/github/mojira/arisa/modules/commands/CommandDispatcher.kt b/src/main/kotlin/io/github/mojira/arisa/modules/commands/CommandDispatcher.kt index dc68764b..4205bff4 100644 --- a/src/main/kotlin/io/github/mojira/arisa/modules/commands/CommandDispatcher.kt +++ b/src/main/kotlin/io/github/mojira/arisa/modules/commands/CommandDispatcher.kt @@ -1,41 +1,33 @@ package io.github.mojira.arisa.modules.commands -import arrow.syntax.function.partially1 import com.mojang.brigadier.CommandDispatcher -import com.mojang.brigadier.arguments.IntegerArgumentType.integer import com.mojang.brigadier.arguments.StringArgumentType.greedyString -import com.mojang.brigadier.arguments.StringArgumentType.string import com.mojang.brigadier.builder.LiteralArgumentBuilder.literal import com.mojang.brigadier.builder.RequiredArgumentBuilder.argument import com.mojang.brigadier.context.CommandContext import io.github.mojira.arisa.infrastructure.AttachmentUtils -import io.github.mojira.arisa.infrastructure.jira.getIssuesFromJql -import io.github.mojira.arisa.jiraClient import io.github.mojira.arisa.modules.commands.arguments.LinkList -import io.github.mojira.arisa.modules.commands.arguments.LinkListArgumentType import io.github.mojira.arisa.modules.commands.arguments.StringWithFlag -import io.github.mojira.arisa.modules.commands.arguments.enumArgumentType -import io.github.mojira.arisa.modules.commands.arguments.greedyStringWithFlag @Suppress("LongMethod") fun getCommandDispatcher( prefix: String, attachmentUtils: AttachmentUtils ): CommandDispatcher { - val addLinksCommand = AddLinksCommand() + // val addLinksCommand = AddLinksCommand() val addVersionCommand = AddVersionCommand() - val clearProjectCacheCommand = ClearProjectCacheCommand() - val deleteCommentsCommand = DeleteCommentsCommand() - val deleteLinksCommand = DeleteLinksCommand() - val deobfuscateCommand = DeobfuscateCommand(attachmentUtils) - val fixCapitalizationCommand = FixCapitalizationCommand() - val fixedCommand = FixedCommand() - val listUserActivityCommand = ListUserActivityCommand( - ::getIssuesFromJql.partially1(jiraClient) - ) - val makePrivateCommand = MakePrivateCommand() - val purgeAttachmentCommand = PurgeAttachmentCommand() - val reopenCommand = ReopenCommand() +// val clearProjectCacheCommand = ClearProjectCacheCommand() +// val deleteCommentsCommand = DeleteCommentsCommand() +// val deleteLinksCommand = DeleteLinksCommand() +// val deobfuscateCommand = DeobfuscateCommand(attachmentUtils) +// val fixCapitalizationCommand = FixCapitalizationCommand() +// val fixedCommand = FixedCommand() +// val listUserActivityCommand = ListUserActivityCommand( +// ::getIssuesFromJql.partially1(jiraClient) +// ) +// val makePrivateCommand = MakePrivateCommand() +// val purgeAttachmentCommand = PurgeAttachmentCommand() +// val reopenCommand = ReopenCommand() // val removeContentCommand = RemoveContentCommand( // ::getIssuesFromJql.partially1(jiraClient), // { @@ -46,21 +38,21 @@ fun getCommandDispatcher( // }, // { Thread(it).start() } // ) - val shadowbanCommand = ShadowbanCommand() +// val shadowbanCommand = ShadowbanCommand() return CommandDispatcher().apply { - val addLinksCommandNode = - literal("${prefix}_ADD_LINKS") - .requires(::sentByModerator) - .then( - argument("linkList", LinkListArgumentType()) - .executes { - addLinksCommand( - it.source.issue, - it.getLinkList("linkList") - ) - } - ) +// val addLinksCommandNode = +// literal("${prefix}_ADD_LINKS") +// .requires(::sentByModerator) +// .then( +// argument("linkList", LinkListArgumentType()) +// .executes { +// addLinksCommand( +// it.source.issue, +// it.getLinkList("linkList") +// ) +// } +// ) val addVersionCommandNode = literal("${prefix}_ADD_VERSION") @@ -74,240 +66,240 @@ fun getCommandDispatcher( } ) - val clearProjectCacheCommandNode = - literal("${prefix}_CLEAR_PROJECT_CACHE") - .executes { - clearProjectCacheCommand() - } - - val deleteCommentsCommandNodeChild = - argument("name", greedyString()) - .executes { - deleteCommentsCommand( - it.source.issue, - it.getString("name") - ) - } - val deleteCommentsCommandNode = - literal("${prefix}_DELETE_COMMENTS") - .requires(::sentByModerator) - .then(deleteCommentsCommandNodeChild) - val removeCommentsCommandNode = - literal("${prefix}_REMOVE_COMMENTS") - .requires(::sentByModerator) - .then(deleteCommentsCommandNodeChild) - - val deobfuscateCommandNode = run { - val attachmentIdArg = "attachmentId" - val minecraftVersionIdArg = "minecraftVersionId" - val crashReportTypeArg = "crashReportType" - literal("${prefix}_DEOBFUSCATE") - .then( - argument(attachmentIdArg, string()) - .executes { - deobfuscateCommand.invoke( - it.source.issue, - it.getString(attachmentIdArg) - ) - } - .then( - argument(minecraftVersionIdArg, string()) - .executes { - deobfuscateCommand.invoke( - it.source.issue, - it.getString(attachmentIdArg), - it.getString(minecraftVersionIdArg) - ) - } - .then( - argument( - crashReportTypeArg, - enumArgumentType() - ) - .executes { - deobfuscateCommand.invoke( - it.source.issue, - it.getString(attachmentIdArg), - it.getString(minecraftVersionIdArg), - it.getArgument(crashReportTypeArg, CrashReportType::class.java) - ) - } - ) - ) - ) - } - - val deleteLinksCommandNodeChild = - argument("linkList", LinkListArgumentType()) - .executes { - deleteLinksCommand( - it.source.issue, - it.getLinkList("linkList") - ) - } - val deleteLinksCommandNode = - literal("${prefix}_DELETE_LINKS") - .requires(::sentByModerator) - .then(deleteLinksCommandNodeChild) - val removeLinksCommandNode = - literal("${prefix}_REMOVE_LINKS") - .requires(::sentByModerator) - .then(deleteLinksCommandNodeChild) - - val fixCapitalizationCommandNode = - literal("${prefix}_FIX_CAPITALIZATION") - .executes { - fixCapitalizationCommand( - it.source.issue - ) - } - - val fixedCommandNode = - literal("${prefix}_FIXED") - .requires(::sentByModerator) - .then( - argument("version", greedyStringWithFlag("force")) - .executes { - val (version, force) = it.getStringWithFlag("version") - fixedCommand( - it.source.issue, - version, - force - ) - } - ) - - val listUserActivityCommandNode = - literal("${prefix}_LIST_USER_ACTIVITY") - .requires(::sentByModerator) - .then( - argument("username", greedyString()) - .executes { - listUserActivityCommand( - it.source.issue, - it.getString("username") - ) - } - ) - - val makePrivateCommandNode = - literal("${prefix}_MAKE_PRIVATE") - .executes { - makePrivateCommand( - it.source.issue - ) - } - - val purgeAttachmentCommandNode = - literal("${prefix}_PURGE_ATTACHMENT") - .requires(::sentByModerator) - .executes { - purgeAttachmentCommand( - it.source.issue, - null, - 0, - Int.MAX_VALUE - ) - } - .then( - literal("from") - .then( - argument("minId", integer(0)) - .executes { - purgeAttachmentCommand( - it.source.issue, - null, - it.getInt("minId"), - Int.MAX_VALUE - ) - } - .then( - literal("to") - .then( - argument("maxId", integer(0)) - .executes { - purgeAttachmentCommand( - it.source.issue, - null, - it.getInt("minId"), - it.getInt("maxId") - ) - } - .then( - literal("by") - .then( - argument("username", greedyString()) - .executes { - purgeAttachmentCommand( - it.source.issue, - it.getString("username"), - it.getInt("minId"), - it.getInt("maxId") - ) - } - ) - ) - ) - ) - .then( - literal("by") - .then( - argument("username", greedyString()) - .executes { - purgeAttachmentCommand( - it.source.issue, - it.getString("username"), - it.getInt("minId"), - Int.MAX_VALUE - ) - } - ) - ) - ) - ) - .then( - literal("to") - .then( - argument("maxId", integer(0)) - .executes { - purgeAttachmentCommand( - it.source.issue, - null, - 0, - it.getInt("maxId") - ) - } - .then( - literal("by") - .then( - argument("username", greedyString()) - .executes { - purgeAttachmentCommand( - it.source.issue, - it.getString("username"), - 0, - it.getInt("maxId") - ) - } - ) - ) - ) - ) - .then( - literal("by") - .then( - argument("username", greedyString()) - .executes { - purgeAttachmentCommand( - it.source.issue, - it.getString("username"), - 0, - Int.MAX_VALUE - ) - } - ) - ) - +// val clearProjectCacheCommandNode = +// literal("${prefix}_CLEAR_PROJECT_CACHE") +// .executes { +// clearProjectCacheCommand() +// } +// +// val deleteCommentsCommandNodeChild = +// argument("name", greedyString()) +// .executes { +// deleteCommentsCommand( +// it.source.issue, +// it.getString("name") +// ) +// } +// val deleteCommentsCommandNode = +// literal("${prefix}_DELETE_COMMENTS") +// .requires(::sentByModerator) +// .then(deleteCommentsCommandNodeChild) +// val removeCommentsCommandNode = +// literal("${prefix}_REMOVE_COMMENTS") +// .requires(::sentByModerator) +// .then(deleteCommentsCommandNodeChild) +// +// val deobfuscateCommandNode = run { +// val attachmentIdArg = "attachmentId" +// val minecraftVersionIdArg = "minecraftVersionId" +// val crashReportTypeArg = "crashReportType" +// literal("${prefix}_DEOBFUSCATE") +// .then( +// argument(attachmentIdArg, string()) +// .executes { +// deobfuscateCommand.invoke( +// it.source.issue, +// it.getString(attachmentIdArg) +// ) +// } +// .then( +// argument(minecraftVersionIdArg, string()) +// .executes { +// deobfuscateCommand.invoke( +// it.source.issue, +// it.getString(attachmentIdArg), +// it.getString(minecraftVersionIdArg) +// ) +// } +// .then( +// argument( +// crashReportTypeArg, +// enumArgumentType() +// ) +// .executes { +// deobfuscateCommand.invoke( +// it.source.issue, +// it.getString(attachmentIdArg), +// it.getString(minecraftVersionIdArg), +// it.getArgument(crashReportTypeArg, CrashReportType::class.java) +// ) +// } +// ) +// ) +// ) +// } +// +// val deleteLinksCommandNodeChild = +// argument("linkList", LinkListArgumentType()) +// .executes { +// deleteLinksCommand( +// it.source.issue, +// it.getLinkList("linkList") +// ) +// } +// val deleteLinksCommandNode = +// literal("${prefix}_DELETE_LINKS") +// .requires(::sentByModerator) +// .then(deleteLinksCommandNodeChild) +// val removeLinksCommandNode = +// literal("${prefix}_REMOVE_LINKS") +// .requires(::sentByModerator) +// .then(deleteLinksCommandNodeChild) +// +// val fixCapitalizationCommandNode = +// literal("${prefix}_FIX_CAPITALIZATION") +// .executes { +// fixCapitalizationCommand( +// it.source.issue +// ) +// } +// +// val fixedCommandNode = +// literal("${prefix}_FIXED") +// .requires(::sentByModerator) +// .then( +// argument("version", greedyStringWithFlag("force")) +// .executes { +// val (version, force) = it.getStringWithFlag("version") +// fixedCommand( +// it.source.issue, +// version, +// force +// ) +// } +// ) +// +// val listUserActivityCommandNode = +// literal("${prefix}_LIST_USER_ACTIVITY") +// .requires(::sentByModerator) +// .then( +// argument("username", greedyString()) +// .executes { +// listUserActivityCommand( +// it.source.issue, +// it.getString("username") +// ) +// } +// ) +// +// val makePrivateCommandNode = +// literal("${prefix}_MAKE_PRIVATE") +// .executes { +// makePrivateCommand( +// it.source.issue +// ) +// } +// +// val purgeAttachmentCommandNode = +// literal("${prefix}_PURGE_ATTACHMENT") +// .requires(::sentByModerator) +// .executes { +// purgeAttachmentCommand( +// it.source.issue, +// null, +// 0, +// Int.MAX_VALUE +// ) +// } +// .then( +// literal("from") +// .then( +// argument("minId", integer(0)) +// .executes { +// purgeAttachmentCommand( +// it.source.issue, +// null, +// it.getInt("minId"), +// Int.MAX_VALUE +// ) +// } +// .then( +// literal("to") +// .then( +// argument("maxId", integer(0)) +// .executes { +// purgeAttachmentCommand( +// it.source.issue, +// null, +// it.getInt("minId"), +// it.getInt("maxId") +// ) +// } +// .then( +// literal("by") +// .then( +// argument("username", greedyString()) +// .executes { +// purgeAttachmentCommand( +// it.source.issue, +// it.getString("username"), +// it.getInt("minId"), +// it.getInt("maxId") +// ) +// } +// ) +// ) +// ) +// ) +// .then( +// literal("by") +// .then( +// argument("username", greedyString()) +// .executes { +// purgeAttachmentCommand( +// it.source.issue, +// it.getString("username"), +// it.getInt("minId"), +// Int.MAX_VALUE +// ) +// } +// ) +// ) +// ) +// ) +// .then( +// literal("to") +// .then( +// argument("maxId", integer(0)) +// .executes { +// purgeAttachmentCommand( +// it.source.issue, +// null, +// 0, +// it.getInt("maxId") +// ) +// } +// .then( +// literal("by") +// .then( +// argument("username", greedyString()) +// .executes { +// purgeAttachmentCommand( +// it.source.issue, +// it.getString("username"), +// 0, +// it.getInt("maxId") +// ) +// } +// ) +// ) +// ) +// ) +// .then( +// literal("by") +// .then( +// argument("username", greedyString()) +// .executes { +// purgeAttachmentCommand( +// it.source.issue, +// it.getString("username"), +// 0, +// Int.MAX_VALUE +// ) +// } +// ) +// ) +// // val removeContentCommandNode = // literal("${prefix}_REMOVE_CONTENT") // .requires(::sentByModerator) @@ -320,43 +312,43 @@ fun getCommandDispatcher( // ) // } // ) - - val reopenCommandNode = - literal("${prefix}_REOPEN") - .executes { - reopenCommand( - it.source.issue - ) - } - - val shadowbanCommandNode = - literal("${prefix}_SHADOWBAN") - .requires(::sentByModerator) - .then( - argument("username", greedyString()) - .executes { - shadowbanCommand( - it.getString("username") - ) - } - ) - - register(addLinksCommandNode) +// +// val reopenCommandNode = +// literal("${prefix}_REOPEN") +// .executes { +// reopenCommand( +// it.source.issue +// ) +// } +// +// val shadowbanCommandNode = +// literal("${prefix}_SHADOWBAN") +// .requires(::sentByModerator) +// .then( +// argument("username", greedyString()) +// .executes { +// shadowbanCommand( +// it.getString("username") +// ) +// } +// ) +// +// register(addLinksCommandNode) register(addVersionCommandNode) - register(clearProjectCacheCommandNode) - register(deleteCommentsCommandNode) - register(deleteLinksCommandNode) - register(deobfuscateCommandNode) - register(fixCapitalizationCommandNode) - register(fixedCommandNode) - register(listUserActivityCommandNode) - register(makePrivateCommandNode) - register(purgeAttachmentCommandNode) - register(removeCommentsCommandNode) - register(removeLinksCommandNode) +// register(clearProjectCacheCommandNode) +// register(deleteCommentsCommandNode) +// register(deleteLinksCommandNode) +// register(deobfuscateCommandNode) +// register(fixCapitalizationCommandNode) +// register(fixedCommandNode) +// register(listUserActivityCommandNode) +// register(makePrivateCommandNode) +// register(purgeAttachmentCommandNode) +// register(removeCommentsCommandNode) +// register(removeLinksCommandNode) // register(removeContentCommandNode) - register(reopenCommandNode) - register(shadowbanCommandNode) +// register(reopenCommandNode) +// register(shadowbanCommandNode) } } @@ -366,4 +358,5 @@ private fun sentByModerator(source: CommandSource) = private fun CommandContext<*>.getInt(name: String) = getArgument(name, Int::class.java) private fun CommandContext<*>.getLinkList(name: String) = getArgument(name, LinkList::class.java) private fun CommandContext<*>.getString(name: String) = getArgument(name, String::class.java) -private fun CommandContext<*>.getStringWithFlag(name: String) = getArgument(name, StringWithFlag::class.java) +private fun CommandContext<*>.getStringWithFlag(name: String) = + getArgument(name, StringWithFlag::class.java) diff --git a/src/main/kotlin/io/github/mojira/arisa/modules/commands/CommandSource.kt b/src/main/kotlin/io/github/mojira/arisa/modules/commands/CommandSource.kt index 768ecc2d..4d599075 100644 --- a/src/main/kotlin/io/github/mojira/arisa/modules/commands/CommandSource.kt +++ b/src/main/kotlin/io/github/mojira/arisa/modules/commands/CommandSource.kt @@ -1,10 +1,10 @@ package io.github.mojira.arisa.modules.commands import io.github.mojira.arisa.domain.Comment -import io.github.mojira.arisa.domain.Issue +import io.github.mojira.arisa.domain.cloud.CloudIssue data class CommandSource( - val issue: Issue, + val issue: CloudIssue, val comment: Comment, val line: Int ) diff --git a/src/main/kotlin/io/github/mojira/arisa/registry/InstantModuleRegistry.kt b/src/main/kotlin/io/github/mojira/arisa/registry/InstantModuleRegistry.kt index e460e7f9..8c62764f 100644 --- a/src/main/kotlin/io/github/mojira/arisa/registry/InstantModuleRegistry.kt +++ b/src/main/kotlin/io/github/mojira/arisa/registry/InstantModuleRegistry.kt @@ -1,10 +1,13 @@ package io.github.mojira.arisa.registry import com.uchuhimo.konf.Config +import com.urielsalis.mccrashlib.CrashReader import io.github.mojira.arisa.ExecutionTimeframe import io.github.mojira.arisa.domain.cloud.CloudIssue +import io.github.mojira.arisa.infrastructure.AttachmentUtils import io.github.mojira.arisa.infrastructure.HelperMessageService import io.github.mojira.arisa.infrastructure.config.Arisa +import io.github.mojira.arisa.modules.CommandModule import io.github.mojira.arisa.modules.PrivateDuplicateModule import io.github.mojira.arisa.modules.ReopenAwaitingModule import io.github.mojira.arisa.modules.privacy.AccessTokenRedactor @@ -50,6 +53,21 @@ class InstantModuleRegistry(config: Config) : ModuleRegistry(config) HelperMessageService.getMessage("MC", keys = listOf(config[Arisa.Modules.ReopenAwaiting.message])) ) ) + + val attachmentUtils = AttachmentUtils( + config[Arisa.Modules.Crash.crashExtensions], + CrashReader() + ) + + register( + Arisa.Modules.Command, + CommandModule( + config[Arisa.Modules.Command.commandPrefix], + config[Arisa.Credentials.username], + config[Arisa.Debug.ignoreOwnCommands], + attachmentUtils + ) + ) } private fun List.toSetNoDuplicates(): Set { diff --git a/src/test/kotlin/io/github/mojira/arisa/modules/CommandModuleTest.kt b/src/test/kotlin/io/github/mojira/arisa/modules/CommandModuleTest.kt index aecdf98e..b21b4b0f 100644 --- a/src/test/kotlin/io/github/mojira/arisa/modules/CommandModuleTest.kt +++ b/src/test/kotlin/io/github/mojira/arisa/modules/CommandModuleTest.kt @@ -11,8 +11,8 @@ import io.github.mojira.arisa.infrastructure.AttachmentUtils import io.github.mojira.arisa.modules.commands.CommandExceptions import io.github.mojira.arisa.modules.commands.CommandSource import io.github.mojira.arisa.utils.RIGHT_NOW +import io.github.mojira.arisa.utils.mockCloudIssue import io.github.mojira.arisa.utils.mockComment -import io.github.mojira.arisa.utils.mockIssue import io.github.mojira.arisa.utils.mockUser import io.kotest.assertions.arrow.either.shouldBeLeft import io.kotest.assertions.arrow.either.shouldBeRight @@ -29,7 +29,7 @@ class CommandModuleTest : StringSpec({ val module = CommandModule(PREFIX, BOT_USER_NAME, true, attachmentUtils, ::getDispatcher) "should return OperationNotNeededModuleResponse when no comments" { - val issue = mockIssue() + val issue = mockCloudIssue() val result = module(issue, RIGHT_NOW) @@ -40,7 +40,7 @@ class CommandModuleTest : StringSpec({ val comment = getComment( getAuthorGroups = { emptyList() } ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -54,7 +54,7 @@ class CommandModuleTest : StringSpec({ visibilityType = "group", visibilityValue = "users" ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -67,7 +67,7 @@ class CommandModuleTest : StringSpec({ val comment = getComment( visibilityType = "notagroup" ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -80,7 +80,7 @@ class CommandModuleTest : StringSpec({ val comment = getComment( visibilityValue = "notagroup" ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -94,7 +94,7 @@ class CommandModuleTest : StringSpec({ visibilityType = null, visibilityValue = null ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -108,7 +108,7 @@ class CommandModuleTest : StringSpec({ // No underscore ('_') after prefix body = PREFIX ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -123,7 +123,7 @@ class CommandModuleTest : StringSpec({ name = BOT_USER_NAME ) ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -140,7 +140,7 @@ class CommandModuleTest : StringSpec({ ), update = { updatedComment = it } ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -162,7 +162,7 @@ class CommandModuleTest : StringSpec({ update = { updatedComment = it }, body = "ARISA_VALUE 42" ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -184,7 +184,7 @@ class CommandModuleTest : StringSpec({ update = { updatedComment = it }, body = "ARISA_FAIL arg" ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -209,7 +209,7 @@ class CommandModuleTest : StringSpec({ update = { updatedComment = it }, body = "TESTING_COMMAND_SUCCESS arg" ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) @@ -238,7 +238,7 @@ class CommandModuleTest : StringSpec({ |Thank you! """.trimMargin() ) - val issue = mockIssue( + val issue = mockCloudIssue( comments = listOf(comment) ) diff --git a/src/test/kotlin/io/github/mojira/arisa/modules/commands/AddVersionCommandTest.kt b/src/test/kotlin/io/github/mojira/arisa/modules/commands/AddVersionCommandTest.kt index 705c1646..6aa43196 100644 --- a/src/test/kotlin/io/github/mojira/arisa/modules/commands/AddVersionCommandTest.kt +++ b/src/test/kotlin/io/github/mojira/arisa/modules/commands/AddVersionCommandTest.kt @@ -1,7 +1,7 @@ package io.github.mojira.arisa.modules.commands import com.mojang.brigadier.exceptions.CommandSyntaxException -import io.github.mojira.arisa.utils.mockIssue +import io.github.mojira.arisa.utils.mockCloudIssue import io.github.mojira.arisa.utils.mockProject import io.github.mojira.arisa.utils.mockVersion import io.kotest.assertions.throwables.shouldThrow @@ -12,7 +12,7 @@ class AddVersionCommandTest : StringSpec({ "should throw VERSION_ALREADY_AFFECTED when version is already added" { val command = AddVersionCommand() - val issue = mockIssue( + val issue = mockCloudIssue( project = mockProject( versions = listOf(getVersion(released = true, archived = false)) ), @@ -28,7 +28,7 @@ class AddVersionCommandTest : StringSpec({ "should throw NO_SUCH_VERSION when version doesn't exist" { val command = AddVersionCommand() - val issue = mockIssue( + val issue = mockCloudIssue( project = mockProject( versions = listOf(getVersion(released = true, archived = false)) ), @@ -44,7 +44,7 @@ class AddVersionCommandTest : StringSpec({ "should add version" { val command = AddVersionCommand() - val issue = mockIssue( + val issue = mockCloudIssue( project = mockProject( versions = listOf( getVersion(released = true, archived = false), diff --git a/src/test/kotlin/io/github/mojira/arisa/utils/MockCloudDomain.kt b/src/test/kotlin/io/github/mojira/arisa/utils/MockCloudDomain.kt index 856e9e86..52b5c18f 100644 --- a/src/test/kotlin/io/github/mojira/arisa/utils/MockCloudDomain.kt +++ b/src/test/kotlin/io/github/mojira/arisa/utils/MockCloudDomain.kt @@ -8,9 +8,11 @@ import io.github.mojira.arisa.domain.Comment import io.github.mojira.arisa.domain.CommentOptions import io.github.mojira.arisa.domain.Project import io.github.mojira.arisa.domain.User +import io.github.mojira.arisa.domain.Version import io.github.mojira.arisa.domain.cloud.CloudIssue import io.github.mojira.arisa.domain.cloud.CloudLink import io.github.mojira.arisa.domain.cloud.CloudLinkedIssue +import io.github.mojira.arisa.infrastructure.jira.addAffectedVersionById import java.io.File import java.time.Instant @@ -26,6 +28,7 @@ fun mockCloudIssue( created: Instant = RIGHT_NOW, updated: Instant = RIGHT_NOW, project: Project = mockProject(), + affectedVersions: List = emptyList(), attachments: List = emptyList(), comments: List = emptyList(), links: List = emptyList(), @@ -35,6 +38,9 @@ fun mockCloudIssue( addComment: (options: CommentOptions) -> Unit = { }, addRawRestrictedComment: (body: String, restriction: String) -> Unit = { _, _ -> }, addRawBotComment: (rawMessage: String) -> Unit = { }, + addAffectedVersionById: (id: String) -> Unit = { }, + unarchiveVersion: (id: String) -> Unit = { }, + archiveVersion: (id: String) -> Unit = { }, addAttachmentFromFile: (file: File, cleanupCallback: () -> Unit) -> Unit = { _, cleanupCallback -> cleanupCallback() } ) = CloudIssue( key = key, @@ -48,6 +54,7 @@ fun mockCloudIssue( created = created, updated = updated, project = project, + affectedVersions = affectedVersions, attachments = attachments, comments = comments, links = links, @@ -57,6 +64,9 @@ fun mockCloudIssue( addComment = addComment, addRawRestrictedComment = addRawRestrictedComment, addRawBotComment = addRawBotComment, + addAffectedVersionById = addAffectedVersionById, + unarchiveVersion = unarchiveVersion, + archiveVersion = archiveVersion, addAttachmentFromFile = addAttachmentFromFile )