Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: RedCrewOS/api-sdk-creator-mpp
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: http-api-client_v0.5.3
Choose a base ref
...
head repository: RedCrewOS/api-sdk-creator-mpp
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Loading
23 changes: 14 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: 'http-api-client'
concurrency: 'build'

on:
push:
@@ -29,19 +30,23 @@ jobs:
with:
arguments: :http-api-client:jvmTest :http-api-client:iosX64Test

#- name: Update docs
# if: ${{ github.ref == 'refs/heads/main' }}
# run: |
# ./gradlew dokkaHtml
# git add sdk/docs/
# git commit -m 'Updated docs' || echo "No changes to docs"
# git push
- name: Update docs
if: ${{ github.ref == 'refs/heads/main' }}
run: |
./gradlew dokkaHtml
git add http-api-client/docs/
git commit -m 'Updated docs' || echo "No changes to docs"
git push
- name: Tag
- name: Publish to Sonatype
if: ${{ github.ref == 'refs/heads/main' }}
run: ./scripts/tag.sh http-api-client
run: ./scripts/publish.sh http-api-client
env:
GH_AUTH_TOKEN: ${{ secrets.GH_AUTH_TOKEN }}
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SIGNING_PASSWORD }}

- name: Push changes
if: ${{ github.ref == 'refs/heads/main' }}
2 changes: 2 additions & 0 deletions .jitpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
install:
- ./gradlew publish -PmavPublishToMavenLocal=true
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -33,12 +33,14 @@ $ ./gradlew jvmTest iosX64Test

### JVM

