Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript #3208

Merged
merged 65 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
5fde3e7
Initial JS support with TODOs
Jul 2, 2021
9b49bbc
configureMppTestsDefaults
Jul 9, 2021
45f83ac
Merge remote-tracking branch 'origin/dev-3.x' into js
Jul 9, 2021
2f2af42
Progress
Jul 9, 2021
61bd1ba
Merge remote-tracking branch 'origin/dev-3.x' into js
Jul 14, 2021
625c498
Merge remote-tracking branch 'origin/dev-3.x' into js
Jul 15, 2021
67ffa7e
Merge remote-tracking branch 'origin/dev-3.x' into js
Jul 16, 2021
d2c1715
okio 3.0.0-alpha.8
Jul 16, 2021
05aecfd
Merge remote-tracking branch 'origin/dev-3.x' into js
Jul 19, 2021
350cdb5
Updates
Jul 19, 2021
ba71e58
readFile
Jul 19, 2021
bbef0e0
Merge remote-tracking branch 'origin/dev-3.x' into js
Jul 20, 2021
35136e3
MockServer and Http Client
Jul 20, 2021
111cbe2
Apply suggestions from code review
Pitel Jul 21, 2021
0b2c169
Tests are almost green
Jul 22, 2021
079d128
Try another path
Jul 22, 2021
9793fab
Merge remote-tracking branch 'origin/dev-3.x' into js
Jul 22, 2021
5fabced
Merge remote-tracking branch 'origin/dev-3.x' into js
Aug 6, 2021
e82fa45
Fixes for new headers
Aug 6, 2021
f6b9fcd
Fix tests
Aug 6, 2021
b82129f
Revert data class
Aug 6, 2021
5f57518
added a few comments
martinbonnin Aug 6, 2021
9fdfe06
KtorWhateverEngine
Aug 6, 2021
4c86c0c
Merge remote-tracking branch 'origin/dev-3.x' into js
Aug 9, 2021
b45400d
Merge remote-tracking branch 'origin/dev-3.x' into js
Aug 16, 2021
f1310f6
Merge remote-tracking branch 'origin/dev-3.x' into js
Aug 24, 2021
307f6d7
Merge remote-tracking branch 'origin/dev-3.x' into js
Aug 25, 2021
96bd34d
Merge remote-tracking branch 'origin/dev-3.x' into js
Aug 26, 2021
326a113
Merge remote-tracking branch 'origin/dev-3.x' into js
Aug 31, 2021
a351271
ktor 1.6.3
Aug 31, 2021
49bb6aa
Fix build
Aug 31, 2021
6f24ae6
Merge remote-tracking branch 'origin/dev-3.x' into js
Oct 6, 2021
00eb202
Fix Okio artifact name
Oct 6, 2021
8bc82fd
connect generated sources to the JS sourceSets
martinbonnin Oct 6, 2021
ea02e1e
remove duplicate test
martinbonnin Oct 6, 2021
6eb855e
fix tests
martinbonnin Oct 6, 2021
497405d
fix the cancel tests
martinbonnin Oct 6, 2021
c53997a
Ignore some tests on Js
martinbonnin Oct 6, 2021
8035148
fix Batching tests
martinbonnin Oct 6, 2021
e74be24
fix typo
martinbonnin Oct 6, 2021
3f41665
Merge remote-tracking branch 'origin/dev-3.x' into js
Oct 7, 2021
a9a6efa
add some documentation
martinbonnin Oct 7, 2021
b5e8a95
be more explicit about WebSockets not supported on JS
martinbonnin Oct 7, 2021
2dad62e
add comment about the current working directory
martinbonnin Oct 7, 2021
0b926a3
correctly stop the server
martinbonnin Oct 7, 2021
ef7b21f
do not target js browser for tests
martinbonnin Oct 7, 2021
56ddcfe
Revert accidentaly modified .idea XML
Oct 7, 2021
ac9ad34
always target nodejs() for test.
martinbonnin Oct 7, 2021
0f17bc1
Merge branch 'js' of https://github.com/Pitel/apollo-android into js
martinbonnin Oct 7, 2021
c0bc9cf
fix hopefully remaining tests
martinbonnin Oct 7, 2021
275cc96
Merge remote-tracking branch 'origin/dev-3.x' into js
Oct 11, 2021
e12fdc8
Update KtorHttpEngine.kt
Pitel Oct 12, 2021
20de5c4
Remove lock and just return block
Pitel Oct 12, 2021
c70ed5a
remove unfinished sentence
martinbonnin Oct 13, 2021
1749b17
Merge branch 'dev-3.x' into js
martinbonnin Oct 13, 2021
0518b93
fix merge
martinbonnin Oct 13, 2021
c52299f
fix typo
martinbonnin Oct 14, 2021
54f3405
preven a double free
martinbonnin Oct 14, 2021
d9b28d6
Merge branch 'dev-3.x' into js
martinbonnin Oct 14, 2021
c618c1e
remove unused imports
martinbonnin Oct 14, 2021
a4c1171
Merge branch 'js' of https://github.com/Pitel/apollo-android into js
martinbonnin Oct 14, 2021
fa0cdcb
remove unused imports
martinbonnin Oct 14, 2021
c13824a
ignore SIGPIPE and errors writing the response
martinbonnin Oct 15, 2021
0590251
more logs during tests
martinbonnin Oct 15, 2021
7669875
Merge branch 'dev-3.x' into js
martinbonnin Oct 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import okio.Buffer
import okio.BufferedSink
import okio.BufferedSource
import okio.ByteString
import kotlin.js.JsName

