Skip to content

Commit 10dc9fb

Browse files
authored
Add graphql-ws support (#60)
* Add graphql-js support * update apiDump
1 parent d052a82 commit 10dc9fb

File tree

8 files changed

+490
-101
lines changed

8 files changed

+490
-101
lines changed

apollo-execution-ktor/api/apollo-execution-ktor.api

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@ public final class com/apollographql/execution/ktor/MainKt {
33
public static synthetic fun apolloModule$default (Lio/ktor/server/application/Application;Lcom/apollographql/apollo/execution/ExecutableSchema;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
44
public static final fun apolloSandboxModule (Lio/ktor/server/application/Application;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
55
public static synthetic fun apolloSandboxModule$default (Lio/ktor/server/application/Application;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
6-
public static final fun apolloSubscriptionModule (Lio/ktor/server/application/Application;Lcom/apollographql/apollo/execution/ExecutableSchema;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
7-
public static synthetic fun apolloSubscriptionModule$default (Lio/ktor/server/application/Application;Lcom/apollographql/apollo/execution/ExecutableSchema;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
6+
public static final fun apolloSubscriptionModule (Lio/ktor/server/application/Application;Lcom/apollographql/apollo/execution/ExecutableSchema;Ljava/lang/String;Lcom/apollographql/execution/ktor/WsProtocol;Lkotlin/jvm/functions/Function1;)V
7+
public static synthetic fun apolloSubscriptionModule$default (Lio/ktor/server/application/Application;Lcom/apollographql/apollo/execution/ExecutableSchema;Ljava/lang/String;Lcom/apollographql/execution/ktor/WsProtocol;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
88
public static final fun parseAsGraphQLRequest (Lio/ktor/server/request/ApplicationRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
99
public static final fun respondGraphQL (Lio/ktor/server/application/ApplicationCall;Lcom/apollographql/apollo/execution/ExecutableSchema;Lcom/apollographql/apollo/api/ExecutionContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1010
public static synthetic fun respondGraphQL$default (Lio/ktor/server/application/ApplicationCall;Lcom/apollographql/apollo/execution/ExecutableSchema;Lcom/apollographql/apollo/api/ExecutionContext;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
1111
}
1212

13+
public final class com/apollographql/execution/ktor/WsProtocol : java/lang/Enum {
14+
public static final field GraphqlWS Lcom/apollographql/execution/ktor/WsProtocol;
15+
public static final field Legacy Lcom/apollographql/execution/ktor/WsProtocol;
16+
public static fun getEntries ()Lkotlin/enums/EnumEntries;
17+
public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/execution/ktor/WsProtocol;
18+
public static fun values ()[Lcom/apollographql/execution/ktor/WsProtocol;
19+
}
20+

apollo-execution-ktor/src/commonMain/kotlin/com/apollographql/execution/ktor/main.kt

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import com.apollographql.apollo.execution.GraphQLResponse
77
import com.apollographql.apollo.execution.parseAsGraphQLRequest
88
import com.apollographql.execution.*
99
import com.apollographql.execution.websocket.ConnectionInitAck
10+
import com.apollographql.execution.websocket.GraphQLWsWebSocketHandler
1011
import com.apollographql.execution.websocket.SubscriptionWebSocketHandler
1112
import com.apollographql.execution.websocket.WebSocketBinaryMessage
1213
import com.apollographql.execution.websocket.WebSocketTextMessage
14+
import com.apollographql.execution.websocket.WsConnectionInitAck
1315
import io.ktor.http.*
1416
import io.ktor.http.content.*
1517
import io.ktor.server.application.*
@@ -99,30 +101,59 @@ fun Application.apolloModule(
99101
}
100102
}
101103

104+
enum class WsProtocol {
105+
GraphqlWS,
106+
Legacy
107+
}
102108
fun Application.apolloSubscriptionModule(
103109
executableSchema: ExecutableSchema,
104110
path: String = "/subscription",
111+
protocol: WsProtocol = WsProtocol.GraphqlWS,
105112
executionContext: (ApplicationRequest) -> ExecutionContext = { ExecutionContext.Empty }
106113
) {
107114
install(WebSockets)
108115

109116
routing {
110-
webSocket(path, "graphql-ws") {
117+
val transport = when (protocol) {
118+
WsProtocol.GraphqlWS -> "graphql-transport-ws"
119+
WsProtocol.Legacy -> "graphql-ws"
120+
}
121+
webSocket(path, transport) {
111122
coroutineScope {
112-
val handler = SubscriptionWebSocketHandler(
113-
executableSchema = executableSchema,
114-
scope = this,
115-
executionContext = executionContext(call.request),
116-
sendMessage = {
117-
when (it) {
118-
is WebSocketBinaryMessage -> send(Frame.Binary(true, it.data))
119-
is WebSocketTextMessage -> send(Frame.Text(it.data))
120-
}
121-
},
122-
connectionInitHandler = {
123-
ConnectionInitAck
123+
val handler = when(protocol) {
124+
WsProtocol.GraphqlWS -> {
125+
GraphQLWsWebSocketHandler(
126+
executableSchema = executableSchema,
127+
scope = this,
128+
executionContext = executionContext(call.request),
129+
sendMessage = {
130+
when (it) {
131+
is WebSocketBinaryMessage -> send(Frame.Binary(true, it.data))
132+
is WebSocketTextMessage -> send(Frame.Text(it.data))
133+
}
134+
},
135+
connectionInitHandler = {
136+
WsConnectionInitAck
137+
}
138+
)
124139
}
125-
)
140+
WsProtocol.Legacy -> {
141+
SubscriptionWebSocketHandler(
142+
executableSchema = executableSchema,
143+
scope = this,
144+
executionContext = executionContext(call.request),
145+
sendMessage = {
146+
when (it) {
147+
is WebSocketBinaryMessage -> send(Frame.Binary(true, it.data))
148+
is WebSocketTextMessage -> send(Frame.Text(it.data))
149+
}
150+
},
151+
connectionInitHandler = {
152+
ConnectionInitAck
153+
}
154+
)
155+
}
156+
}
126157

127158
for (frame in incoming) {
128159
if (frame !is Frame.Text) {

apollo-execution-runtime/api/apollo-execution-runtime.api

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,18 @@ public final class com/apollographql/execution/websocket/ConnectionInitError : c
6262
public abstract interface class com/apollographql/execution/websocket/ConnectionInitResult {
6363
}
6464

65-
public final class com/apollographql/execution/websocket/SubscriptionWebSocketHandler : com/apollographql/execution/websocket/WebSocketHandler {
65+
public final class com/apollographql/execution/websocket/GraphQLWsWebSocketHandler : com/apollographql/execution/websocket/WebSocketHandler {
6666
public fun <init> (Lcom/apollographql/apollo/execution/ExecutableSchema;Lkotlinx/coroutines/CoroutineScope;Lcom/apollographql/apollo/api/ExecutionContext;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V
6767
public synthetic fun <init> (Lcom/apollographql/apollo/execution/ExecutableSchema;Lkotlinx/coroutines/CoroutineScope;Lcom/apollographql/apollo/api/ExecutionContext;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
6868
public final fun close ()V
6969
public fun handleMessage (Lcom/apollographql/execution/websocket/WebSocketMessage;)V
7070
}
7171

72-
public final class com/apollographql/execution/websocket/SubscriptionWebSocketHandlerKt {
73-
public static final fun subscriptionId (Lcom/apollographql/apollo/api/ExecutionContext;)Ljava/lang/String;
72+
public final class com/apollographql/execution/websocket/SubscriptionWebSocketHandler : com/apollographql/execution/websocket/WebSocketHandler {
73+
public fun <init> (Lcom/apollographql/apollo/execution/ExecutableSchema;Lkotlinx/coroutines/CoroutineScope;Lcom/apollographql/apollo/api/ExecutionContext;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V
74+
public synthetic fun <init> (Lcom/apollographql/apollo/execution/ExecutableSchema;Lkotlinx/coroutines/CoroutineScope;Lcom/apollographql/apollo/api/ExecutionContext;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
75+
public final fun close ()V
76+
public fun handleMessage (Lcom/apollographql/execution/websocket/WebSocketMessage;)V
7477
}
7578

7679
public final class com/apollographql/execution/websocket/WebSocketBinaryMessage : com/apollographql/execution/websocket/WebSocketMessage {
@@ -90,3 +93,20 @@ public final class com/apollographql/execution/websocket/WebSocketTextMessage :
9093
public final fun getData ()Ljava/lang/String;
9194
}
9295

96+
public final class com/apollographql/execution/websocket/WsConnectionInitAck : com/apollographql/execution/websocket/WsConnectionInitResult {
97+
public static final field INSTANCE Lcom/apollographql/execution/websocket/WsConnectionInitAck;
98+
public fun equals (Ljava/lang/Object;)Z
99+
public fun hashCode ()I
100+
public fun toString ()Ljava/lang/String;
101+
}
102+
103+
public final class com/apollographql/execution/websocket/WsConnectionInitError : com/apollographql/execution/websocket/WsConnectionInitResult {
104+
public fun <init> ()V
105+
public fun <init> (Lcom/apollographql/apollo/api/Optional;)V
106+
public synthetic fun <init> (Lcom/apollographql/apollo/api/Optional;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
107+
public final fun getPayload ()Lcom/apollographql/apollo/api/Optional;
108+
}
109+
110+
public abstract interface class com/apollographql/execution/websocket/WsConnectionInitResult {
111+
}
112+

apollo-execution-runtime/api/apollo-execution-runtime.klib.api

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,22 @@ sealed interface com.apollographql.execution.websocket/ConnectionInitResult // c
5555

5656
sealed interface com.apollographql.execution.websocket/WebSocketMessage // com.apollographql.execution.websocket/WebSocketMessage|null[0]
5757

58+
sealed interface com.apollographql.execution.websocket/WsConnectionInitResult // com.apollographql.execution.websocket/WsConnectionInitResult|null[0]
59+
5860
final class com.apollographql.execution.websocket/ConnectionInitError : com.apollographql.execution.websocket/ConnectionInitResult { // com.apollographql.execution.websocket/ConnectionInitError|null[0]
5961
constructor <init>(com.apollographql.apollo.api/Optional<kotlin/Any?> = ...) // com.apollographql.execution.websocket/ConnectionInitError.<init>|<init>(com.apollographql.apollo.api.Optional<kotlin.Any?>){}[0]
6062

6163
final val payload // com.apollographql.execution.websocket/ConnectionInitError.payload|{}payload[0]
6264
final fun <get-payload>(): com.apollographql.apollo.api/Optional<kotlin/Any?> // com.apollographql.execution.websocket/ConnectionInitError.payload.<get-payload>|<get-payload>(){}[0]
6365
}
6466

67+
final class com.apollographql.execution.websocket/GraphQLWsWebSocketHandler : com.apollographql.execution.websocket/WebSocketHandler { // com.apollographql.execution.websocket/GraphQLWsWebSocketHandler|null[0]
68+
constructor <init>(com.apollographql.apollo.execution/ExecutableSchema, kotlinx.coroutines/CoroutineScope, com.apollographql.apollo.api/ExecutionContext, kotlin.coroutines/SuspendFunction1<com.apollographql.execution.websocket/WebSocketMessage, kotlin/Unit>, kotlin.coroutines/SuspendFunction1<kotlin/Any?, com.apollographql.execution.websocket/WsConnectionInitResult> = ...) // com.apollographql.execution.websocket/GraphQLWsWebSocketHandler.<init>|<init>(com.apollographql.apollo.execution.ExecutableSchema;kotlinx.coroutines.CoroutineScope;com.apollographql.apollo.api.ExecutionContext;kotlin.coroutines.SuspendFunction1<com.apollographql.execution.websocket.WebSocketMessage,kotlin.Unit>;kotlin.coroutines.SuspendFunction1<kotlin.Any?,com.apollographql.execution.websocket.WsConnectionInitResult>){}[0]
69+
70+
final fun close() // com.apollographql.execution.websocket/GraphQLWsWebSocketHandler.close|close(){}[0]
71+
final fun handleMessage(com.apollographql.execution.websocket/WebSocketMessage) // com.apollographql.execution.websocket/GraphQLWsWebSocketHandler.handleMessage|handleMessage(com.apollographql.execution.websocket.WebSocketMessage){}[0]
72+
}
73+
6574
final class com.apollographql.execution.websocket/SubscriptionWebSocketHandler : com.apollographql.execution.websocket/WebSocketHandler { // com.apollographql.execution.websocket/SubscriptionWebSocketHandler|null[0]
6675
constructor <init>(com.apollographql.apollo.execution/ExecutableSchema, kotlinx.coroutines/CoroutineScope, com.apollographql.apollo.api/ExecutionContext, kotlin.coroutines/SuspendFunction1<com.apollographql.execution.websocket/WebSocketMessage, kotlin/Unit>, kotlin.coroutines/SuspendFunction1<kotlin/Any?, com.apollographql.execution.websocket/ConnectionInitResult> = ...) // com.apollographql.execution.websocket/SubscriptionWebSocketHandler.<init>|<init>(com.apollographql.apollo.execution.ExecutableSchema;kotlinx.coroutines.CoroutineScope;com.apollographql.apollo.api.ExecutionContext;kotlin.coroutines.SuspendFunction1<com.apollographql.execution.websocket.WebSocketMessage,kotlin.Unit>;kotlin.coroutines.SuspendFunction1<kotlin.Any?,com.apollographql.execution.websocket.ConnectionInitResult>){}[0]
6776

@@ -83,6 +92,13 @@ final class com.apollographql.execution.websocket/WebSocketTextMessage : com.apo
8392
final fun <get-data>(): kotlin/String // com.apollographql.execution.websocket/WebSocketTextMessage.data.<get-data>|<get-data>(){}[0]
8493
}
8594

95+
final class com.apollographql.execution.websocket/WsConnectionInitError : com.apollographql.execution.websocket/WsConnectionInitResult { // com.apollographql.execution.websocket/WsConnectionInitError|null[0]
96+
constructor <init>(com.apollographql.apollo.api/Optional<kotlin/Any?> = ...) // com.apollographql.execution.websocket/WsConnectionInitError.<init>|<init>(com.apollographql.apollo.api.Optional<kotlin.Any?>){}[0]
97+
98+
final val payload // com.apollographql.execution.websocket/WsConnectionInitError.payload|{}payload[0]
99+
final fun <get-payload>(): com.apollographql.apollo.api/Optional<kotlin/Any?> // com.apollographql.execution.websocket/WsConnectionInitError.payload.<get-payload>|<get-payload>(){}[0]
100+
}
101+
86102
final class com.apollographql.execution/CompositeResolverBuilder { // com.apollographql.execution/CompositeResolverBuilder|null[0]
87103
constructor <init>() // com.apollographql.execution/CompositeResolverBuilder.<init>|<init>(){}[0]
88104

@@ -103,6 +119,11 @@ final object com.apollographql.execution.websocket/ConnectionInitAck : com.apoll
103119
final fun toString(): kotlin/String // com.apollographql.execution.websocket/ConnectionInitAck.toString|toString(){}[0]
104120
}
105121

106-
final fun (com.apollographql.apollo.api/ExecutionContext).com.apollographql.execution.websocket/subscriptionId(): kotlin/String // com.apollographql.execution.websocket/subscriptionId|subscriptionId@com.apollographql.apollo.api.ExecutionContext(){}[0]
122+
final object com.apollographql.execution.websocket/WsConnectionInitAck : com.apollographql.execution.websocket/WsConnectionInitResult { // com.apollographql.execution.websocket/WsConnectionInitAck|null[0]
123+
final fun equals(kotlin/Any?): kotlin/Boolean // com.apollographql.execution.websocket/WsConnectionInitAck.equals|equals(kotlin.Any?){}[0]
124+
final fun hashCode(): kotlin/Int // com.apollographql.execution.websocket/WsConnectionInitAck.hashCode|hashCode(){}[0]
125+
final fun toString(): kotlin/String // com.apollographql.execution.websocket/WsConnectionInitAck.toString|toString(){}[0]
126+
}
127+
107128
final fun (com.apollographql.apollo.execution/ExecutableSchema.Builder).com.apollographql.execution/compositeResolver(kotlin/Function1<com.apollographql.execution/CompositeResolverBuilder, kotlin/Unit>): com.apollographql.apollo.execution/ExecutableSchema.Builder // com.apollographql.execution/compositeResolver|compositeResolver@com.apollographql.apollo.execution.ExecutableSchema.Builder(kotlin.Function1<com.apollographql.execution.CompositeResolverBuilder,kotlin.Unit>){}[0]
108129
final fun com.apollographql.execution/sandboxHtml(kotlin/String, kotlin/String): kotlin/String // com.apollographql.execution/sandboxHtml|sandboxHtml(kotlin.String;kotlin.String){}[0]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.apollographql.execution
2+
3+
import com.apollographql.apollo.api.Error
4+
import com.apollographql.apollo.api.json.BufferedSinkJsonWriter
5+
import com.apollographql.apollo.api.json.JsonWriter
6+
import com.apollographql.apollo.api.json.writeAny
7+
import com.apollographql.apollo.api.json.writeArray
8+
import com.apollographql.apollo.api.json.writeObject
9+
import okio.BufferedSink
10+
import okio.Sink
11+
import okio.buffer
12+
13+
internal fun JsonWriter.writeError(error: Error) {
14+
writeObject {
15+
name("message")
16+
value(error.message)
17+
if (error.locations != null) {
18+
name("locations")
19+
writeArray {
20+
error.locations!!.forEach {
21+
writeObject {
22+
name("line")
23+
value(it.line)
24+
name("column")
25+
value(it.column)
26+
}
27+
}
28+
}
29+
}
30+
if (error.path != null) {
31+
name("path")
32+
writeArray {
33+
error.path!!.forEach {
34+
when (it) {
35+
is Int -> value(it)
36+
is String -> value(it)
37+
else -> error("path can only contain Int and Double (found '${it::class.simpleName}')")
38+
}
39+
}
40+
}
41+
}
42+
if (error.extensions != null) {
43+
name("extensions")
44+
writeObject {
45+
error.extensions!!.entries.forEach {
46+
name(it.key)
47+
writeAny(it.value)
48+
}
49+
}
50+
}
51+
}
52+
}
53+
54+
internal fun Sink.jsonWriter(): JsonWriter = BufferedSinkJsonWriter(if (this is BufferedSink) this else this.buffer())

0 commit comments

Comments
 (0)