[platform] IJPL-148284 Move intellij.platform.ide.provisioner to platform

GitOrigin-RevId: f7b22ed30a78468e91c473c4d7b45176342452a0
This commit is contained in:
Yuriy Artamonov
2025-02-04 22:54:05 +00:00
committed by intellij-monorepo-bot
parent f8f3c82302
commit ca368aaeda
10 changed files with 203 additions and 3 deletions
@@ -7,6 +7,8 @@
<module value="com.intellij.modules.java-capable"/>
<module value="com.intellij.modules.python-core-capable"/> <!-- Python plugin can be installed -->
<module value="com.intellij.modules.python-in-non-pycharm-ide-capable"/> <!-- Enable Non-Pycharm-IDE support in Python plugin -->
<module value="com.intellij.platform.ide.provisioner"/>
<content>
<module name="intellij.platform.coverage"/>
<module name="intellij.platform.coverage.agent"/>
@@ -95,9 +95,6 @@ internal fun createModulesWithDependenciesAndAdditionalEdges(plugins: Collection
if (doesDependOnPluginAlias(module, ML_INLINE_ALIAS_ID)) {
moduleMap.get("intellij.ml.inline.completion")?.let { dependenciesCollector.add(it) }
}
if (doesDependOnPluginAlias(module, PROVISIONER_ALIAS_ID)) {
moduleMap.get("intellij.platform.ide.provisioner")?.let { dependenciesCollector.add(it) }
}
if (doesDependOnPluginAlias(module, PluginId.getId("org.jetbrains.completion.full.line"))) {
moduleMap.get("intellij.fullLine.core")?.let { dependenciesCollector.add(it) }
moduleMap.get("intellij.fullLine.local")?.let { dependenciesCollector.add(it) }
@@ -0,0 +1,37 @@
package com.intellij.platform.ide.provisioner
import com.intellij.platform.ide.provisioner.endpoint.ServiceEndpoint
data class ProvisionedServiceConfiguration(
/** Generic Key-Value map of service-specific properties. */
private val properties: Map<String, String>,
/** Endpoint descriptor in case the service involves a remote server. */
val endpoint: ServiceEndpoint?,
) {
operator fun get(key: String): String? = properties[key]
}
sealed interface ProvisionedServiceConfigurationResult {
/**
* Represents the successfully loaded state.
*/
sealed interface Success : ProvisionedServiceConfigurationResult {
data class ServiceProvisioned(val configuration: ProvisionedServiceConfiguration) : Success
data object ServiceNotProvisioned : Success
}
/**
* Denotes that the provisioner could not load the configuration of the service due to an error.
* Depending on the particular service, there may be different ways of treating this state.
* For example, the client may fall back to some predefined default configuration,
* or it may choose to prohibit the use of the corresponding IDE functionality altogether
* until a proper configuration becomes available.
*/
sealed interface Failure : ProvisionedServiceConfigurationResult {
val message: String
data class LoginRequired(override val message: String) : Failure
data class GenericError(override val message: String, val cause: Throwable? = null) : Failure
}
}
@@ -0,0 +1,19 @@
package com.intellij.platform.ide.provisioner
import kotlinx.coroutines.flow.Flow
/**
* Descriptor for a (potentially provisioned) piece of IDE functionality.
*/
interface ProvisionedServiceDescriptor {
/** Unique identifier for accessing the service endpoint using [ProvisionedServiceRegistry.getServiceById]. */
val id: String
/**
* The state of whether the service is provisioned (with its configuration in that case) or not.
*
* Note that it may take some time for the initial value to become available in the flow
* while the configuration is still loading.
*/
val configurationFlow: Flow<ProvisionedServiceConfigurationResult>
}
@@ -0,0 +1,23 @@
package com.intellij.platform.ide.provisioner
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
interface ProvisionedServiceRegistry {
/**
* Retrieves a [ProvisionedServiceDescriptor] by its [ProvisionedServiceDescriptor.id],
* or null if the provisioner doesn't recognize the ID.
* Note that a non-null result only means that the installed version of the provisioner plugin
* is aware and support the requested service; it doesn't mean that the service is available and/or enabled -
* this is what [ProvisionedServiceDescriptor.configurationFlow] is for.
*/
fun getServiceById(id: String): ProvisionedServiceDescriptor?
companion object {
fun getInstance(): ProvisionedServiceRegistry = ApplicationManager.getApplication().service()
}
}
internal class DefaultProvisionedServiceRegistry : ProvisionedServiceRegistry {
override fun getServiceById(id: String): ProvisionedServiceDescriptor? = null
}
@@ -0,0 +1,39 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.ide.provisioner
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
import com.intellij.openapi.util.NlsSafe
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.jetbrains.annotations.ApiStatus
import javax.swing.Icon
@ApiStatus.Internal
interface ProvisionerCompanyBrandingProvider{
val companyBranding: Flow<CompanyBranding>
@ApiStatus.Experimental
fun getCurrentEnterpriseState(): CompanyBranding
companion object {
fun getInstance(): ProvisionerCompanyBrandingProvider = ApplicationManager.getApplication().service()
}
}
internal class DefaultProvisionerCompanyBrandingProvider: ProvisionerCompanyBrandingProvider {
override val companyBranding = flowOf(CompanyBranding.NotProvisioned)
override fun getCurrentEnterpriseState(): CompanyBranding = CompanyBranding.NotProvisioned
}
sealed class CompanyBranding {
data object NotReady: CompanyBranding()
data class Provisioned(val info: EnterpriseInfo): CompanyBranding()
data object NotProvisioned: CompanyBranding()
}
data class EnterpriseInfo(
val logo: Icon,
val companyName: @NlsSafe String,
val browserUrl: String,
)
@@ -0,0 +1,31 @@
package com.intellij.platform.ide.provisioner.endpoint
data class AuthToken(
/**
* The map of HTTP request headers required for authenticating with the corresponding [ServiceEndpoint].
* Typically, it contains at least the `"Authorization"` credentials, but that's not guaranteed.
*/
val requestHeaders: Map<String, String>,
) {
@Deprecated("For backward compatibility, until TBE plugin is updated")
@Suppress("unused")
constructor(
tokenValue: String,
tokenSchema: String,
additionalHeaders: Map<String, String>,
) : this(requestHeaders = mapOf("Authorization" to "${tokenSchema} ${tokenValue}") + additionalHeaders)
}
sealed interface AuthTokenResult {
data class Success(val token: AuthToken) : AuthTokenResult
sealed interface Failure : AuthTokenResult {
val message: String
data class Timeout(override val message: String) : Failure
data class NetworkError(override val message: String) : Failure
data class LoginRequired(override val message: String) : Failure
data class ValidationError(override val message: String) : Failure
data class GenericError(override val message: String, val cause: Throwable? = null) : Failure
}
}
@@ -0,0 +1,44 @@
package com.intellij.platform.ide.provisioner.endpoint
import kotlinx.coroutines.flow.Flow
/**
* Describes a server endpoint for the provisioned service.
*/
interface ServiceEndpoint {
/** The URL of the service endpoint. */
val serverUrl: String
/**
* The current [token][AuthTokenResult] required to access the [server][serverUrl].
* The implementation is responsible for refreshing the token, so that the latest token value
* available in the flow is always usable (unless there's an [AuthTokenResult.Failure]).
*
* Note that an [AuthTokenResult.Success] doesn't guarantee that the token is going to stay
* valid up until its expiration time.
* The client code should still be able to handle an authorization error properly,
* and the [reportAuthFailure] method can be helpful in facilitating that.
*/
val authTokenFlow: Flow<AuthTokenResult>
/**
* The client is advised to call this method as part of graceful error handling if a request
* to the endpoint fails because of an authentication failure ("401 Unauthorized").
*
* The token may become invalid due to an external change; for instance, the SSO provider
* may forcibly log the user out. An event like that may go unnoticed by the provisioner,
* and the new state may not be reflected in the [authTokenFlow] automatically.
*
* By calling this method, the client notifies the provisioner that the token is no more
* usable. The provisioner then makes the best effort to revalidate the token, which may
* hopefully result in a new token state (probably an [AuthTokenResult.Failure.LoginRequired])
* being pushed eventually through the [authTokenFlow].
*
* Note that the contract of this method should indeed be only treated as "best effort".
* The client must not rely on a new token state becoming available in the [authTokenFlow]
* right after calling this method, and should design the interaction with the user accordingly.
* In this sense, the default "noop" way of how the provisioner could implement this method
* is perfectly fine.
*/
fun reportAuthFailure(authToken: AuthToken) {}
}
@@ -1847,6 +1847,11 @@
<actionGroupCustomization
implementation="com.intellij.openapi.wm.impl.headertoolbar.MainToolbarActionGroupCustomization" />
<applicationService serviceInterface="com.intellij.platform.ide.provisioner.ProvisionedServiceRegistry"
serviceImplementation="com.intellij.platform.ide.provisioner.DefaultProvisionedServiceRegistry"/>
<applicationService serviceInterface="com.intellij.platform.ide.provisioner.ProvisionerCompanyBrandingProvider"
serviceImplementation="com.intellij.platform.ide.provisioner.DefaultProvisionerCompanyBrandingProvider"/>
</extensions>
<applicationListeners>
@@ -2,6 +2,9 @@
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<module value="com.intellij.modules.pycharm.community"/>
<module value="com.intellij.modules.python-core-capable"/>
<!-- for compatibility -->
<module value="com.intellij.platform.ide.provisioner"/>
<!-- for compatibility -->
<content>
<module name="intellij.platform.ide.newUiOnboarding"/>