* Improve shutdown logic of language server This PR addresses problems mentioned in #7470 and #7729: - shutting a language server explicitly will not lead to a soft shutdown - `project/status` endpoint returns the state of the language server `LanguageServerController` now also signed up for `ClientConnect` messages. For it to be unambiguous, we need to carry around the port number of the language server as a way of identifying the right one. One can now use `project/status` to additionally determine the state of the language server. Also relies on a proper fix for #7765. * changelog * PR comments
28 KiB
layout | title | category | tags | order | |||
---|---|---|---|---|---|---|---|
developer-doc | Enso Protocol Project Manager Message Specification | language-server |
|
3 |
Enso Protocol Project Manager Message Specification
This document contains the specification of the Enso protocol messages that pertain to the project manager component. Please familiarise yourself with the common features of the protocol before reading this document.
For information on the design and architecture of the protocol, as well as its transport formats, please look here.
- Types
- Project Management Operations
- Action Progress Reporting
- Runtime Version Management
- Configuration Management
- Logging Service
- Language Server Management
- Errors
MissingComponentError
BrokenComponentError
ProjectManagerUpgradeRequired
ComponentInstallationError
ComponentUninstallationError
ComponentRepositoryUnavailable
ProjectNameValidationError
ProjectDataStoreError
ProjectExistsError
ProjectNotFoundError
ProjectOpenError
ProjectNotOpenError
ProjectOpenByOtherPeersError
CannotRemoveOpenProjectError
ProjectCloseError
LanguageServerError
GlobalConfigurationAccessError
ProjectCreateError
LoggingServiceUnavailable
Types
There are a number of types that are used only within the project server's protocol messages. These are specified here.
ProjectMetadata
This type represents information about a project.
Format
interface ProjectMetadata {
/**
* The name of the project.
*/
name: String;
/**
* The namespace of the project.
*/
namespace: String;
/**
* The project id.
*/
id: UUID;
/**
* Enso Engine version to use for the project, represented by a semver version
* string.
*
* If the edition associated with the project could not be resolved, the
* engine version may be missing.
*/
engineVersion?: String;
/**
* The project creation time.
*/
created: UTCDateTime;
/**
* The last opened datetime.
*/
lastOpened?: UTCDateTime;
}
MissingComponentAction
This type specifies what action should be taken if a component required to complete an operation is missing.
Fail
will make the operation fail if any components are missing.Install
will try to install any missing components, unless they are marked as broken.ForceInstallBroken
will try to install all missing components, even if some of them are marked as broken.
Format
type MissingComponentAction = Fail | Install | ForceInstallBroken;
ProgressUnit
This type specifies the unit of progress updates related to a task.
Format
type ProgressUnit = Bytes | Other;
EngineVersion
This type represents an installed or available engine version.
Format
interface EngineVersion {
/** Semver string of engine version. */
version: String;
/** Specifies if that version is marked as broken. */
markedAsBroken: bool;
}
RunningState
This type represents information about a state of the (potentially) running project.
Format
interface RunningStatus {
/**
* If true, the project is open and (still) accepting new connections.
* False, if the project is currently shutdown.
*/
open: bool;
/**
* If true, the project is currently running but in a soft shutdown mode.
* False, if the project is either not running or it is not shutting down.
*/
shuttingDown: bool;
}
Project Management Operations
The primary responsibility of the project managers is to allow users to manage their projects.
project/open
This message requests that the project manager open a specified project. This operation also includes spawning an instance of the language server open on the specified project.
To open a project, an engine version that is specified in project settings needs
to be installed. If missingComponentAction
is set to Install
or
ForceInstallBroken
, this action will install any missing components,
otherwise, an error will be reported if a component is missing. A typical usage
scenario may consist of first trying to open the project without installing
missing components. If that fails with the MissingComponentError
, the client
can ask the user if they want to install the missing components and re-attempt
the action.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
{
projectId: UUID;
/**
* Specifies how to handle missing components.
*
* If not provided, defaults to `Fail`.
*/
missingComponentAction?: MissingComponentAction;
}
Result
{
/**
* The version of the started language server represented by a semver version
* string.
*/
engineVersion: String;
/**
* The endpoint used for JSON-RPC protocol.
*/
languageServerJsonAddress: IPWithSocket;
/**
* The endpoint used for binary protocol.
*/
languageServerBinaryAddress: IPWithSocket;
// The name of the project as it is opened.
projectName: String;
// The normalized name of the project.
projectNormalizedName: String;
// The namespace of the project.
projectNamespace: String;
}
Errors
ProjectNotFoundError
to signal that the project doesn't exist.ProjectDataStoreError
to signal problems with underlying data store.ProjectOpenError
to signal failures during server boot.MissingComponentError
to signal that the component required to open the project was missing (only in casemissingComponentAction
was set tofail
).BrokenComponentError
to signal that the component required to open the project was being installed but is marked as broken (only in casemissingComponentAction
was set toinstall
).ComponentInstallationError
to signal that the installation of a missing component has failed.ProjectManagerUpgradeRequired
to signal that the requested engine version requires a more recent project manager, so an upgrade has to be performed before continuing.
project/close
This message requests that the project manager close a specified project. This operation includes shutting down the language server gracefully so that it can persist state to disk as needed.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface ProjectCloseRequest {
projectId: UUID;
}
Result
{
}
Errors
ProjectNotFoundError
to signal that the project doesn't exist.ProjectDataStoreError
to signal problems with underlying data store.ProjectCloseError
to signal failures that occurred during language server stoppage.ProjectNotOpenError
to signal cannot close a project that is not open.ProjectOpenByOtherPeersError
to signal that cannot close a project that is open by other clients.
project/list
This message requests that the project manager lists all user's projects. The list of projects is sorted by the open time.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface ProjectListRequest {
numberOfProjects?: Int;
}
Result
interface ProjectListResponse {
projects: [ProjectMetadata];
}
Errors
ProjectDataStoreError
to signal problems with underlying data store.GlobalConfigurationAccessError
to signal that the global configuration file could not be accessed or parsed.
project/create
This message requests the creation of a new project.
To create a project, an engine version associated with it needs to be installed.
Depending on missingComponentAction
, any components required to complete the
operation are missing will be installed or a failure will be reported.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface ProjectCreateRequest {
/** Name of the project to create. */
name: String;
/** The name of the project template to create. */
projectTemplate?: String;
/**
* Enso Engine version to use for the project.
*
* Possible values are:
* - a semver version string identifying an Enso engine version,
* - `default` to use the current default.
*
* The field is optional - if it is missing, it is treated as `default`.
*/
version?: String;
/**
* Specifies how to handle missing components.
*
* If not provided, defaults to `Fail`.
*/
missingComponentAction?: MissingComponentAction;
}
Result
interface ProjectCreateResponse {
projectId: UUID;
projectName: string;
}
Errors
ProjectNameValidationError
to signal validation failures.ProjectDataStoreError
to signal problems with underlying data store.ProjectExistsError
to signal that the project already exists.MissingComponentError
to signal that the component required to create the project was missing (only in casemissingComponentAction
was set tofail
).BrokenComponentError
to signal that the component required to create the project was being installed but is marked as broken (only in casemissingComponentAction
was set toinstall
).ComponentInstallationError
to signal that the installation of a missing component has failed.ProjectManagerUpgradeRequired
to signal that the requested engine version requires a more recent project manager, so an upgrade has to be performed before continuing.
project/rename
This message requests the renaming of a project.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface ProjectRenameRequest {
projectId: UUID;
name: String;
}
Result
null
Errors
ProjectNameValidationError
to signal validation failures.ProjectDataStoreError
to signal problems with underlying data store.ProjectExistsError
to signal that the project with the provided name already exists.ServiceError
to signal that the the operation timed out.LanguageServerError
to signal generic language server failures.
project/delete
This message requests the deletion of a project.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface ProjectDeleteRequest {
projectId: UUID;
}
Result
{
}
Errors
ProjectDataStoreError
to signal problems with underlying data store.ProjectNotFoundError
to signal that the project doesn't exist.CannotRemoveOpenProjectError
to signal that the project cannot be removed, because is open by at least one user.
project/listSample
This request lists the sample projects that are available to the user.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface ProjectListSampleRequest {
numProjects: Int;
}
Result
interface ProjectListSampleResponse {
projects: [ProjectMetadata];
}
Errors
TBC
project/status
This request asks for the current state of the project.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface ProjectStatusRequest {
projectID: UUID;
}
Result
interface ProjectStatusResponse {
status: RunningStatus;
}
Action Progress Reporting
Some actions, especially those related to installation of new components may take a long time (for example because big packages need to be downloaded).
The protocol includes notifications tied to such actions that can be used to display progress bars.
Each task has a lifecycle of being initialized with a task/started
notification (which contains a UUID that identifies that task), being updated
with task/progress-update
and finalized with task/finished
. task/finished
may include an error (but please note that regardless of the task-related error,
the error will also be reported for the original request associated with the
task, for example as ComponentInstallationError
returned for the
project/open
request that triggered the installation).
Tasks are sent while an operation is being processed and a single operation may consist of several (sub)tasks.
For example, when opening a project the flow may be following:
project/open
request sent to the server- notification
task/started
(downloading the archive) - multiple notifications
task/progress-update
related to that task - notification
task/finished
- notification
task/started
(extracting the archive) - multiple notifications
task/progress-update
related to that task - notification
task/finished
- reply to the original
project/open
request
All task progress updates happen within the response/request flow (up to a possible reordering of messages).
task/started
Indicates that a long running task has been started.
Currently only used when components are being installed to show installation progress.
- Type: Notification
- Direction: Server -> Client
- Connection: Protocol
- Visibility: Public
Parameters
interface TaskStartNotification {
/**
* Unique identifier of the task, used to correlate progress updates and the
* finished notification.
*/
taskId: UUID;
/**
* Name of the operation this task is related to, for example
* `project/open`.
*/
relatedOperation: String;
/** Unit in which progress of this task is measured. */
unit: ProgressUnit;
/**
* Indicates total expected progress.
*
* May be missing, as it is not always known, for example when downloading a
* file of unknown size or waiting on a lock.
*/
total?: Long;
}
task/progress-update
Indicates a progress update for a specific task.
- Type: Notification
- Direction: Server -> Client
- Connection: Protocol
- Visibility: Public
Parameters
interface TaskUpdateNotification {
taskId: UUID;
/** Optional message explaining current status of the task. */
message?: String;
/** Indicates amount of progress, for example: count of processed bytes. */
done: Long;
}
task/finished
Indicates that a task has been finished, either successfully or with an error.
- Type: Notification
- Direction: Server -> Client
- Connection: Protocol
- Visibility: Public
Parameters
interface TaskFinishedNotification {
taskId: UUID;
/** Optional message informing about task completion. */
message?: String;
/** Specifies if the task succeeded or failed. */
success: bool;
}
Runtime Version Management
engine/list-installed
Lists engine versions currently installed.
Please note that the broken marks associated with each engine currently represent the state at the moment of installation. As of now, if the broken mark has been added later, it is not updated automatically.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
null;
Result
interface EngineVersionListResponse {
/** List of installed engines. */
versions: [EngineVersion];
}
Errors
TBC
engine/list-available
Queries the repository to list all engine versions that are available to be installed.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
null;
Result
interface EngineVersionListResponse {
/** List of available engines. */
versions: [EngineVersion];
}
Errors
ComponentRepositoryUnavailable
to signal that the component repository could not be reached.
engine/install
Requests to install the specified engine version. If that version is already installed, it has no effect.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface EngineInstallRequest {
/** Semver string of engine version that should be installed. */
version: String;
/**
* Specifies whether the engine should be installed even if it is marked as
* broken.
*
* If not provided, defaults to `false`.
*/
forceInstallBroken?: bool;
}
Result
null;
Errors
BrokenComponentError
to signal that the requested engine version is marked as broken (only in caseforceInstallBroken
was set tofalse
).ComponentInstallationError
to signal that the installation of a missing component has failed.ProjectManagerUpgradeRequired
to signal that the requested engine version requires a more recent project manager, so an upgrade has to be performed before continuing.
engine/uninstall
Requests to uninstall the specified engine version.
If that version was not installed, it has no effect.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface EngineUninstallRequest {
/** Semver string of engine version that should be uninstalled. */
version: String;
}
Result
null;
Errors
ComponentUninstallationError
to signal that the component could not have been uninstalled.
Configuration Management
global-config/get
Gets a value from the global config.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface GlobalConfigGetRequest {
key: String;
}
Result
interface GlobalConfigGetResponse {
/**
* The value set in the config.
*
* The field may be missing if the requested value is not set in the config.
*/
value?: String;
}
Errors
GlobalConfigurationAccessError
to signal that the configuration file could not be accessed.
global-config/set
Sets a value in the global config.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface GlobalConfigSetRequest {
key: String;
value: String;
}
Result
null;
Errors
GlobalConfigurationAccessError
to signal that the configuration file could not be accessed.
global-config/delete
Deletes a value from the global config, or does nothing if it did not exist.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
interface GlobalConfigDeleteRequest {
key: String;
}
Result
null;
Errors
GlobalConfigurationAccessError
to signal that the configuration file could not be accessed.
Logging Service
logging-service/get-endpoint
Requests the endpoint for connecting to the logging service.
- Type: Request
- Direction: Client -> Server
- Connection: Protocol
- Visibility: Public
Parameters
null;
Result
interface LoggingServiceEndpointResponse {
uri: String;
}
Errors
LoggingServiceUnavailable
to signal that the logging service is unavailable.
Language Server Management
The project manager is also responsible for managing the language server. This means that it needs to be able to spawn the process, but also tell the process when to shut down.
A language server process is spawned within the project/open
call. That call
returns endpoints that the client can use to connect to the language server.
When project/close
is called, the language server is shutdown. Moreover,
between these two calls, the project manager sends heartbeat messages to the
language server to check if it is still running. In case that it has crashed, a
restart is attempted.
Errors
The project manager component has its own set of errors. This section is not a complete specification and will be updated as new errors are added.
Besides the required code
and message
fields, the errors may have a data
field which can store additional error-specific payload.
MissingComponentError
Signals that a component required to complete the action was missing, but the action did not ask for it to be automatically installed.
"error" : {
"code" : 4020,
"message" : "Engine 1.2.3 is required to complete the action but it is not installed."
}
BrokenComponentError
Signals that a component that was being installed is marked as broken, but the option to forcibly install broken components was not set.
This error may handled by warning the user about the broken version or suggesting to upgrade the project and asking to confirm using the broken version. If the user wants to ignore the warning, the operation can be reattempted with the option to forcibly install broken components.
"error" : {
"code" : 4021,
"message" : "Engine 1.2.3 is marked as broken."
}
ProjectManagerUpgradeRequired
Signals that installation of a missing compoment has been attempted, but the required engine version requires a newer version of project manager than what is currently running.
This error type includes the optional data
field which is an object with a
field minimumRequiredVersion
that is a semver string of the project manager
version that is required to complete the related action.
"error" : {
"code" : 4022,
"message" : "Project manager 1.2.3 is required to install the requested engine. Please upgrade.",
"payload": {
"minimumRequiredVersion": "1.2.3"
}
}
ComponentInstallationError
Signals that installation of a missing component has been attempted but it has failed.
"error" : {
"code" : 4023,
"message" : "A problem occurred when trying to find the release: Cannot find release `enso-1.2.3-not-published`."
}
ComponentUninstallationError
Signals that uninstallation of a component has failed.
"error" : {
"code" : 4024,
"message" : "The requested engine version is not installed."
}
ComponentRepositoryUnavailable
Signals that the repository is unavailable and could not be queried (usually caused by lack of internet connection).
"error" : {
"code" : 4025,
"message" : "Could not connect to github.com"
}
ProjectNameValidationError
Signals validation failures.
"error" : {
"code" : 4001,
"message" : "Cannot create project with empty name"
}
ProjectDataStoreError
Signals problems with underlying data store.
"error" : {
"code" : 4002,
"message" : "Cannot load project index"
}
ProjectExistsError
Signals that the project already exists.
"error" : {
"code" : 4003,
"message" : "Project with the provided name exists"
}
ProjectNotFoundError
Signals that the project doesn't exist.
"error" : {
"code" : 4004,
"message" : "Project with the provided id does not exist"
}
ProjectOpenError
Signals that the project cannot be open due to boot failures.
"error" : {
"code" : 4005,
"message" : "A boot failure."
}
ProjectNotOpenError
Signals that cannot close project that is not open.
"error" : {
"code" : 4006,
"message" : "Cannot close project that is not open"
}
ProjectOpenByOtherPeersError
Signals that cannot close a project that is open by other clients.
"error" : {
"code" : 4007,
"message" : "Cannot close project because it is open by other peers"
}
CannotRemoveOpenProjectError
Signals that cannot remove open project.
"error" : {
"code" : 4008,
"message" : "Cannot remove open project"
}
ProjectCloseError
Signals failures during shutdown of a server.
"error" : {
"code" : 4009,
"message" : "A shutdown failure."
}
LanguageServerError
Signals generic language server errors.
"error" : {
"code" : 4010,
"message" : "The language server is unresponsive"
}
GlobalConfigurationAccessError
Signals that the global configuration file could not be accessed or parsed.
"error" : {
"code" : 4011,
"message" : "The global configuration file is malformed."
}
ProjectCreateError
Signals that an error occurred when creating the project.
"error" : {
"code" : 4012,
"message" : "Could not create the project."
}
LoggingServiceUnavailable
Signals that the logging service is not available.
"error" : {
"code" : 4013,
"message" : "The logging service has failed to boot."
}