Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 23 additions & 17 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,49 @@ on:
jobs:

build:
runs-on: macos-latest
strategy:
matrix:
os: [macos-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
uses: gradle/actions/wrapper-validation@v4

- name: Set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-read-only: ${{ github.ref != 'refs/heads/main' }}

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Build and run tests
run: ./gradlew build --warning-mode all

- name: Upload build outputs
if: always()
uses: actions/upload-artifact@v3
with:
name: build-outputs
path: app/build/outputs

- name: Upload reports
if: always()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: reports
name: reports-${{ matrix.os }}
path: |
**/build/reports/*

- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: test-results
name: test-results-${{ matrix.os }}
path: |
**/build/test-results/*

Expand All @@ -65,14 +66,19 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
cache-read-only: false

- name: Publish
run: ./gradlew publish --no-parallel
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Release Drafter
uses: release-drafter/release-drafter@v5.21.1
uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
83 changes: 83 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build and Development Commands

### Building the Project
```bash
# Build the entire project
./gradlew build

# Assemble without running tests
./gradlew assemble

# Clean build outputs
./gradlew clean
```

### Running Tests
```bash
# Run all tests
./gradlew allTests

# Run JVM tests only
./gradlew jvmTest

# Run specific platform tests
./gradlew jsTest # JavaScript tests
./gradlew iosX64Test # iOS X64 tests
./gradlew iosSimulatorArm64Test # iOS Simulator ARM64 tests
```

### Code Quality
```bash
# Run all checks (tests only)
./gradlew check
```

## Architecture Overview

This is a Kotlin Multiplatform library for the Trakt API, supporting JVM, JavaScript, and iOS platforms.

### Key Components

1. **Main Entry Point**: `Trakt` class (lib/src/commonMain/kotlin/app/moviebase/trakt/Trakt.kt:26)
- Factory function with DSL configuration
- Lazy-loaded API endpoints
- HTTP client configuration with interceptors

2. **API Structure**: Each API category has its own class in `app.moviebase.trakt.api`:
- `TraktMoviesApi` - Movie-related endpoints
- `TraktShowsApi` - TV show endpoints
- `TraktSeasonsApi` - Season endpoints
- `TraktEpisodesApi` - Episode endpoints
- `TraktSearchApi` - Search functionality
- `TraktUsersApi` - User-related operations
- `TraktSyncApi` - Synchronization features
- `TraktAuthApi` - Authentication handling

3. **Core Infrastructure**:
- `HttpClientFactory` - Creates configured Ktor HTTP clients
- `TraktClientConfig` - Configuration DSL for client setup
- Request interceptors for API key and version headers
- JSON serialization with kotlinx-serialization

4. **Platform-Specific Implementations**:
- JVM: Uses OkHttp engine
- iOS: Uses Darwin engine
- JS: Browser and Node.js support

### Dependency Management

The project uses Gradle version catalogs (gradle/libs.versions.toml) for dependency management:
- Kotlin 2.0.21
- Ktor 3.0.1 for HTTP client
- kotlinx-serialization 1.7.3
- kotlinx-datetime 0.6.1

### Testing Approach

- Unit tests in `jvmTest` using JUnit 5 and Google Truth
- Integration tests that interact with the actual Trakt API
- Test fixtures for mocked responses in `src/jvmTest/resources/trakt/`
23 changes: 0 additions & 23 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import com.diffplug.gradle.spotless.SpotlessExtension
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
Expand All @@ -9,7 +8,6 @@ plugins {
alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.dokka) apply false
alias(libs.plugins.ben.manes.versions) apply false
alias(libs.plugins.spotless) apply false
alias(libs.plugins.maven.publish) apply false
}

Expand All @@ -29,27 +27,6 @@ allprojects {
mavenLocal()
}

apply(
plugin =
rootProject.libs.plugins.spotless
.get()
.pluginId,
)

configure<SpotlessExtension> {
kotlin {
target("**/*.kt")
targetExclude("$buildDir/**/*.kt")
targetExclude("bin/**/*.kt")
ktlint(libs.versions.ktlint.get())
}
kotlinGradle {
target("**/*.kts")
targetExclude("$buildDir/**/*.kts")
ktlint(libs.versions.ktlint.get())
}
}

tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions {
// Treat all Kotlin warnings as errors
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ SONATYPE_AUTOMATIC_RELEASE=true
RELEASE_SIGNING_ENABLED=true

GROUP=app.moviebase
VERSION_NAME=0.7.1
VERSION_NAME=1.0.0-SNAPSHOT

POM_NAME=Trakt API for KMM
POM_DESCRIPTION=Kotlin Multiplatform library to access the Trakt API.
POM_INCEPTION_YEAR=2024
POM_INCEPTION_YEAR=2025

POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
Expand Down
3 changes: 0 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
[versions]
kotlin = "2.0.21"
spotless = "7.0.0.BETA4"
ben-manes-versions = "0.51.0"
dokka = "1.9.20"
kotlinx-datetime = "0.6.1"
kotlinx-serialization = "1.7.3"
coroutines = "1.9.0"
ktor = "3.0.1"
ktlint = "1.4.1"
junit-jupiter = "5.11.3"
truth = "1.4.4"
junit = "4.13.2"
Expand All @@ -17,7 +15,6 @@ maven-publish = "0.30.0"
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ben-manes-versions = { id = "com.github.ben-manes.versions", version.ref = "ben-manes-versions" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "maven-publish" }

Expand Down
32 changes: 8 additions & 24 deletions lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,38 +71,22 @@ kotlin {
}

val iosX64Main by getting
val iosArm64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting

val iosMain by creating {
dependsOn(commonMain)

iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)


configure(listOf(iosX64Main, iosArm64Main, iosSimulatorArm64Main)) {
dependencies {
implementation(libs.ktor.darwin)
}
}

val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting

val iosTest by creating {
dependsOn(commonTest)

iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = JavaVersion.VERSION_17.toString()
kotlinOptions.freeCompilerArgs += "-Xjvm-default=all"
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
freeCompilerArgs.add("-Xjvm-default=all")
}
}

tasks.withType<Test>().configureEach {
Expand All @@ -120,7 +104,7 @@ tasks.withType<DependencyUpdatesTask> {
}

fun isNonStable(version: String): Boolean {
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) }
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
val isStable = stableKeyword || regex.matches(version)
return isStable.not()
Expand Down
2 changes: 2 additions & 0 deletions lib/src/commonMain/kotlin/app/moviebase/trakt/Trakt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import app.moviebase.trakt.api.TraktCheckinApi
import app.moviebase.trakt.api.TraktCommentsApi
import app.moviebase.trakt.api.TraktEpisodesApi
import app.moviebase.trakt.api.TraktMoviesApi
import app.moviebase.trakt.api.TraktPeopleApi
import app.moviebase.trakt.api.TraktRecommendationsApi
import app.moviebase.trakt.api.TraktSearchApi
import app.moviebase.trakt.api.TraktSeasonsApi
Expand Down Expand Up @@ -48,6 +49,7 @@ class Trakt internal constructor(
val shows by buildApi(::TraktShowsApi)
val seasons by buildApi(::TraktSeasonsApi)
val episodes by buildApi(::TraktEpisodesApi)
val people by buildApi(::TraktPeopleApi)
val checkin by buildApi(::TraktCheckinApi)
val search by buildApi(::TraktSearchApi)
val users by buildApi(::TraktUsersApi)
Expand Down
19 changes: 14 additions & 5 deletions lib/src/commonMain/kotlin/app/moviebase/trakt/api/TraktAuthApi.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package app.moviebase.trakt.api

import app.moviebase.trakt.TraktClientConfig
import app.moviebase.trakt.core.postByPaths
import app.moviebase.trakt.core.endPoint
import app.moviebase.trakt.model.TraktAccessToken
import app.moviebase.trakt.model.TraktGrantType
import app.moviebase.trakt.model.TraktTokenRefreshRequest
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.contentType
Expand All @@ -15,10 +18,12 @@ class TraktAuthApi(
private val config: TraktClientConfig,
) {
suspend fun postToken(request: TraktTokenRefreshRequest): TraktAccessToken =
client.postByPaths("oauth", "token") {
contentType(ContentType.Application.Json)
setBody(request)
}
client
.post {
endPointOAuth("token")
contentType(ContentType.Application.Json)
setBody(request)
}.body()

suspend fun requestAccessToken(
redirectUri: String,
Expand Down Expand Up @@ -51,4 +56,8 @@ class TraktAuthApi(
)
return postToken(requestToken)
}

private fun HttpRequestBuilder.endPointOAuth(vararg paths: String) {
endPoint("oauth", *paths)
}
}
Loading
Loading