Skip to content

Commit

Permalink
get kotlin client working with CR and CRLF
Browse files Browse the repository at this point in the history
  • Loading branch information
joshmossas committed Aug 24, 2024
1 parent 0ec988d commit 71b2af4
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class ExampleClientBooksService(
scope: CoroutineScope,
params: BookParams,
lastEventId: String? = null,
bufferCapacity: Int = 1024,
bufferCapacity: Int = 1024 * 1024,
onOpen: ((response: HttpResponse) -> Unit) = {},
onClose: (() -> Unit) = {},
onError: ((error: ExampleClientError) -> Unit) = {},
Expand Down Expand Up @@ -1886,6 +1886,7 @@ private suspend fun __prepareRequest(
return client.prepareRequest(builder)
}

// SSE_FN_START
private enum class SseEventLineType {
Id,
Event,
Expand All @@ -1910,7 +1911,7 @@ private fun __parseSseEventLine(line: String): Pair<SseEventLineType, String> {
return Pair(SseEventLineType.None, "")
}

private class __SseEvent(
private data class __SseEvent(
val id: String? = null,
val event: String,
val data: String,
Expand All @@ -1921,7 +1922,7 @@ private class __SseEventParsingResult(val events: List<__SseEvent>, val leftover

private fun __parseSseEvents(input: String): __SseEventParsingResult {
val events = mutableListOf<__SseEvent>()
val lines = input.lines().toMutableList()
val lines = input.lines()
if (lines.isEmpty()) {
return __SseEventParsingResult(events = listOf(), leftover = "")
}
Expand All @@ -1941,7 +1942,7 @@ private fun __parseSseEvents(input: String): __SseEventParsingResult {
SseEventLineType.None -> {}
}
}
val isEnd = line.isEmpty()
val isEnd = line == ""
if (isEnd) {
if (data != null) {
events.add(
Expand All @@ -1965,6 +1966,7 @@ private fun __parseSseEvents(input: String): __SseEventParsingResult {
leftover = if (lastIndex != null) lines.subList(lastIndex!!, lines.size).joinToString(separator = "\n") else ""
)
}
// SSE_FN_END


private suspend fun __handleSseRequest(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import kotlin.test.Test
import kotlin.test.assertEquals

// SSE_FN_START
private enum class SseEventLineType {
Id,
Event,
Data,
Retry,
None,
}

private fun __parseSseEventLine(line: String): Pair<SseEventLineType, String> {
if (line.startsWith("id:")) {
return Pair(SseEventLineType.Id, line.substring(3).trim())
}
if (line.startsWith("event:")) {
return Pair(SseEventLineType.Event, line.substring(6).trim())
}
if (line.startsWith("data:")) {
return Pair(SseEventLineType.Data, line.substring(5).trim())
}
if (line.startsWith("retry:")) {
return Pair(SseEventLineType.Retry, line.substring(6).trim())
}
return Pair(SseEventLineType.None, "")
}

private data class __SseEvent(
val id: String? = null,
val event: String,
val data: String,
val retry: Int? = null
)

private class __SseEventParsingResult(val events: List<__SseEvent>, val leftover: String)

private fun __parseSseEvents(input: String): __SseEventParsingResult {
val events = mutableListOf<__SseEvent>()
val lines = input.lines()
if (lines.isEmpty()) {
return __SseEventParsingResult(events = listOf(), leftover = "")
}
var id: String? = null
var event: String? = null
var data: String? = null
var retry: Int? = null
var lastIndex: Int? = 0
lines.forEachIndexed { index, line ->
if (line.isNotEmpty()) {
val (type, value) = __parseSseEventLine(line)
when (type) {
SseEventLineType.Id -> id = value
SseEventLineType.Event -> event = value
SseEventLineType.Data -> data = value
SseEventLineType.Retry -> retry = value.toInt()
SseEventLineType.None -> {}
}
}
val isEnd = line == ""
if (isEnd) {
if (data != null) {
events.add(
__SseEvent(
id = id,
event = event ?: "message",
data = data!!,
retry = retry,
)
)
}
id = null
event = null
data = null
retry = null
lastIndex = if (index + 1 < lines.size) index + 1 else null
}
}
return __SseEventParsingResult(
events = events,
leftover = if (lastIndex != null) lines.subList(lastIndex!!, lines.size).joinToString(separator = "\n") else ""
)
}
// SSE_FN_END

class SseEventTests {

@Test
fun parseSseLine() {
val idInputs = listOf("id:1", "id: 1")
idInputs.forEach {
val (type, value) = __parseSseEventLine(it)
assertEquals(type, SseEventLineType.Id)
assertEquals(value, "1")
}
val eventInputs = listOf("event:foo", "event: foo")
eventInputs.forEach {
val (type, value) = __parseSseEventLine(it)
assertEquals(type, SseEventLineType.Event)
assertEquals(value, "foo")
}
val dataInputs = listOf("data:foo", "data: foo")
dataInputs.forEach {
val (type, value) = __parseSseEventLine(it)
assertEquals(type, SseEventLineType.Data)
assertEquals(value, "foo")
}
val retryInputs = listOf("retry:150", "retry: 150")
retryInputs.forEach {
val (type, value) = __parseSseEventLine(it)
assertEquals(type, SseEventLineType.Retry)
assertEquals(value, "150")
}
}

@Test
fun parseStandardEvents() {
val lines = listOf(
"id: 1",
"data: hello world",
"",
"data: hello world",
"",
"id: 2",
"event: foo",
"data: hello world",
"retry: 150",
"",
"id: 3",
"data: hello world"
)
val expectedOutput = listOf(
__SseEvent(id = "1", event = "message", data = "hello world"),
__SseEvent(event = "message", data = "hello world"),
__SseEvent(id = "2", event = "foo", data = "hello world", retry = 150)
)
val expectedLeftover = "id: 3\ndata: hello world"


val newlineInput = lines.joinToString("\n")
val newlineResult = __parseSseEvents(newlineInput)
assertEquals(newlineResult.events, expectedOutput)
assertEquals(newlineResult.leftover, expectedLeftover)

val crLineInput = lines.joinToString("\r")
val crLineResult = __parseSseEvents(crLineInput)
assertEquals(crLineResult.events, expectedOutput)
assertEquals(crLineResult.leftover, expectedLeftover)

val crlfLineInput = lines.joinToString("\r\n")
val crlfLineResult = __parseSseEvents(crlfLineInput)
assertEquals(crlfLineResult.events, expectedOutput)
assertEquals(crlfLineResult.leftover, expectedLeftover)
}

@Test
fun parsePartialEvents() {
val lines = listOf(
":",
" :",
"hello world",
"",
"data: foo",
"",
"id: 1",
"data: {\"id\":\"foo"
)
val expectedOutput = listOf(__SseEvent(event = "message", data = "foo"))
val expectedLeftover = "id: 1\ndata: {\"id\":\"foo"

val newLineResult = __parseSseEvents(lines.joinToString("\n"))
assertEquals(newLineResult.events, expectedOutput)
assertEquals(newLineResult.leftover, expectedLeftover)

val crResult = __parseSseEvents(lines.joinToString("\r"))
assertEquals(crResult.events, expectedOutput)
assertEquals(crResult.leftover, expectedLeftover)

val crlfResult = __parseSseEvents(lines.joinToString("\r\n"))
assertEquals(crlfResult.events, expectedOutput)
assertEquals(crlfResult.leftover, expectedLeftover)
}
}
2 changes: 1 addition & 1 deletion languages/kotlin/kotlin-codegen/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"test": {
"executor": "nx:run-commands",
"inputs": [
"{projectRoot}/src",
"{workspaceRoot}/languages/kotlin/kotlin-codegen/src",
"{workspaceRoot}/languages/kotlin/kotlin-codegen-reference/src"
],
"outputs": ["{workspaceRoot}/languages/kotlin/kotlin-codegen"],
Expand Down
37 changes: 36 additions & 1 deletion languages/kotlin/kotlin-codegen/src/_index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ test("output matches the reference client", () => {
encoding: "utf8",
},
);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument

const result = kotlinClientFromAppDefinition(JSON.parse(referenceSchema), {
clientName: "ExampleClient",
outputFile: "",
Expand All @@ -33,3 +33,38 @@ test("output matches the reference client", () => {
normalizeWhitespace(referenceClient),
);
});

test("kotlin reference has correct sse functionality", () => {
const referenceClient = fs.readFileSync(
path.resolve(
__dirname,
"../../kotlin-codegen-reference/src/main/kotlin/ExampleClient.kt",
),
{
encoding: "utf8",
},
);
const testFile = fs.readFileSync(
path.resolve(
__dirname,
"../../kotlin-codegen-reference/src/test/kotlin/SseEventTests.kt",
),
{
encoding: "utf8",
},
);
const lines: string[] = [];
let startAdding = false;
for (const line of testFile.split("\n")) {
if (startAdding) {
lines.push(line);
}
if (line.includes("// SSE_FN_START")) {
startAdding = true;
}
if (line.includes("// SSE_FN_END")) {
break;
}
}
expect(referenceClient.includes(lines.join("\n"))).toBe(true);
});
8 changes: 5 additions & 3 deletions languages/kotlin/kotlin-codegen/src/_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ private suspend fun __prepareRequest(
return client.prepareRequest(builder)
}
// SSE_FN_START
private enum class SseEventLineType {
Id,
Event,
Expand All @@ -448,7 +449,7 @@ private fun __parseSseEventLine(line: String): Pair<SseEventLineType, String> {
return Pair(SseEventLineType.None, "")
}
private class __SseEvent(
private data class __SseEvent(
val id: String? = null,
val event: String,
val data: String,
Expand All @@ -459,7 +460,7 @@ private class __SseEventParsingResult(val events: List<__SseEvent>, val leftover
private fun __parseSseEvents(input: String): __SseEventParsingResult {
val events = mutableListOf<__SseEvent>()
val lines = input.lines().toMutableList()
val lines = input.lines()
if (lines.isEmpty()) {
return __SseEventParsingResult(events = listOf(), leftover = "")
}
Expand All @@ -479,7 +480,7 @@ private fun __parseSseEvents(input: String): __SseEventParsingResult {
SseEventLineType.None -> {}
}
}
val isEnd = line.isEmpty()
val isEnd = line == ""
if (isEnd) {
if (data != null) {
events.add(
Expand All @@ -503,6 +504,7 @@ private fun __parseSseEvents(input: String): __SseEventParsingResult {
leftover = if (lastIndex != null) lines.subList(lastIndex!!, lines.size).joinToString(separator = "\\n") else ""
)
}
// SSE_FN_END
private suspend fun __handleSseRequest(
scope: CoroutineScope,
Expand Down
2 changes: 1 addition & 1 deletion languages/kotlin/kotlin-codegen/src/procedures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function kotlinHttpRpcFromSchema(
scope: CoroutineScope,
${params ? `params: ${params},` : ""}
lastEventId: String? = null,
bufferCapacity: Int = 1024,
bufferCapacity: Int = 1024 * 1024,
onOpen: ((response: HttpResponse) -> Unit) = {},
onClose: (() -> Unit) = {},
onError: ((error: ${context.clientName}Error) -> Unit) = {},
Expand Down
15 changes: 2 additions & 13 deletions languages/kotlin/kotlin-codegen/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"types": ["vitest"]
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
"types": ["vitest", "vitest/globals", "node"]
}
}
9 changes: 0 additions & 9 deletions languages/kotlin/kotlin-codegen/tsconfig.lib.json

This file was deleted.

18 changes: 0 additions & 18 deletions languages/kotlin/kotlin-codegen/tsconfig.spec.json

This file was deleted.

Loading

0 comments on commit 71b2af4

Please sign in to comment.