From 9d77fe0c7786ba00b393bd154c55e9487bfe22fc Mon Sep 17 00:00:00 2001 From: Mendy Man Date: Fri, 28 Feb 2025 19:49:18 -0500 Subject: [PATCH 1/2] kotlin: Pull in change from latest codegen --- kotlin/lib/src/main/kotlin/Environment.kt | 47 ++++++ kotlin/lib/src/main/kotlin/Health.kt | 11 ++ kotlin/lib/src/main/kotlin/IngestEndpoint.kt | 157 ++++++++++++++++++ kotlin/lib/src/main/kotlin/Message.kt | 6 +- .../lib/src/main/kotlin/models/EndpointIn.kt | 1 + .../kotlin/models/ExpungeAllContentsOut.kt | 11 ++ .../kotlin/models/IngestEndpointHeadersIn.kt | 6 + .../kotlin/models/IngestEndpointHeadersOut.kt | 7 + .../main/kotlin/models/IngestEndpointIn.kt | 15 ++ .../main/kotlin/models/IngestEndpointOut.kt | 18 ++ .../kotlin/models/IngestEndpointSecretIn.kt | 6 + .../kotlin/models/IngestEndpointSecretOut.kt | 6 + .../kotlin/models/IngestEndpointUpdate.kt | 14 ++ .../models/ListResponseIngestEndpointOut.kt | 12 ++ 14 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 kotlin/lib/src/main/kotlin/Environment.kt create mode 100644 kotlin/lib/src/main/kotlin/Health.kt create mode 100644 kotlin/lib/src/main/kotlin/IngestEndpoint.kt create mode 100644 kotlin/lib/src/main/kotlin/models/ExpungeAllContentsOut.kt create mode 100644 kotlin/lib/src/main/kotlin/models/IngestEndpointHeadersIn.kt create mode 100644 kotlin/lib/src/main/kotlin/models/IngestEndpointHeadersOut.kt create mode 100644 kotlin/lib/src/main/kotlin/models/IngestEndpointIn.kt create mode 100644 kotlin/lib/src/main/kotlin/models/IngestEndpointOut.kt create mode 100644 kotlin/lib/src/main/kotlin/models/IngestEndpointSecretIn.kt create mode 100644 kotlin/lib/src/main/kotlin/models/IngestEndpointSecretOut.kt create mode 100644 kotlin/lib/src/main/kotlin/models/IngestEndpointUpdate.kt create mode 100644 kotlin/lib/src/main/kotlin/models/ListResponseIngestEndpointOut.kt diff --git a/kotlin/lib/src/main/kotlin/Environment.kt b/kotlin/lib/src/main/kotlin/Environment.kt new file mode 100644 index 000000000..d2c394e85 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/Environment.kt @@ -0,0 +1,47 @@ +// this file is @generated +package com.svix.kotlin + +import com.svix.kotlin.models.EnvironmentIn +import com.svix.kotlin.models.EnvironmentOut +import okhttp3.Headers + +data class EnvironmentExportOptions(val idempotencyKey: String? = null) + +data class EnvironmentImportOptions(val idempotencyKey: String? = null) + +class Environment(private val client: SvixHttpClient) { + + /** Download a JSON file containing all org-settings and event types. */ + suspend fun export( + options: EnvironmentExportOptions = EnvironmentExportOptions() + ): EnvironmentOut { + val url = client.newUrlBuilder().encodedPath("/api/v1/environment/export") + val headers = Headers.Builder() + options.idempotencyKey?.let { headers.add("idempotency-key", it) } + return client.executeRequest( + "POST", + url.build(), + headers = headers.build(), + ) + } + + /** + * Import a configuration into the active organization. It doesn't delete anything, only + * adds/updates what was passed to it. + */ + suspend fun import( + environmentIn: EnvironmentIn, + options: EnvironmentImportOptions = EnvironmentImportOptions(), + ) { + val url = client.newUrlBuilder().encodedPath("/api/v1/environment/import") + val headers = Headers.Builder() + options.idempotencyKey?.let { headers.add("idempotency-key", it) } + + client.executeRequest( + "POST", + url.build(), + headers = headers.build(), + reqBody = environmentIn, + ) + } +} diff --git a/kotlin/lib/src/main/kotlin/Health.kt b/kotlin/lib/src/main/kotlin/Health.kt new file mode 100644 index 000000000..c2e61d5f2 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/Health.kt @@ -0,0 +1,11 @@ +// this file is @generated +package com.svix.kotlin + +class Health(private val client: SvixHttpClient) { + + /** Verify the API server is up and running. */ + suspend fun get() { + val url = client.newUrlBuilder().encodedPath("/api/v1/health") + client.executeRequest("GET", url.build()) + } +} diff --git a/kotlin/lib/src/main/kotlin/IngestEndpoint.kt b/kotlin/lib/src/main/kotlin/IngestEndpoint.kt new file mode 100644 index 000000000..d65f5bfb3 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/IngestEndpoint.kt @@ -0,0 +1,157 @@ +// this file is @generated +package com.svix.kotlin + +import com.svix.kotlin.models.IngestEndpointHeadersIn +import com.svix.kotlin.models.IngestEndpointHeadersOut +import com.svix.kotlin.models.IngestEndpointIn +import com.svix.kotlin.models.IngestEndpointOut +import com.svix.kotlin.models.IngestEndpointSecretIn +import com.svix.kotlin.models.IngestEndpointSecretOut +import com.svix.kotlin.models.IngestEndpointUpdate +import com.svix.kotlin.models.ListResponseIngestEndpointOut +import com.svix.kotlin.models.Ordering +import okhttp3.Headers + +data class IngestEndpointListOptions( + /** Limit the number of returned items */ + val limit: ULong? = null, + /** The iterator returned from a prior invocation */ + val iterator: String? = null, + /** The sorting order of the returned items */ + val order: Ordering? = null, +) + +data class IngestEndpointCreateOptions(val idempotencyKey: String? = null) + +data class IngestEndpointRotateSecretOptions(val idempotencyKey: String? = null) + +class IngestEndpoint(private val client: SvixHttpClient) { + + /** List ingest endpoints. */ + suspend fun list( + options: IngestEndpointListOptions = IngestEndpointListOptions() + ): ListResponseIngestEndpointOut { + val url = client.newUrlBuilder().encodedPath("/ingest/api/v1/source/{source_id}/endpoint") + options.limit?.let { url.addQueryParameter("limit", serializeQueryParam(it)) } + options.iterator?.let { url.addQueryParameter("iterator", it) } + options.order?.let { url.addQueryParameter("order", serializeQueryParam(it)) } + return client.executeRequest("GET", url.build()) + } + + /** Create an ingest endpoint. */ + suspend fun create( + ingestEndpointIn: IngestEndpointIn, + options: IngestEndpointCreateOptions = IngestEndpointCreateOptions(), + ): IngestEndpointOut { + val url = client.newUrlBuilder().encodedPath("/ingest/api/v1/source/{source_id}/endpoint") + val headers = Headers.Builder() + options.idempotencyKey?.let { headers.add("idempotency-key", it) } + + return client.executeRequest( + "POST", + url.build(), + headers = headers.build(), + reqBody = ingestEndpointIn, + ) + } + + /** Get an ingest endpoint. */ + suspend fun get(endpointId: String): IngestEndpointOut { + val url = + client + .newUrlBuilder() + .encodedPath("/ingest/api/v1/source/{source_id}/endpoint/$endpointId") + return client.executeRequest("GET", url.build()) + } + + /** Update an ingest endpoint. */ + suspend fun update( + endpointId: String, + ingestEndpointUpdate: IngestEndpointUpdate, + ): IngestEndpointOut { + val url = + client + .newUrlBuilder() + .encodedPath("/ingest/api/v1/source/{source_id}/endpoint/$endpointId") + + return client.executeRequest( + "PUT", + url.build(), + reqBody = ingestEndpointUpdate, + ) + } + + /** Delete an ingest endpoint. */ + suspend fun delete(endpointId: String) { + val url = + client + .newUrlBuilder() + .encodedPath("/ingest/api/v1/source/{source_id}/endpoint/$endpointId") + client.executeRequest("DELETE", url.build()) + } + + /** Get the additional headers to be sent with the ingest. */ + suspend fun getHeaders(endpointId: String): IngestEndpointHeadersOut { + val url = + client + .newUrlBuilder() + .encodedPath("/ingest/api/v1/source/{source_id}/endpoint/$endpointId/headers") + return client.executeRequest("GET", url.build()) + } + + /** Set the additional headers to be sent to the endpoint. */ + suspend fun updateHeaders( + endpointId: String, + ingestEndpointHeadersIn: IngestEndpointHeadersIn, + ) { + val url = + client + .newUrlBuilder() + .encodedPath("/ingest/api/v1/source/{source_id}/endpoint/$endpointId/headers") + + client.executeRequest( + "PUT", + url.build(), + reqBody = ingestEndpointHeadersIn, + ) + } + + /** + * Get an ingest endpoint's signing secret. + * + * This is used to verify the authenticity of the webhook. For more information please refer to + * [the consuming webhooks docs](https://docs.svix.com/consuming-webhooks/). + */ + suspend fun getSecret(endpointId: String): IngestEndpointSecretOut { + val url = + client + .newUrlBuilder() + .encodedPath("/ingest/api/v1/source/{source_id}/endpoint/$endpointId/secret") + return client.executeRequest("GET", url.build()) + } + + /** + * Rotates an ingest endpoint's signing secret. + * + * The previous secret will remain valid for the next 24 hours. + */ + suspend fun rotateSecret( + endpointId: String, + ingestEndpointSecretIn: IngestEndpointSecretIn, + options: IngestEndpointRotateSecretOptions = IngestEndpointRotateSecretOptions(), + ) { + val url = + client + .newUrlBuilder() + .encodedPath("/ingest/api/v1/source/{source_id}/endpoint/$endpointId/secret/rotate") + val headers = Headers.Builder() + options.idempotencyKey?.let { headers.add("idempotency-key", it) } + + client.executeRequest( + "POST", + url.build(), + headers = headers.build(), + reqBody = ingestEndpointSecretIn, + ) + } +} diff --git a/kotlin/lib/src/main/kotlin/Message.kt b/kotlin/lib/src/main/kotlin/Message.kt index bd82fe42b..715ec642a 100644 --- a/kotlin/lib/src/main/kotlin/Message.kt +++ b/kotlin/lib/src/main/kotlin/Message.kt @@ -2,7 +2,7 @@ package com.svix.kotlin import com.svix.kotlin.models.ApplicationIn -import com.svix.kotlin.models.ExpungAllContentsOut +import com.svix.kotlin.models.ExpungeAllContentsOut import com.svix.kotlin.models.ListResponseMessageOut import com.svix.kotlin.models.MessageIn import com.svix.kotlin.models.MessageOut @@ -117,11 +117,11 @@ class Message(private val client: SvixHttpClient) { suspend fun expungeAllContents( appId: String, options: MessageExpungeAllContentsOptions = MessageExpungeAllContentsOptions(), - ): ExpungAllContentsOut { + ): ExpungeAllContentsOut { val url = client.newUrlBuilder().encodedPath("/api/v1/app/$appId/msg/expunge-all-contents") val headers = Headers.Builder() options.idempotencyKey?.let { headers.add("idempotency-key", it) } - return client.executeRequest( + return client.executeRequest( "POST", url.build(), headers = headers.build(), diff --git a/kotlin/lib/src/main/kotlin/models/EndpointIn.kt b/kotlin/lib/src/main/kotlin/models/EndpointIn.kt index 52a81f681..4f5318a71 100644 --- a/kotlin/lib/src/main/kotlin/models/EndpointIn.kt +++ b/kotlin/lib/src/main/kotlin/models/EndpointIn.kt @@ -9,6 +9,7 @@ data class EndpointIn( val description: String? = null, val disabled: Boolean? = null, val filterTypes: Set? = null, + val headers: Map? = null, val metadata: Map? = null, val rateLimit: UShort? = null, val secret: String? = null, diff --git a/kotlin/lib/src/main/kotlin/models/ExpungeAllContentsOut.kt b/kotlin/lib/src/main/kotlin/models/ExpungeAllContentsOut.kt new file mode 100644 index 000000000..4113b1378 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/ExpungeAllContentsOut.kt @@ -0,0 +1,11 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable + +@Serializable +data class ExpungeAllContentsOut( + val id: String, + val status: BackgroundTaskStatus, + val task: BackgroundTaskType, +) diff --git a/kotlin/lib/src/main/kotlin/models/IngestEndpointHeadersIn.kt b/kotlin/lib/src/main/kotlin/models/IngestEndpointHeadersIn.kt new file mode 100644 index 000000000..1556e9244 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/IngestEndpointHeadersIn.kt @@ -0,0 +1,6 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable + +@Serializable data class IngestEndpointHeadersIn(val headers: Map) diff --git a/kotlin/lib/src/main/kotlin/models/IngestEndpointHeadersOut.kt b/kotlin/lib/src/main/kotlin/models/IngestEndpointHeadersOut.kt new file mode 100644 index 000000000..d614479dc --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/IngestEndpointHeadersOut.kt @@ -0,0 +1,7 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable + +@Serializable +data class IngestEndpointHeadersOut(val headers: Map, val sensitive: Set) diff --git a/kotlin/lib/src/main/kotlin/models/IngestEndpointIn.kt b/kotlin/lib/src/main/kotlin/models/IngestEndpointIn.kt new file mode 100644 index 000000000..1c735ff3d --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/IngestEndpointIn.kt @@ -0,0 +1,15 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable + +@Serializable +data class IngestEndpointIn( + val description: String? = null, + val disabled: Boolean? = null, + val metadata: Map? = null, + val rateLimit: UShort? = null, + val secret: String? = null, + val uid: String? = null, + val url: String, +) diff --git a/kotlin/lib/src/main/kotlin/models/IngestEndpointOut.kt b/kotlin/lib/src/main/kotlin/models/IngestEndpointOut.kt new file mode 100644 index 000000000..f87aedf83 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/IngestEndpointOut.kt @@ -0,0 +1,18 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.datetime.Instant +import kotlinx.serialization.Serializable + +@Serializable +data class IngestEndpointOut( + val createdAt: Instant, + val description: String, + val disabled: Boolean? = null, + val id: String, + val metadata: Map, + val rateLimit: UShort? = null, + val uid: String? = null, + val updatedAt: Instant, + val url: String, +) diff --git a/kotlin/lib/src/main/kotlin/models/IngestEndpointSecretIn.kt b/kotlin/lib/src/main/kotlin/models/IngestEndpointSecretIn.kt new file mode 100644 index 000000000..9cbf311e5 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/IngestEndpointSecretIn.kt @@ -0,0 +1,6 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable + +@Serializable data class IngestEndpointSecretIn(val key: String? = null) diff --git a/kotlin/lib/src/main/kotlin/models/IngestEndpointSecretOut.kt b/kotlin/lib/src/main/kotlin/models/IngestEndpointSecretOut.kt new file mode 100644 index 000000000..a14463293 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/IngestEndpointSecretOut.kt @@ -0,0 +1,6 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable + +@Serializable data class IngestEndpointSecretOut(val key: String) diff --git a/kotlin/lib/src/main/kotlin/models/IngestEndpointUpdate.kt b/kotlin/lib/src/main/kotlin/models/IngestEndpointUpdate.kt new file mode 100644 index 000000000..743e77ed1 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/IngestEndpointUpdate.kt @@ -0,0 +1,14 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable + +@Serializable +data class IngestEndpointUpdate( + val description: String? = null, + val disabled: Boolean? = null, + val metadata: Map? = null, + val rateLimit: UShort? = null, + val uid: String? = null, + val url: String, +) diff --git a/kotlin/lib/src/main/kotlin/models/ListResponseIngestEndpointOut.kt b/kotlin/lib/src/main/kotlin/models/ListResponseIngestEndpointOut.kt new file mode 100644 index 000000000..661606b37 --- /dev/null +++ b/kotlin/lib/src/main/kotlin/models/ListResponseIngestEndpointOut.kt @@ -0,0 +1,12 @@ +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable + +@Serializable +data class ListResponseIngestEndpointOut( + val data: List, + val done: Boolean, + val iterator: String? = null, + val prevIterator: String? = null, +) From 9b74033f0fd5bebb1134f44389bc1a6fd153c89b Mon Sep 17 00:00:00 2001 From: Mendy Man Date: Fri, 28 Feb 2025 19:49:37 -0500 Subject: [PATCH 2/2] Add kotlin codegen to `regen_openapi.sh` --- .../templates/api_extra/application_create.kt | 16 ++ kotlin/templates/api_extra/message.kt | 46 ++++++ kotlin/templates/api_resource.kt.jinja | 137 ++++++++++++++++++ kotlin/templates/component_type.kt.jinja | 7 + kotlin/templates/types/integer_enum.kt.jinja | 49 +++++++ kotlin/templates/types/string_enum.kt.jinja | 22 +++ kotlin/templates/types/struct.kt.jinja | 40 +++++ regen_openapi.sh | 18 +++ 8 files changed, 335 insertions(+) create mode 100644 kotlin/templates/api_extra/application_create.kt create mode 100644 kotlin/templates/api_extra/message.kt create mode 100644 kotlin/templates/api_resource.kt.jinja create mode 100644 kotlin/templates/component_type.kt.jinja create mode 100644 kotlin/templates/types/integer_enum.kt.jinja create mode 100644 kotlin/templates/types/string_enum.kt.jinja create mode 100644 kotlin/templates/types/struct.kt.jinja diff --git a/kotlin/templates/api_extra/application_create.kt b/kotlin/templates/api_extra/application_create.kt new file mode 100644 index 000000000..005ed40dc --- /dev/null +++ b/kotlin/templates/api_extra/application_create.kt @@ -0,0 +1,16 @@ +/** Get or create an application. */ +suspend fun getOrCreate( + applicationIn: ApplicationIn, + options: ApplicationCreateOptions = ApplicationCreateOptions() +): ApplicationOut { + val url = client.newUrlBuilder().encodedPath("/api/v1/app").addQueryParameter("get_if_exists","true") + var headers = Headers.Builder() + options.idempotencyKey?.let { headers = headers.add("idempotency-key", it) } + + return client.executeRequest( + "POST", + url.build(), + headers = headers.build(), + reqBody = applicationIn, + ) +} \ No newline at end of file diff --git a/kotlin/templates/api_extra/message.kt b/kotlin/templates/api_extra/message.kt new file mode 100644 index 000000000..8d203a178 --- /dev/null +++ b/kotlin/templates/api_extra/message.kt @@ -0,0 +1,46 @@ + +/** + * Creates a [MessageIn] with a pre-serialized payload. + * + * The payload is not normalized on the server. Normally, payloads are required to be JSON, and Svix + * will minify the payload before sending the webhooks (for example, by removing extraneous + * whitespace or unnecessarily escaped characters in strings). With this function, the payload will + * be sent "as is", without any minification or other processing. + * + * @param payload Serialized message payload + * @param contentType The value to use for the Content-Type header of the webhook sent by Svix, + * overwriting the default of `application/json` if specified + * + * See the class documentation for details about the other parameters. + */ +fun messageInRaw( + eventType: String, + payload: String, + contentType: String? = null, + application: ApplicationIn? = null, + channels: Set? = null, + eventId: String? = null, + payloadRetentionHours: Long? = null, + payloadRetentionPeriod: Long? = 90L, + tags: Set? = null, + transformationsParams: Map = mapOf(), +): MessageIn { + val transformationsParams = transformationsParams.toMutableMap() + transformationsParams["rawPayload"] = JsonPrimitive(payload) + if (contentType != null) { + val headers = mapOf("content-type" to JsonPrimitive(contentType)) + transformationsParams["headers"] = JsonObject(headers) + } + + return MessageIn( + eventType = eventType, + payload = JsonObject(mapOf()), + application = application, + channels = channels, + eventId = eventId, + payloadRetentionHours = payloadRetentionHours, + payloadRetentionPeriod = payloadRetentionPeriod, + tags = tags, + transformationsParams = JsonObject(transformationsParams), + ) +} \ No newline at end of file diff --git a/kotlin/templates/api_resource.kt.jinja b/kotlin/templates/api_resource.kt.jinja new file mode 100644 index 000000000..db7b45a42 --- /dev/null +++ b/kotlin/templates/api_resource.kt.jinja @@ -0,0 +1,137 @@ +{% set resource_type_name = resource.name | to_upper_camel_case -%} +// this file is @generated +package com.svix.kotlin + +import com.svix.kotlin.models.ApplicationIn +import com.svix.kotlin.models.ListResponseMessageOut +import com.svix.kotlin.models.MessageIn +import com.svix.kotlin.models.MessageOut +import kotlinx.datetime.Instant +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import okhttp3.Headers +{% for c in referenced_components -%} +import com.svix.kotlin.models.{{ c | to_upper_camel_case }} +{% endfor %} + +{% for op in resource.operations -%} + {% if op | has_query_or_header_params -%} +data class {{ resource_type_name }}{{ op.name | to_upper_camel_case }}Options( + {% for p in op.query_params -%} + {% set p_ty = p.type.to_kotlin() -%} + {% set p_val = "" -%} + {% if not p.required -%} + {% set p_ty %}{{ p_ty }}?{% endset -%} + {% set p_val = "= null" -%} + {% endif -%} + {% if p.description is defined -%} + {{ p.description | to_doc_comment(style="kotlin") }} + {% endif -%} + val {{ p.name | to_lower_camel_case }}: {{ p_ty }} {{ p_val }}, + {% endfor -%} + + {% for p in op.header_params -%} + val {{ p.name | to_lower_camel_case }}: String? = null, + {% endfor -%} +) + {% endif -%} +{% endfor -%} + + +class {{ resource_type_name }}(private val client: SvixHttpClient) { + + +{% for op in resource.operations -%} +{% set res_type = op.response_body_schema_name | replace("_", "") -%} +{% set has_query_params = op.query_params | length > 0 -%} +{% set has_required_query_params = + op.query_params | selectattr("required") | length > 0 -%} +{{ op.description | with_javadoc_deprecation(op.deprecated) | to_doc_comment(style="kotlin") }} +{% if op.deprecated -%} + @Deprecated("") +{% endif -%} +suspend fun {{ op.name | to_lower_camel_case }}( + {# path parameters -#} + {% for p in op.path_params -%} + {{ p | to_lower_camel_case }}: String, + {% endfor -%} + + {# body parameter interface -#} + {% if op.request_body_schema_name is defined -%} + {% set req_body_field_name = op.request_body_schema_name | to_lower_camel_case -%} + {{ req_body_field_name }}: {{ op.request_body_schema_name }}, + {% endif -%} + + {# query parameters -#} + {% if op | has_query_or_header_params -%} + {% set field_ty -%} + {{ resource_type_name }}{{ op.name | to_upper_camel_case }}Options + {%- endset -%} + options: {{ field_ty }} + {%- if not has_required_query_params %} = {{ field_ty }}(){% endif -%} + , + {% endif -%} +) +{%- if op.response_body_schema_name is defined -%} + : {{ res_type }} +{%- endif %} +{ + {% set path_variable = op.path -%} + {% if op.path_params | length > 0 -%} + {% set path_variable = op.path | generate_kt_path_str(op.path_params) -%} + {% endif -%} + {% if op.query_params | length > 0 -%} + val url = client.newUrlBuilder().encodedPath("{{ path_variable }}") + {%- for p in op.query_params -%} + {%- if p.required -%} + .addQueryParameter("{{ p.name }}", options.{{ p.name | to_lower_camel_case }}) + {%- endif -%} + {%- endfor %} + {% for p in op.query_params -%} + {% if not p.required -%} + options.{{ p.name | to_lower_camel_case }}?.let {url.addQueryParameter("{{ p.name }}", {% if p.type.is_string() %}it{% else %}serializeQueryParam(it){% endif %}) } + {% endif -%} + {% endfor -%} + {% else -%} + val url = client.newUrlBuilder() + {# path params -#} + .encodedPath("{{ path_variable }}") + {% endif -%} + + {% if op.header_params | length > 0 -%} + val headers = Headers.Builder() + {% for p in op.header_params -%} + options.{{ p.name | to_lower_camel_case }}?.let { headers.add("{{ p.name }}",it) } + {% endfor -%} + {% endif -%} + + {% set generic_res_type = res_type -%} + {% if not op.response_body_schema_name is defined -%} + {% set generic_res_type = "Boolean" -%} + {% endif -%} + {% set generic_req_type = "Any" -%} + {% if op.request_body_schema_name is defined -%} + {% set generic_req_type = op.request_body_schema_name %} + {% endif -%} + {% if op.response_body_schema_name is defined %}return {% endif %}client.executeRequest<{{ generic_req_type }},{{ generic_res_type }}>("{{ op.method | upper}}",url.build() + {%- if op.header_params | length > 0 -%} + ,headers = headers.build() + {%- endif -%} + {%- if op.request_body_schema_name is defined -%} + ,reqBody = {{ req_body_field_name }} + {%- endif -%} + ) +} + +{% set extra_path -%} +api_extra/{{ resource.name | to_snake_case }}_{{ op.name | to_snake_case }}.kt +{%- endset -%} +{% include extra_path ignore missing %} +{% endfor -%} + +} + + +{% set resource_extra_path %}api_extra/{{ resource.name | to_snake_case }}.kt{%- endset -%} +{% include resource_extra_path ignore missing %} diff --git a/kotlin/templates/component_type.kt.jinja b/kotlin/templates/component_type.kt.jinja new file mode 100644 index 000000000..6f7135d38 --- /dev/null +++ b/kotlin/templates/component_type.kt.jinja @@ -0,0 +1,7 @@ +{% if type.kind == "struct" -%} + {% include "types/struct.kt.jinja" -%} +{%- elif type.kind == "string_enum"-%} + {% include "types/string_enum.kt.jinja" -%} +{%- elif type.kind == "integer_enum" %} +{%- include "types/integer_enum.kt.jinja" -%} +{%- endif %} diff --git a/kotlin/templates/types/integer_enum.kt.jinja b/kotlin/templates/types/integer_enum.kt.jinja new file mode 100644 index 000000000..7fa1540b4 --- /dev/null +++ b/kotlin/templates/types/integer_enum.kt.jinja @@ -0,0 +1,49 @@ +{% set ty_name = type.name | to_upper_camel_case -%} +// This file is @generated +package com.svix.kotlin.models + +import kotlinx.serialization.Serializable +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerializationException +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.jsonPrimitive +import com.svix.kotlin.ToQueryParam + + +@Serializable(with = {{ ty_name }}Serializer::class) +enum class {{ ty_name }} : ToQueryParam { + {% for varname, value in type.variants -%} + {{ varname | to_upper_snake_case }} {% if loop.last %};{% else %},{% endif %} + {% endfor -%} + + override fun toQueryParam() = Json.encodeToJsonElement(this).jsonPrimitive.content +} + + +object {{ ty_name }}Serializer : KSerializer<{{ ty_name }}> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("com.svix.kotlin.models.{{ ty_name }}Serializer", PrimitiveKind.LONG) + + override fun serialize(encoder: Encoder, value: {{ ty_name }}) { + val vAsLong = when (value){ + {% for varname, value in type.variants -%} + {{ ty_name }}.{{ varname | to_upper_snake_case }} -> {{ value }}L + {% endfor -%} + } + encoder.encodeLong(vAsLong) + } + + override fun deserialize(decoder: Decoder): {{ ty_name }} { + return when(val vAsLong = decoder.decodeLong()){ + {% for varname, value in type.variants -%} + {{ value }}L -> {{ ty_name }}.{{ varname | to_upper_snake_case }} + {% endfor -%} + else -> { + throw SerializationException("$vAsLong is not a valid value for {{ ty_name }}") + } + } + } +} diff --git a/kotlin/templates/types/string_enum.kt.jinja b/kotlin/templates/types/string_enum.kt.jinja new file mode 100644 index 000000000..92808e4df --- /dev/null +++ b/kotlin/templates/types/string_enum.kt.jinja @@ -0,0 +1,22 @@ +// This file is @generated +package com.svix.kotlin.models + +import com.svix.kotlin.ToQueryParam +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.jsonPrimitive + + +@Serializable +enum class {{ type.name | to_upper_camel_case }} : ToQueryParam { + {% for value in type.values -%} + @SerialName("{{ value }}") + {{ value | to_upper_snake_case }} {% if loop.last %};{% else %},{% endif %} + {% endfor -%} + + override fun toQueryParam() = Json.encodeToJsonElement(this).jsonPrimitive.content + +} diff --git a/kotlin/templates/types/struct.kt.jinja b/kotlin/templates/types/struct.kt.jinja new file mode 100644 index 000000000..0f5171cf1 --- /dev/null +++ b/kotlin/templates/types/struct.kt.jinja @@ -0,0 +1,40 @@ +// This file is @generated +package com.svix.kotlin.models + +import com.svix.kotlin.MaybeUnset +import kotlinx.datetime.Instant +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonObject +import com.svix.kotlin.StringAnyMapSerializer +import com.svix.kotlin.MaybeUnsetStringAnyMapSerializer + + + +@Serializable +data class {{ type.name | to_upper_camel_case }}( +{% for field in type.fields -%} + {% set f_name = field.name | to_lower_camel_case -%} + {% set f_type = field.type.to_kotlin() -%} + {% set f_val = "" -%} + {% set use_nullable = type.name is endingwith "Patch" and field.nullable -%} + {% if use_nullable -%} + {% set f_type %}MaybeUnset<{{ f_type }}>{% endset -%} + {% set f_val = "= MaybeUnset.Unset" -%} + {% endif -%} + {% if (not field.required or field.nullable) and not use_nullable -%} + {% set f_type %}{{ f_type }}?{% endset -%} + {% set f_val = "= null" -%} + {% endif -%} + + {% if field.name | to_lower_camel_case != field.name -%} + @SerialName("{{ field.name }}") + {% endif -%} + {% if field.type.is_json_object() and not use_nullable -%} + @Serializable(with = StringAnyMapSerializer::class) + {% elif field.type.is_json_object() and use_nullable -%} + @Serializable(with = MaybeUnsetStringAnyMapSerializer::class) + {% endif -%} + val {{ field.name | to_lower_camel_case }}: {{ f_type }} {{ f_val }}, +{% endfor %} +) diff --git a/regen_openapi.sh b/regen_openapi.sh index f5e7018ed..e8208ed6a 100755 --- a/regen_openapi.sh +++ b/regen_openapi.sh @@ -18,6 +18,24 @@ if ! command -v openapi-codegen >/dev/null; then fi fi +# Kotlin +( + # Print commands we run + if [[ -z "$CI" ]]; then + set -x + + openapi-codegen generate \ + --template kotlin/templates/api_resource.kt.jinja \ + --input-file lib-openapi.json \ + --output-dir kotlin/lib/src/main/kotlin + openapi-codegen generate \ + --template kotlin/templates/component_type.kt.jinja \ + --input-file lib-openapi.json \ + --output-dir kotlin/lib/src/main/kotlin/models + + fi +) + # JavaScript ( # Print commands we run