diff --git a/docs/existing_use_cases_1.cml b/docs/existing_use_cases_1.cml index f2c9234..50da9ee 100644 --- a/docs/existing_use_cases_1.cml +++ b/docs/existing_use_cases_1.cml @@ -1,5 +1,5 @@ UseCase UC1_CML_Editing { - actor = "ContextMapper User" + actor = "Context Mapper User" interactions = "recognise" a "SyntaxError", "recognise" a "CmlKeyword", @@ -16,7 +16,7 @@ UseCase UC1_CML_Editing { } UseCase UC2_CML_QuickFix { - actor = "ContextMapper User" + actor = "Context Mapper User" interactions = "create" a "MissingBoundedContext", "split" a "UserStory" @@ -24,13 +24,13 @@ UseCase UC2_CML_QuickFix { } UseCase UC3_CML_Generate_ContextMap { - actor = "ContextMapper User" + actor = "Context Mapper User" interactions = "generate" a "VisualContextMap" benefit = "I am able to create a context map diagram from my CML context map" } UseCase UC4_CML_Generate_PlantUML { - actor = "ContextMapper User" + actor = "Context Mapper User" interactions = "generate" a "PlantUmlDiagram" benefit = "I am able to create visual representations of my CML definitions" } diff --git a/docs/existing_use_cases_2.cml b/docs/existing_use_cases_2.cml index 30b3327..0a9fa90 100644 --- a/docs/existing_use_cases_2.cml +++ b/docs/existing_use_cases_2.cml @@ -1,31 +1,23 @@ UseCase UC5_CML_Generate_SketchMiner { - actor = "ContextMapper User" + actor = "Context Mapper User" interactions = "generate" a "SketchMinerDiagram" benefit = "I am able to create a BPMN diagram from my process flow definitions" } UseCase UC6_CML_Generate_MDSLContract { - actor = "ContextMapper User" + actor = "Context Mapper User" interactions = "generate" a "MDSLContract" benefit = "I am able to create an MDSL contract from my CML definitions" } -UseCase UC7_CML_ServiceCutter { - actor = "ContextMapper User" - interactions = - "create" a "ServiceCutterConfiguration", - "generate" a "ServiceCutterDiagram" - benefit = "I am able to create ServiceCutter diagrams so I can visually see my service cuts" -} - -UseCase UC8_CML_FreeMarker { - actor = "ContextMapper User" +UseCase UC7_CML_FreeMarker { + actor = "Context Mapper User" interactions = "apply" a "FreeMakerTemplate" benefit = "I am able to create my own files, e.g. Markdown, from my CML definitions" } -UseCase UC9_CML_Refactorings { - actor = "ContextMapper User" +UseCase UC8_CML_Refactorings { + actor = "Context Mapper User" interactions = "split" an "Aggregate", "split" a "BoundedContext", @@ -39,14 +31,14 @@ UseCase UC9_CML_Refactorings { benefit = "I am able to easily restructure my CML definitions" } -UseCase UC10_CML_Discovery { - actor = "ContextMapper User" - interactions = "discover" a "ContextMapperDefintion" for "my existing project" +UseCase UC9_CML_Discovery { + actor = "Context Mapper User" + interactions = "discover" a "Context MapperDefintion" for "my existing project" benefit = "I am able to get CML defintions for my existing project" } -UseCase U11_CML_Validators { - actor = "ContextMapper User" +UseCase U10_CML_Validators { + actor = "Context Mapper User" interactions = "validate" a "CMLFile" benefit = "I am able to validate my CML files for their semantic correctness" } \ No newline at end of file diff --git a/src/main/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGenerator.kt b/src/main/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGenerator.kt index 365b8ed..1b8d7cd 100644 --- a/src/main/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGenerator.kt +++ b/src/main/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGenerator.kt @@ -3,11 +3,7 @@ package org.contextmapper.intellij.actions.generators import com.intellij.openapi.project.Project import com.redhat.devtools.lsp4ij.LanguageServerItem import com.redhat.devtools.lsp4ij.LanguageServerManager -import com.redhat.devtools.lsp4ij.commands.CommandExecutor import com.redhat.devtools.lsp4ij.commands.LSPCommandContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import org.contextmapper.intellij.actions.LspCommandExecutor import org.contextmapper.intellij.utils.CONTEXT_MAPPER_SERVER_ID import org.eclipse.lsp4j.Command @@ -26,70 +22,78 @@ class ContextMapperGenerator( val context = LSPCommandContext(command, project) - val languageServerResult = getLanguageServer() - if (languageServerResult.isFailure) { - future.complete(Result.failure(languageServerResult.exceptionOrNull()!!)) - return future - } - context.preferredLanguageServerId = CONTEXT_MAPPER_SERVER_ID - context.preferredLanguageServer = languageServerResult.getOrNull() + languageServerManager.getLanguageServer(CONTEXT_MAPPER_SERVER_ID) + .thenAcceptAsync { languageServer -> + executeCommand(future, context, languageServer) + } + .exceptionally { throwable -> + future.complete( + Result.failure( + ContextMapperGeneratorException( + "Could not find language server instance.", + throwable, + ), + ), + ) + null + } - val response = commandExecutor(context) - CoroutineScope(Dispatchers.IO).launch { - processResponse(response, future) - } return future } - private fun processResponse( - response: CommandExecutor.LSPCommandResponse, - future: CompletableFuture> + private fun executeCommand( + future: CompletableFuture>, + context: LSPCommandContext, + languageServerItem: LanguageServerItem? ) { - val result = response.response - - if (result != null) { - try { - val returnedValue = result.join() + if (languageServerItem == null) { + future.complete( + Result.failure( + ContextMapperGeneratorException( + "Could not find language server instance.", + ), + ), + ) + return + } + context.preferredLanguageServerId = CONTEXT_MAPPER_SERVER_ID + context.preferredLanguageServer = languageServerItem - val generatedFilesResult = extractGeneratedFiles(returnedValue) - if (generatedFilesResult.isFailure) { - future.complete(Result.failure(generatedFilesResult.exceptionOrNull()!!)) - } + val response = commandExecutor(context).response + if (response == null) { + future.complete(Result.failure(ContextMapperGeneratorException("Generator failed without error"))) + return + } - val generatedFiles = generatedFilesResult.getOrNull()!! - if (generatedFiles.isEmpty()) { - future.complete(Result.success(GeneratorResult(listOf()))) - } else { - future.complete(Result.success(GeneratorResult(generatedFiles))) - } - } catch (ex: Exception) { + response.thenAcceptAsync { generatorResult -> processResponse(future, generatorResult) } + .exceptionally { throwable -> future.complete( Result.failure( ContextMapperGeneratorException( - "Generator failed with exception: ${ex.message}", - ex, + "Generator failed with exception: ${throwable.message}", + throwable, ), ), ) + null } - } else { - future.complete(Result.failure(ContextMapperGeneratorException("Generator failed without error"))) - } } - private fun getLanguageServer(): Result { - return try { - val languageServer = - languageServerManager.getLanguageServer(CONTEXT_MAPPER_SERVER_ID) - .join() - Result.success(languageServer) - } catch (ex: Exception) { - Result.failure( - ContextMapperGeneratorException( - "Could not find language server instance.", - ex, - ), - ) + private fun processResponse( + future: CompletableFuture>, + generatorResult: Any + ) { + val generatedFilesResult = extractGeneratedFiles(generatorResult) + if (generatedFilesResult.isFailure) { + future.complete(Result.failure(generatedFilesResult.exceptionOrNull()!!)) + return + } + + val generatedFiles = generatedFilesResult.getOrNull()!! + if (generatedFiles.isEmpty()) { + future.complete(Result.success(GeneratorResult(listOf()))) + } else { + future.complete(Result.success(GeneratorResult(generatedFiles))) } } diff --git a/src/test/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGeneratorTest.kt b/src/test/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGeneratorTest.kt index 8b1a2c9..f6e15b5 100644 --- a/src/test/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGeneratorTest.kt +++ b/src/test/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGeneratorTest.kt @@ -1,6 +1,7 @@ package org.contextmapper.intellij.actions.generators import com.intellij.openapi.project.Project +import com.redhat.devtools.lsp4ij.LanguageServerItem import com.redhat.devtools.lsp4ij.LanguageServerManager import com.redhat.devtools.lsp4ij.commands.CommandExecutor import com.redhat.devtools.lsp4ij.settings.UserDefinedLanguageServerSettings @@ -17,6 +18,8 @@ import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer class ContextMapperGeneratorTest() { private val command = Command("TestCommand", "cmd.id") @@ -35,7 +38,11 @@ class ContextMapperGeneratorTest() { mockk(relaxed = true) { every { getLanguageServer(any()) } returns mockk { - every { join() } returns mockk(relaxed = true) + val actionSlot = slot>() + every { thenAcceptAsync(capture(actionSlot)) } answers { + actionSlot.captured.accept(mockk(relaxed = true)) + CompletableFuture.completedFuture(null) + } } } project = @@ -55,7 +62,11 @@ class ContextMapperGeneratorTest() { mockk { every { response } returns mockk { - every { join() } returns listOf("diagram.puml") + val actionSlot = slot>() + every { thenAcceptAsync(capture(actionSlot)) } answers { + actionSlot.captured.accept(listOf("diagram.puml")) + CompletableFuture.completedFuture(null) + } } } @@ -74,7 +85,11 @@ class ContextMapperGeneratorTest() { mockk { every { response } returns mockk { - every { join() } returns listOf() + val actionSlot = slot>() + every { thenAcceptAsync(capture(actionSlot)) } answers { + actionSlot.captured.accept(listOf()) + CompletableFuture.completedFuture(null) + } } } @@ -93,7 +108,10 @@ class ContextMapperGeneratorTest() { mockk { every { response } returns mockk { - every { join() } throws Exception("Test Exception") + val actionSlot = slot>() + every { thenAcceptAsync(capture(actionSlot)) } answers { + CompletableFuture.failedFuture(Exception("Test Exception")) + } } } @@ -128,7 +146,11 @@ class ContextMapperGeneratorTest() { mockk { every { response } returns mockk { - every { join() } returns mockk() + val actionSlot = slot>() + every { thenAcceptAsync(capture(actionSlot)) } answers { + actionSlot.captured.accept(mockk()) + CompletableFuture.completedFuture(null) + } } } @@ -145,7 +167,10 @@ class ContextMapperGeneratorTest() { fun testGeneratorWithMissingLanguageServer() { every { languageServerManager.getLanguageServer(eq(CONTEXT_MAPPER_SERVER_ID)) } returns mockk { - every { join() } throws RuntimeException() + val actionSlot = slot>() + every { thenAcceptAsync(capture(actionSlot)) } answers { + CompletableFuture.failedFuture(RuntimeException()) + } } val result =