JARs are available via [Jit Pack](https://jitpack.io/#RedCrewOS/api-sdk-creator-mpp). Being a multi-module project,
the module name is part of the identifier for the desired JAR. Jit Pack relies on Git tags to identify new versions, however tagging the whole repo for one module change impacts all modules if the tag was just a version string (eg: `v1.0.0`). Consequently, tags contain the module name, and the module version, so that different modules can be versioned and published separately.
JARs are available via [Maven Central](https://mvnrepository.com/).

Previous versions are available via JitPack however JitPack can't [publish MPP](https://github.com/jitpack/jitpack.io/issues/3853)
builds.

```groovy
dependencies {
implementation "com.github.RedCrewOS.api-sdk-creator-mpp:http-api-client:http-api-client_v0.5.0"
implementation "au.com.redcrew.apisdkcreator:http-api-client:0.5.4"
}
```

32 changes: 30 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
plugins {
id "org.jetbrains.dokka" version "1.4.0"
/*
* Currently this plugin can't be used until multi-module (monorepos) are supported.
*
* Consequently releases will have to be manually released from Nexus :'(
*/
//id "io.github.gradle-nexus.publish-plugin"
id "maven-publish"
id "org.jetbrains.dokka"
}

group "au.com.redcrew.apisdkcreator"

allprojects {
apply plugin: "maven-publish"

repositories {
mavenCentral()
maven { url "https://jitpack.io" }

// Needed for Dokka
jcenter()
}

publishing {
repositories {
maven {
def releasesUrl = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
def snapshotRepositoryUrl = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")

name = "deploy"
url = releasesUrl

credentials {
username = System.getenv("OSSRH_USERNAME") ?: ""
password = System.getenv("OSSRH_PASSWORD") ?: ""
}
}
}
}
}

ext {
arrowVersion = "1.0.1"
kotlinCoroutinesVersion = "1.6.0"
kotestVersion = "5.1.0"
}

65 changes: 59 additions & 6 deletions http-api-client/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
plugins {
id "io.kotest.multiplatform"
id "org.jetbrains.dokka"
id "org.jetbrains.kotlin.multiplatform"
id "io.kotest.multiplatform" version "5.0.2"
id "maven-publish"
id "org.jetbrains.dokka"
id "signing"
}

task javadoc(type: Javadoc) {

}

def javadocJar = task javadocJar(type: Jar) {
archiveClassifier.set("javadoc")
from javadoc
}

kotlin {
@@ -54,9 +64,8 @@ kotlin {
}

jvmMain {
// we publish submodules in this group.
group "au.com.redcrew.apisdkcreator"
version "0.5.3"
group rootProject.group
version "0.6.0"
}

jvmTest {
@@ -81,5 +90,49 @@ dokkaHtml {
outputDirectory.set(new File("${projectDir}/docs"))

// TODO When Dokka >= 1.4.10 is published
//moduleName.set(ApiSdkCreator MPP")
//moduleName ApiSdkCreator MPP")
}

publishing {
publications.forEach {
it.artifact(javadocJar)

it.pom {
name.set("HttpApiClient")
description.set("Kotlin library to aid developers in the creation of API client SDKs. ")
url.set("https://github.com/RedCrewOS/api-sdk-creator-mpp")

scm {
connection.set("scm:git:https://github.com/RedCrewOS/api-sdk-creator-mpp/")
developerConnection.set("scm:git:https://github.com/kierans/")
url.set("https://github.com/RedCrewOS/api-sdk-creator-mpp")
}

licenses {
license {
name.set("MIT")
url.set("https://mit-license.org/")
}
}

developers {
developer {
id.set("kierans")
name.set("Kieran Simpson")
email.set("kieran@redcrew.com.au")
}
}
}
}
}

signing {
useGpgCmd()

def signingKey = findProperty("signingKey")
def signingPassword = findProperty("signingPassword")

useInMemoryPgpKeys(signingKey, signingPassword)

sign publishing.publications
}
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
<div class="cover ">
<div class="filter-section" id="filter-section"><button class="platform-tag platform-selector jvm-like" data-active="" data-filter=":http-api-client/commonMain">common</button></div>
<h1 class="cover"><a data-name="generictypecurriedfunction"></a><span>GenericTypeCurriedFunction</span></h1>
<div class="platform-hinted " data-platform-hinted="data-platform-hinted"><div class="content sourceset-depenent-content" data-active="" data-togglable=":http-api-client/commonMain"><p class="paragraph"><p class="paragraph">Because Kotlin does not allow anonymous functions/lambdas to have generic type parameters, we are unable to write curried functions where the generic type does not appear as part of the overall function signature.</p><p class="paragraph">For example, the following code does not compile.</p><div class="sample-container"><code class="" theme="idea"><pre>fun gsonUnmarshaller(gson: Gson): (KClass&lt;T&gt;) -&gt; Unmarshaller&lt;T&gt;</pre></code></div><p class="paragraph">The only option to make it compile is to add &lt;T&gt; to the named function, to make &lt;T&gt; part of the overall function signature; in this case <code>gsonUnmarshaller</code>. This however would bind all resulting functions to one type when the first function is called. If <code>gsonUnmarshaller</code> was called with <code>String</code> then we can only ever call the result with <code>KClass&lt;String&gt;</code> leading to an <code>Unmarshaller&lt;String&gt;</code></p><p class="paragraph">This inhibits reuse and the ability to partially apply functions and pass the result to other functions which may want to work on a set of types that are not known to where the partial application is performed.</p><p class="paragraph">For example, we'd want to pass an initial Gson configuration at the start of a pipeline to specify how all API pipelines are to marshall/unmarshall JSON bodies according to the SDK/application needs. We then would want to pass the curried function to SDK specific functions to add steps to the pipeline when the result type of the SDK operation is known, while not having to perform any casts and maintain type safety.</p><p class="paragraph">The solution is to take advantage of Kotlin's operator overloading to provide an object that knows what to do when invoked; which makes it look like a function. This delays the binding of the generic type until &quot;the function&quot; is invoked, or where the implementation class's <code>invoke</code> method is called, rather than when the curried signature is defined.</p><p class="paragraph">It adds an extra layer of indirection for implementation readers but results in nice type safe code in the caller. Functions that utilise this technique for curried functions with generic type parameters should document the function signature they are emulating using Hindley-Milner, which is the defacto way of specifying function type signatures.</p></p><div class="symbol monospace">interface <a href="index.html">GenericTypeCurriedFunction</a><span class="top-right-position"><span class="copy-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<div class="platform-hinted " data-platform-hinted="data-platform-hinted"><div class="content sourceset-depenent-content" data-active="" data-togglable=":http-api-client/commonMain"><p class="paragraph"><p class="paragraph">Because Kotlin does not allow anonymous functions/lambdas to have generic type parameters, we are unable to write curried functions where the generic type does not appear as part of the overall function signature.</p><p class="paragraph">For example, the following code does not compile.</p><div class="sample-container"><code class="" theme="idea"><pre>fun gsonUnmarshaller(gson: Gson): (KClass&lt;T&gt;) -&gt; Unmarshaller&lt;T&gt;</pre></code></div><p class="paragraph">The only option here to make it compile is to add <code>&lt;T&gt;</code> to the named function, to make <code>&lt;T&gt;</code> part of the overall function signature; in this case <code>gsonUnmarshaller</code>. For example,</p><div class="sample-container"><code class="" theme="idea"><pre>fun &lt;T&gt; gsonUnmarshaller(gson: Gson): (KClass&lt;T&gt;) -&gt; Unmarshaller&lt;T&gt;</pre></code></div><p class="paragraph">This however would bind all resulting functions to one type when the first function is called. If <code>gsonUnmarshaller</code> was called with <code>String</code> then we can only ever call the result with <code>KClass&lt;String&gt;</code> leading to an <code>Unmarshaller&lt;String&gt;</code></p><p class="paragraph">This inhibits reuse and the ability to partially apply functions and pass the result to other functions which may want to work on a set of types that are not known to where the partial application is performed.</p><p class="paragraph">For example, we'd want to pass an initial Gson configuration at the start of a pipeline to specify how all API pipelines are to marshall/unmarshall JSON bodies according to the SDK/application needs. We then would want to pass the curried function to SDK specific functions to add steps to the pipeline when the result type of the SDK operation is known, while not having to perform any casts and maintain type safety.</p><p class="paragraph">The solution is to take advantage of Kotlin's operator overloading to provide an object that knows what to do when invoked; which makes it look like a function. This delays the binding of the generic type until &quot;the function&quot; is invoked, or where the implementation class's <code>invoke</code> method is called, rather than when the curried signature is defined.</p><p class="paragraph">It adds an extra layer of indirection for implementation readers but results in nice type safe code in the caller. Functions that utilise this technique for curried functions with generic type parameters should document the function signature they are emulating using Hindley-Milner, which is the defacto way of specifying function type signatures.</p></p><div class="symbol monospace">interface <a href="index.html">GenericTypeCurriedFunction</a><span class="top-right-position"><span class="copy-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 4H15V16H5V4ZM17 7H19V18V20H17H8V18H17V7Z" fill="black"/>
</svg></span><div class="copy-popup-wrapper popup-to-left"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 9C18 14 14 18 9 18C4 18 0 14 0 9C0 4 4 0 9 0C14 0 18 4 18 9ZM14.2 6.2L12.8 4.8L7.5 10.1L5.3 7.8L3.8 9.2L7.5 13L14.2 6.2Z" fill="#4DBB5F"/>
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ <h2 class=""><a data-name="types"></a>Types</h2>
</div>
<div class="platform-dependent-row keyValue">
<div></div>
<div class="title"><div class="divergent-group" data-filterable-current=":http-api-client/commonMain" data-filterable-set=":http-api-client/commonMain"><div class="brief-with-platform-tags"><div class="inner-brief-with-platform-tags"><div class="brief "><p class="paragraph"><p class="paragraph">Because Kotlin does not allow anonymous functions/lambdas to have generic type parameters, we are unable to write curried functions where the generic type does not appear as part of the overall function signature.</p><p class="paragraph">For example, the following code does not compile.</p><div class="sample-container"><code class="" theme="idea"><pre>fun gsonUnmarshaller(gson: Gson): (KClass&lt;T&gt;) -&gt; Unmarshaller&lt;T&gt;</pre></code></div><p class="paragraph">The only option to make it compile is to add &lt;T&gt; to the named function, to make &lt;T&gt; part of the overall function signature; in this case <code>gsonUnmarshaller</code>. This however would bind all resulting functions to one type when the first function is called. If <code>gsonUnmarshaller</code> was called with <code>String</code> then we can only ever call the result with <code>KClass&lt;String&gt;</code> leading to an <code>Unmarshaller&lt;String&gt;</code></p><p class="paragraph">This inhibits reuse and the ability to partially apply functions and pass the result to other functions which may want to work on a set of types that are not known to where the partial application is performed.</p><p class="paragraph">For example, we'd want to pass an initial Gson configuration at the start of a pipeline to specify how all API pipelines are to marshall/unmarshall JSON bodies according to the SDK/application needs. We then would want to pass the curried function to SDK specific functions to add steps to the pipeline when the result type of the SDK operation is known, while not having to perform any casts and maintain type safety.</p><p class="paragraph">The solution is to take advantage of Kotlin's operator overloading to provide an object that knows what to do when invoked; which makes it look like a function. This delays the binding of the generic type until &quot;the function&quot; is invoked, or where the implementation class's <code>invoke</code> method is called, rather than when the curried signature is defined.</p><p class="paragraph">It adds an extra layer of indirection for implementation readers but results in nice type safe code in the caller. Functions that utilise this technique for curried functions with generic type parameters should document the function signature they are emulating using Hindley-Milner, which is the defacto way of specifying function type signatures.</p></p></div></div>
<div class="title"><div class="divergent-group" data-filterable-current=":http-api-client/commonMain" data-filterable-set=":http-api-client/commonMain"><div class="brief-with-platform-tags"><div class="inner-brief-with-platform-tags"><div class="brief "><p class="paragraph"><p class="paragraph">Because Kotlin does not allow anonymous functions/lambdas to have generic type parameters, we are unable to write curried functions where the generic type does not appear as part of the overall function signature.</p><p class="paragraph">For example, the following code does not compile.</p><div class="sample-container"><code class="" theme="idea"><pre>fun gsonUnmarshaller(gson: Gson): (KClass&lt;T&gt;) -&gt; Unmarshaller&lt;T&gt;</pre></code></div><p class="paragraph">The only option here to make it compile is to add <code>&lt;T&gt;</code> to the named function, to make <code>&lt;T&gt;</code> part of the overall function signature; in this case <code>gsonUnmarshaller</code>. For example,</p><div class="sample-container"><code class="" theme="idea"><pre>fun &lt;T&gt; gsonUnmarshaller(gson: Gson): (KClass&lt;T&gt;) -&gt; Unmarshaller&lt;T&gt;</pre></code></div><p class="paragraph">This however would bind all resulting functions to one type when the first function is called. If <code>gsonUnmarshaller</code> was called with <code>String</code> then we can only ever call the result with <code>KClass&lt;String&gt;</code> leading to an <code>Unmarshaller&lt;String&gt;</code></p><p class="paragraph">This inhibits reuse and the ability to partially apply functions and pass the result to other functions which may want to work on a set of types that are not known to where the partial application is performed.</p><p class="paragraph">For example, we'd want to pass an initial Gson configuration at the start of a pipeline to specify how all API pipelines are to marshall/unmarshall JSON bodies according to the SDK/application needs. We then would want to pass the curried function to SDK specific functions to add steps to the pipeline when the result type of the SDK operation is known, while not having to perform any casts and maintain type safety.</p><p class="paragraph">The solution is to take advantage of Kotlin's operator overloading to provide an object that knows what to do when invoked; which makes it look like a function. This delays the binding of the generic type until &quot;the function&quot; is invoked, or where the implementation class's <code>invoke</code> method is called, rather than when the curried signature is defined.</p><p class="paragraph">It adds an extra layer of indirection for implementation readers but results in nice type safe code in the caller. Functions that utilise this technique for curried functions with generic type parameters should document the function signature they are emulating using Hindley-Milner, which is the defacto way of specifying function type signatures.</p></p></div></div>
<span class="pull-right"></span></div>

<div class="main-subrow">
Loading