-
Notifications
You must be signed in to change notification settings - Fork 326
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a method for getting component groups without execution context (#…
…7569) close #7510 Changelog: - add: `runtime/getComponentGroups` request returning the component groups currently available in runtime
- Loading branch information
Showing
6 changed files
with
281 additions
and
6 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
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
146 changes: 146 additions & 0 deletions
146
...main/scala/org/enso/languageserver/requesthandler/runtime/GetComponentGroupsHandler.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,146 @@ | ||
package org.enso.languageserver.requesthandler.runtime | ||
|
||
import akka.actor.{Actor, ActorRef, Cancellable, Props} | ||
import com.typesafe.scalalogging.LazyLogging | ||
import org.enso.editions.LibraryName | ||
import org.enso.jsonrpc._ | ||
import org.enso.languageserver.libraries.{ | ||
ComponentGroupsResolver, | ||
ComponentGroupsValidator, | ||
LibraryComponentGroup | ||
} | ||
import org.enso.languageserver.requesthandler.RequestTimeout | ||
import org.enso.languageserver.runtime.RuntimeApi._ | ||
import org.enso.languageserver.util.UnhandledLogging | ||
import org.enso.pkg.ComponentGroups | ||
import org.enso.polyglot.runtime.Runtime.Api | ||
|
||
import java.util.UUID | ||
|
||
import scala.collection.immutable.ListMap | ||
import scala.concurrent.duration.FiniteDuration | ||
|
||
/** A request handler for `runtime/getComponentGroups` commands. | ||
* | ||
* @param timeout request timeout | ||
* @param runtime a reference to the runtime connector | ||
* @param componentGroupsResolver resolves dependencies between the component | ||
* groups of different packages | ||
* @param componentGroupsValidator validates the component groups | ||
*/ | ||
class GetComponentGroupsHandler( | ||
timeout: FiniteDuration, | ||
runtime: ActorRef, | ||
componentGroupsResolver: ComponentGroupsResolver, | ||
componentGroupsValidator: ComponentGroupsValidator | ||
) extends Actor | ||
with LazyLogging | ||
with UnhandledLogging { | ||
|
||
import context.dispatcher | ||
|
||
override def receive: Receive = requestStage | ||
|
||
private def requestStage: Receive = { | ||
case Request( | ||
RuntimeGetComponentGroups, | ||
id, | ||
_ | ||
) => | ||
runtime ! Api.Request(UUID.randomUUID(), Api.GetComponentGroupsRequest()) | ||
val cancellable = | ||
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout) | ||
context.become(responseStage(id, sender(), cancellable)) | ||
} | ||
|
||
private def responseStage( | ||
id: Id, | ||
replyTo: ActorRef, | ||
cancellable: Cancellable | ||
): Receive = { | ||
case RequestTimeout => | ||
logger.error("Request [{}] timed out.", id) | ||
replyTo ! ResponseError(Some(id), Errors.RequestTimeout) | ||
context.stop(self) | ||
|
||
case Api.Response(_, Api.GetComponentGroupsResponse(componentGroups)) => | ||
replyTo ! ResponseResult( | ||
RuntimeGetComponentGroups, | ||
id, | ||
RuntimeGetComponentGroups.Result( | ||
resolveComponentGroups(componentGroups.to(ListMap)) | ||
) | ||
) | ||
cancellable.cancel() | ||
context.stop(self) | ||
} | ||
|
||
private def resolveComponentGroups( | ||
componentGroups: Map[LibraryName, ComponentGroups] | ||
): Seq[LibraryComponentGroup] = { | ||
val validated = componentGroupsValidator.validate(componentGroups) | ||
|
||
validated.collect { case (_, Left(error)) => | ||
logValidationError(error) | ||
} | ||
|
||
val validatedComponents = validated | ||
.collect { case (libraryName, Right(componentGroups)) => | ||
libraryName -> componentGroups | ||
} | ||
componentGroupsResolver.resolveComponentGroups(validatedComponents) | ||
} | ||
|
||
private def logValidationError( | ||
error: ComponentGroupsValidator.ValidationError | ||
): Unit = | ||
error match { | ||
case ComponentGroupsValidator.ValidationError | ||
.InvalidComponentGroups(libraryName, message) => | ||
logger.warn( | ||
s"Validation error. Failed to read library [$libraryName] " + | ||
s"component groups (reason: $message)." | ||
) | ||
case ComponentGroupsValidator.ValidationError | ||
.DuplicatedComponentGroup(libraryName, moduleReference) => | ||
logger.warn( | ||
s"Validation error. Library [$libraryName] defines duplicate " + | ||
s"component group [$moduleReference]." | ||
) | ||
case ComponentGroupsValidator.ValidationError | ||
.ComponentGroupExtendsNothing(libraryName, moduleReference) => | ||
logger.warn( | ||
s"Validation error. Library [$libraryName] component group " + | ||
s"[$moduleReference] extends nothing." | ||
) | ||
} | ||
} | ||
|
||
object GetComponentGroupsHandler { | ||
|
||
/** Creates configuration object used to create a | ||
* [[GetComponentGroupsHandler]]. | ||
* | ||
* @param timeout request timeout | ||
* @param runtime a reference to the runtime connector | ||
* @param componentGroupsResolver resolves dependencies between the component | ||
* groups of different packages | ||
* @param componentGroupsValidator validates the component groups | ||
*/ | ||
def props( | ||
timeout: FiniteDuration, | ||
runtime: ActorRef, | ||
componentGroupsResolver: ComponentGroupsResolver = | ||
new ComponentGroupsResolver, | ||
componentGroupsValidator: ComponentGroupsValidator = | ||
new ComponentGroupsValidator | ||
): Props = | ||
Props( | ||
new GetComponentGroupsHandler( | ||
timeout, | ||
runtime, | ||
componentGroupsResolver, | ||
componentGroupsValidator | ||
) | ||
) | ||
} |
23 changes: 23 additions & 0 deletions
23
engine/language-server/src/main/scala/org/enso/languageserver/runtime/RuntimeApi.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,23 @@ | ||
package org.enso.languageserver.runtime | ||
|
||
import org.enso.jsonrpc.{HasParams, HasResult, Method, Unused} | ||
import org.enso.languageserver.libraries.LibraryComponentGroup | ||
|
||
object RuntimeApi { | ||
|
||
case object RuntimeGetComponentGroups | ||
extends Method("runtime/getComponentGroups") { | ||
|
||
case class Result(componentGroups: Seq[LibraryComponentGroup]) | ||
|
||
implicit val hasParams: HasParams.Aux[this.type, Unused.type] = | ||
new HasParams[this.type] { | ||
type Params = Unused.type | ||
} | ||
implicit val hasResult | ||
: HasResult.Aux[this.type, RuntimeGetComponentGroups.Result] = | ||
new HasResult[this.type] { | ||
type Result = RuntimeGetComponentGroups.Result | ||
} | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
...e/language-server/src/test/scala/org/enso/languageserver/websocket/json/RuntimeTest.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,62 @@ | ||
package org.enso.languageserver.websocket.json | ||
|
||
import io.circe.literal._ | ||
import org.enso.languageserver.runtime.TestComponentGroups | ||
import org.enso.polyglot.runtime.Runtime.Api | ||
|
||
class RuntimeTest extends BaseServerTest { | ||
|
||
"runtime/getComponentGroups" should { | ||
|
||
"return component groups successfully" in { | ||
val client = getInitialisedWsClient() | ||
|
||
// get component groups | ||
client.send( | ||
json""" | ||
{ "jsonrpc": "2.0", | ||
"method": "runtime/getComponentGroups", | ||
"id": 1, | ||
"params": null | ||
} | ||
""" | ||
) | ||
val requestId = | ||
runtimeConnectorProbe.receiveN(1).head match { | ||
case Api.Request(requestId, Api.GetComponentGroupsRequest()) => | ||
requestId | ||
case msg => | ||
fail(s"Unexpected message: $msg") | ||
} | ||
|
||
runtimeConnectorProbe.lastSender ! Api.Response( | ||
requestId, | ||
Api.GetComponentGroupsResponse( | ||
TestComponentGroups.standardBase.toVector | ||
) | ||
) | ||
client.expectJson(json""" | ||
{ "jsonrpc": "2.0", | ||
"id": 1, | ||
"result": { | ||
"componentGroups": [ | ||
{ | ||
"library" : "Standard.Base", | ||
"name" : "Input", | ||
"exports" : [ | ||
{ | ||
"name" : "Standard.Base.File.new" | ||
}, | ||
{ | ||
"name" : "Standard.Database.Connection.Database.connect" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} | ||
""") | ||
} | ||
} | ||
|
||
} |