/**
* Represents a GraphQL operation (mutation, query or subscription).
Expand All @@ -30,6 +31,7 @@ interface Operation<D : Operation.Data> : Executable<D> {
/**
* An unique identifier for the operation. Used for Automatic Persisted Queries. You can customize it with a [OperationIdGenerator]
*/
@JsName("operationId")
fun id(): String

override fun adapter(): Adapter<D>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ interface HttpBody {
*/
class HttpHeader(val name: String, val value: String)

fun List<HttpHeader>.valueOf(name: String): String? = firstOrNull { it.name == name }?.value
/**
* Get the value of the "name" header. HTTP header names are case insensitive, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
*
* @param name: the name of the header
*/
fun List<HttpHeader>.valueOf(name: String): String? = firstOrNull { it.name.equals(name, true) }?.value

/**
* a HTTP request to be sent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ object Utils {
try {
return nextInt()
} catch (e: Exception) {
e.printStackTrace()
}
try {
return nextLong()
Expand Down
10 changes: 8 additions & 2 deletions apollo-mockserver/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
kotlin("multiplatform")
}

configureMppDefaults(withJs = false)
configureMppTestsDefaults()

kotlin {
sourceSets {
Expand All @@ -21,10 +21,16 @@ kotlin {
}
}

val jsMain by getting {
dependencies {
implementation(groovy.util.Eval.x(project, "x.dep.kotlin.nodejs"))
}
}

val commonTest by getting {
dependencies {
implementation(projects.apolloTestingSupport) {
because("runWithMainLoop")
because("runTest")
}
implementation(projects.apolloRuntime) {
because("We need HttpEngine for SocketTest")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import kotlin.native.concurrent.freeze
actual class MockServer {
private val pthreadT: pthread_tVar
private val port: Int
private val socket: Socket
private var socket: Socket? = null

init {
val socketFd = socket(AF_INET, SOCK_STREAM, 0)
Expand Down Expand Up @@ -64,7 +64,7 @@ actual class MockServer {

socket = Socket(socketFd)

val stableRef = StableRef.create(socket.freeze())
val stableRef = StableRef.create(socket!!.freeze())

pthread_create(pthreadT.ptr, null, staticCFunction { arg ->
initRuntimeIfNeeded()
Expand All @@ -79,27 +79,38 @@ actual class MockServer {
}, stableRef.asCPointer())
}

actual fun url(): String {
actual suspend fun url(): String {
return "http://localhost:$port"
}

actual fun enqueue(mockResponse: MockResponse) {
socket.enqueue(mockResponse)
check(socket != null) {
"Cannot enqueue a response to a stopped MockServer"
}
socket!!.enqueue(mockResponse)
}

/**
* [MockServer] can only stop in between complete request/responses pairs
* If stop() is called while we're reading a request, this might wait forever
* Revisit once okio has native Timeout
*/
actual fun stop() {
socket.stop()
actual suspend fun stop() {
if (socket == null) {
return
}
socket!!.stop()
pthread_join(pthreadT.value, null)

nativeHeap.free(pthreadT.rawPtr)
pthreadT.value = null
socket = null
}

actual fun takeRequest(): MockRecordedRequest {
return socket.takeRequest()
check(socket != null) {
"Cannot take a request from a stopped MockServer"
}
return socket!!.takeRequest()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,28 @@ import kotlinx.atomicfu.locks.reentrantLock
import kotlinx.atomicfu.locks.synchronized
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.IntVar
import kotlinx.cinterop.alloc
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.convert
import kotlinx.cinterop.get
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.nativeHeap
import kotlinx.cinterop.nativeHeap.free
import kotlinx.cinterop.ptr
import kotlinx.cinterop.value
import okio.IOException
import okio.buffer
import platform.Foundation.NSMutableArray
import platform.posix.POLLIN
import platform.posix.SOL_SOCKET
import platform.posix.SO_NOSIGPIPE
import platform.posix.accept
import platform.posix.close
import platform.posix.errno
import platform.posix.pipe
import platform.posix.poll
import platform.posix.pollfd
import platform.posix.setsockopt
import platform.posix.usleep
import platform.posix.write
import kotlin.experimental.and
Expand Down Expand Up @@ -70,6 +77,10 @@ class Socket(private val socketFd: Int) {
"Cannot accept socket (errno = $errno)"
}

val one = alloc<IntVar>()
one.value = 1
setsockopt(connectionFd, SOL_SOCKET, SO_NOSIGPIPE, one.ptr, 4);

handleConnection(connectionFd)
close(connectionFd)
}
Expand Down Expand Up @@ -103,7 +114,7 @@ class Socket(private val socketFd: Int) {
val request = try {
readRequest(source)
} catch (e: IOException) {
debug("'$connectionFd': Connection error")
debug("'$connectionFd': readRequest error")
return
}
if (request == null) {
Expand All @@ -129,7 +140,13 @@ class Socket(private val socketFd: Int) {
if (mockResponse.delayMs > 0) {
usleep((mockResponse.delayMs * 1000).convert())
}
writeResponse(sink, mockResponse, request.version)

try {
writeResponse(sink, mockResponse, request.version)
} catch (e: IOException) {
debug("'$connectionFd': writeResponse error")
return
}

debug("Response Written")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@ package com.apollographql.apollo3.mockserver
import okio.ByteString.Companion.encodeUtf8

expect class MockServer() {
fun url(): String
/**
* Returns the root url for this server
*
* It will block until a port is found to listen to
*/
suspend fun url(): String

/**
* Stops the server
*/
suspend fun stop()

/**
* Enqueue a response
*/
fun enqueue(mockResponse: MockResponse)

/**
* Returns a request from the recorded requests or throws if no request has been received
*/
fun takeRequest(): MockRecordedRequest
fun stop()
}

Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ class MockServerCommonTest {
assertEquals("POST", recordedRequest.method)
assertEquals("Hello world", recordedRequest.body.utf8())
}

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package com.apollographql.apollo3.mockserver.test

import com.apollographql.apollo3.api.http.HttpMethod
import com.apollographql.apollo3.api.http.HttpRequest
import com.apollographql.apollo3.mockserver.MockResponse
import com.apollographql.apollo3.mockserver.MockServer
import com.apollographql.apollo3.network.http.DefaultHttpEngine
import com.apollographql.apollo3.api.http.HttpMethod
import com.apollographql.apollo3.api.http.HttpRequest
import com.apollographql.apollo3.testing.runWithMainLoop
import com.apollographql.apollo3.testing.runTest
import okio.ByteString.Companion.encodeUtf8
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals

class SocketTest {
val mockServer = MockServer()

@Test
fun writeMoreThan8kToTheSocket() = runWithMainLoop {
fun writeMoreThan8kToTheSocket() = runTest {
val mockServer = MockServer()

val builder = StringBuilder()
0.until(10000).forEach {
builder.append(Random.nextInt())
Expand All @@ -27,5 +27,7 @@ class SocketTest {
val response = engine.execute(HttpRequest(HttpMethod.Get, mockServer.url(), emptyList(), null))

assertEquals(response.body!!.readUtf8(), str)

mockServer.stop()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.apollographql.apollo3.mockserver

import Buffer
import net.AddressInfo
import okio.ByteString.Companion.toByteString
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

actual class MockServer {

private val responseQueue = mutableListOf<MockResponse>()
private val requests = mutableListOf<MockRecordedRequest>()

private val server = http.createServer { req, res ->
val requestBody = StringBuilder()
req.on("data") { chunk ->
when (chunk) {
is String -> requestBody.append(chunk)
is Buffer -> requestBody.append(chunk.toString("utf8"))
else -> println("WTF")
}
}
req.on("end") { _ ->
requests.add(
MockRecordedRequest(
req.method,
req.url,
req.httpVersion,
req.rawHeaders.toList().zipWithNext().toMap(),
requestBody.toString().encodeToByteArray().toByteString()
)
)
}

val mockResponse = responseQueue.removeFirst()
res.statusCode = mockResponse.statusCode
mockResponse.headers.forEach {
res.setHeader(it.key, it.value)
}
res.end(mockResponse.body.utf8())
}.listen()

actual suspend fun url() = suspendCoroutine<String> { cont ->
server.on("listening") { _ ->
cont.resume("http://localhost:${server.address().unsafeCast<AddressInfo>().port}")
}
}

actual fun enqueue(mockResponse: MockResponse) {
responseQueue.add(mockResponse)
}

actual fun takeRequest(): MockRecordedRequest {
return requests.removeFirst()
}

actual suspend fun stop() = suspendCoroutine<Unit> { cont ->
server.close {
cont.resume(Unit)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ actual class MockServer {
name(it) to get(name(it))!!
}.toMap()

actual fun url(): String {
actual suspend fun url(): String {
return mockWebServer.url("/").toString()
}

actual fun stop() {
actual suspend fun stop() {
mockWebServer.shutdown()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ actual fun assertMainThreadOnNative() {
check(NSThread.isMainThread()) {
"Non-main native call"
}
}
}

actual fun platform() = Platform.Native
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,17 @@ expect fun currentThreadId(): String
expect fun ensureNeverFrozen(obj: Any)
expect fun isFrozen(obj: Any): Boolean
expect fun freeze(obj: Any)
expect fun assertMainThreadOnNative()
expect fun assertMainThreadOnNative()

enum class Platform {
Jvm,
Native,
Js
}

/**
* The current platform. This is used from tests because Double.toString() doesn't behave the same on JS and other platforms.
* Prefer more specific functions like `assertMainThreadOnNative` when possible instead of checking the platform.
*/
expect fun platform(): Platform

Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ actual fun freeze(obj: Any) {
}

actual fun assertMainThreadOnNative() {
}
}

actual fun platform() = Platform.Js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ actual fun freeze(obj: Any) {
}

actual fun assertMainThreadOnNative() {
}
}

actual fun platform() = Platform.Jvm
Loading