Skip to content

Commit d1b64dc

Browse files
author
Andrea Falzetti
committed
feat(jb): observe ports status and send notification
1 parent 6972cd5 commit d1b64dc

File tree

1 file changed

+107
-0
lines changed
  • components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote

1 file changed

+107
-0
lines changed

components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package io.gitpod.jetbrains.remote
66

7+
import com.intellij.ide.BrowserUtil
78
import com.intellij.ide.plugins.PluginManagerCore
89
import com.intellij.notification.NotificationAction
910
import com.intellij.notification.NotificationGroupManager
@@ -23,6 +24,10 @@ import io.gitpod.jetbrains.remote.utils.Retrier.retry
2324
import io.gitpod.supervisor.api.*
2425
import io.gitpod.supervisor.api.Info.WorkspaceInfoResponse
2526
import io.gitpod.supervisor.api.Notification.*
27+
import io.gitpod.supervisor.api.Status.OnPortExposedAction
28+
import io.gitpod.supervisor.api.Status.PortsStatus
29+
import io.gitpod.supervisor.api.Status.PortsStatusRequest
30+
import io.gitpod.supervisor.api.Status.PortsStatusResponse
2631
import io.grpc.ManagedChannel
2732
import io.grpc.ManagedChannelBuilder
2833
import io.grpc.stub.ClientCallStreamObserver
@@ -46,6 +51,7 @@ import java.util.concurrent.CancellationException
4651
import java.util.concurrent.CompletableFuture
4752
import javax.websocket.DeploymentException
4853

54+
4955
@Service
5056
class GitpodManager : Disposable {
5157

@@ -199,6 +205,107 @@ class GitpodManager : Disposable {
199205
}
200206
}
201207

208+
private val portsObserveJob = GlobalScope.launch {
209+
if (application.isHeadlessEnvironment) {
210+
return@launch
211+
}
212+
213+
val ignorePorts = listOf(5990, 6942)
214+
val portsStatus = hashMapOf<Int, PortsStatus>()
215+
216+
val status = StatusServiceGrpc.newStub(supervisorChannel)
217+
//while (isActive) {
218+
try {
219+
val f = CompletableFuture<Void>()
220+
status.portsStatus(
221+
PortsStatusRequest.newBuilder().setObserve(true).build(),
222+
object : ClientResponseObserver<PortsStatusRequest, PortsStatusResponse> {
223+
override fun beforeStart(requestStream: ClientCallStreamObserver<PortsStatusRequest>) {
224+
println("[Andrea]: beforeStart")
225+
// TODO(ak): actually should be bound to cancellation of notifications job
226+
lifetime.onTerminationOrNow {
227+
requestStream.cancel(null, null)
228+
}
229+
}
230+
231+
override fun onNext(ps: PortsStatusResponse) {
232+
for (port in ps.portsList) {
233+
if (ignorePorts.contains(port.localPort)) {
234+
continue
235+
}
236+
237+
if (!portsStatus.containsKey(port.localPort)) {
238+
portsStatus[port.localPort] = port
239+
continue
240+
}
241+
242+
println("DEBUG[${port.localPort}] -> before hasExposed(${portsStatus[port.localPort]?.hasExposed().toString()}) -> now hasExposed(${port.hasExposed()})")
243+
244+
if (!portsStatus[port.localPort]?.hasExposed()!! && port.hasExposed()) {
245+
if (port.exposed.onExposed.number == OnPortExposedAction.ignore_VALUE) {
246+
continue
247+
}
248+
249+
if (port.exposed.onExposed.number == OnPortExposedAction.open_browser_VALUE) {
250+
BrowserUtil.browse(port.exposed.url)
251+
continue
252+
}
253+
254+
if (port.exposed.onExposed.number == OnPortExposedAction.open_preview_VALUE) {
255+
BrowserUtil.browse(port.exposed.url)
256+
continue
257+
}
258+
259+
if (port.served && !port.exposed.url.isNullOrEmpty()) {
260+
println("Show port notification‼️‼️ ${port.localPort}")
261+
val message =
262+
"Your application running on port " + port.localPort + " is available."
263+
println(message)
264+
val notification =
265+
notificationGroup.createNotification(message, NotificationType.INFORMATION)
266+
// TODO(andreafalzetti): add analytics event
267+
val lambda = { BrowserUtil.browse(port.exposed.url) }
268+
val action = NotificationAction.createSimpleExpiring("Open in browser", lambda)
269+
notification.addAction(action)
270+
notification.notify(null)
271+
}
272+
//
273+
}
274+
275+
}
276+
277+
}
278+
279+
override fun onError(t: Throwable) {
280+
println("[Andrea]: onError")
281+
println(t)
282+
f.completeExceptionally(t)
283+
}
284+
285+
override fun onCompleted() {
286+
f.complete(null)
287+
}
288+
})
289+
} catch (t: Throwable) {
290+
if (t is CancellationException) {
291+
throw t
292+
}
293+
thisLogger().error("gitpod: failed to stream ports status: ", t)
294+
}
295+
//println("**** DEBUG **** ${portsStatus.size}")
296+
//portsStatus.forEach { ps ->
297+
// println("${ps.key} : ${ps.value.exposed.url}")
298+
//}
299+
// println("***************")
300+
// delay(1000)
301+
//}
302+
}
303+
init {
304+
lifetime.onTerminationOrNow {
305+
portsObserveJob.cancel()
306+
}
307+
}
308+
202309
val pendingInfo = CompletableFuture<WorkspaceInfoResponse>()
203310
private val infoJob = GlobalScope.launch {
204311
if (application.isHeadlessEnvironment) {

0 commit comments

Comments
 (0)