diff --git a/CHANGELOG.md b/CHANGELOG.md
index f3aa24a8..435676ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Change Log
+## 28.0.0
+
+* Breaking: removed `Account.listLogs` and the `Log`, `LogList` models
+* Breaking: removed `Client.setDevKey`
+* Breaking: SDK now targets Appwrite 2.3 (`x-appwrite-response-format: 2.3.0`)
+* Added: `Account.createIdTokenSession` for native Apple and Google sign-in with `IdTokenProvider` enum
+* Added: `Account.createRecoveryOTP` and `Account.updateRecoveryOTP` for code-based password recovery
+* Added: `Account.createEmailVerificationOTP` and `Account.updateEmailVerificationOTP`
+* Added: `KAKAO` and `TIKTOK` values to `OAuthProvider`
+* Added: `passwordPwned` field on `User` and `providerIdToken` field on `Identity`
+* Fixed: empty-string required path parameters are rejected instead of sent to the API
+
## 27.2.0
* Added: `DocumentsDB` and `VectorsDB` services for document and vector database access
diff --git a/README.md b/README.md
index e8e30481..c8fb25f0 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,12 @@


-
+
[](https://travis-ci.com/appwrite/sdk-generator)
[](https://twitter.com/appwrite)
[](https://appwrite.io/discord)
-**This SDK targets Appwrite server version 2.0.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-android/releases) when APIs differ.
+**This SDK targets Appwrite server version 2.3.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-android/releases) when APIs differ.
Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the Android SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)
@@ -38,7 +38,7 @@ repositories {
Next, add the dependency to your project's `build.gradle(.kts)` file:
```groovy
-implementation("io.appwrite:sdk-for-android:27.2.0")
+implementation("io.appwrite:sdk-for-android:28.0.0")
```
### Maven
@@ -49,7 +49,7 @@ Add this to your project's `pom.xml` file:
io.appwrite
sdk-for-android
- 27.2.0
+ 28.0.0
```
diff --git a/docs/examples/java/account/list-logs.md b/docs/examples/java/account/create-email-verification-otp.md
similarity index 86%
rename from docs/examples/java/account/list-logs.md
rename to docs/examples/java/account/create-email-verification-otp.md
index e8de501d..951b4bd8 100644
--- a/docs/examples/java/account/list-logs.md
+++ b/docs/examples/java/account/create-email-verification-otp.md
@@ -11,9 +11,8 @@ Client client = new Client(context)
Account account = new Account(client);
-account.listLogs(
- List.of(), // queries (optional)
- false, // total (optional)
+account.createEmailVerificationOTP(
+ false, // phrase (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
diff --git a/docs/examples/java/account/create-id-token-session.md b/docs/examples/java/account/create-id-token-session.md
new file mode 100644
index 00000000..457b9fee
--- /dev/null
+++ b/docs/examples/java/account/create-id-token-session.md
@@ -0,0 +1,32 @@
+```java
+import android.util.Log;
+
+import io.appwrite.Client;
+import io.appwrite.coroutines.CoroutineCallback;
+import io.appwrite.services.Account;
+import io.appwrite.enums.IdTokenProvider;
+
+Client client = new Client(context)
+ .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint
+ .setProject(""); // Your project ID
+
+Account account = new Account(client);
+
+account.createIdTokenSession(
+ IdTokenProvider.APPLE, // provider
+ "", // idToken
+ "", // nonce (optional)
+ "", // accessToken (optional)
+ 0, // accessTokenExpiry (optional)
+ "", // name (optional)
+ new CoroutineCallback<>((result, error) -> {
+ if (error != null) {
+ error.printStackTrace();
+ return;
+ }
+
+ Log.d("Appwrite", result.toString());
+ })
+);
+
+```
diff --git a/docs/examples/java/account/create-recovery-otp.md b/docs/examples/java/account/create-recovery-otp.md
new file mode 100644
index 00000000..66c165b8
--- /dev/null
+++ b/docs/examples/java/account/create-recovery-otp.md
@@ -0,0 +1,27 @@
+```java
+import android.util.Log;
+
+import io.appwrite.Client;
+import io.appwrite.coroutines.CoroutineCallback;
+import io.appwrite.services.Account;
+
+Client client = new Client(context)
+ .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint
+ .setProject(""); // Your project ID
+
+Account account = new Account(client);
+
+account.createRecoveryOTP(
+ "email@example.com", // email
+ false, // phrase (optional)
+ new CoroutineCallback<>((result, error) -> {
+ if (error != null) {
+ error.printStackTrace();
+ return;
+ }
+
+ Log.d("Appwrite", result.toString());
+ })
+);
+
+```
diff --git a/docs/examples/java/account/update-email-verification-otp.md b/docs/examples/java/account/update-email-verification-otp.md
new file mode 100644
index 00000000..d66521c8
--- /dev/null
+++ b/docs/examples/java/account/update-email-verification-otp.md
@@ -0,0 +1,27 @@
+```java
+import android.util.Log;
+
+import io.appwrite.Client;
+import io.appwrite.coroutines.CoroutineCallback;
+import io.appwrite.services.Account;
+
+Client client = new Client(context)
+ .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint
+ .setProject(""); // Your project ID
+
+Account account = new Account(client);
+
+account.updateEmailVerificationOTP(
+ "", // userId
+ "", // secret
+ new CoroutineCallback<>((result, error) -> {
+ if (error != null) {
+ error.printStackTrace();
+ return;
+ }
+
+ Log.d("Appwrite", result.toString());
+ })
+);
+
+```
diff --git a/docs/examples/java/account/update-recovery-otp.md b/docs/examples/java/account/update-recovery-otp.md
new file mode 100644
index 00000000..c16df289
--- /dev/null
+++ b/docs/examples/java/account/update-recovery-otp.md
@@ -0,0 +1,28 @@
+```java
+import android.util.Log;
+
+import io.appwrite.Client;
+import io.appwrite.coroutines.CoroutineCallback;
+import io.appwrite.services.Account;
+
+Client client = new Client(context)
+ .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint
+ .setProject(""); // Your project ID
+
+Account account = new Account(client);
+
+account.updateRecoveryOTP(
+ "", // userId
+ "", // secret
+ "password", // password
+ new CoroutineCallback<>((result, error) -> {
+ if (error != null) {
+ error.printStackTrace();
+ return;
+ }
+
+ Log.d("Appwrite", result.toString());
+ })
+);
+
+```
diff --git a/docs/examples/kotlin/account/list-logs.md b/docs/examples/kotlin/account/create-email-verification-otp.md
similarity index 76%
rename from docs/examples/kotlin/account/list-logs.md
rename to docs/examples/kotlin/account/create-email-verification-otp.md
index 0bccbf25..6acba146 100644
--- a/docs/examples/kotlin/account/list-logs.md
+++ b/docs/examples/kotlin/account/create-email-verification-otp.md
@@ -9,8 +9,7 @@ val client = Client(context)
val account = Account(client)
-val result = account.listLogs(
- queries = listOf(), // (optional)
- total = false, // (optional)
+val result = account.createEmailVerificationOTP(
+ phrase = false, // (optional)
)
```
diff --git a/docs/examples/kotlin/account/create-id-token-session.md b/docs/examples/kotlin/account/create-id-token-session.md
new file mode 100644
index 00000000..5eb6d0da
--- /dev/null
+++ b/docs/examples/kotlin/account/create-id-token-session.md
@@ -0,0 +1,21 @@
+```kotlin
+import io.appwrite.Client
+import io.appwrite.coroutines.CoroutineCallback
+import io.appwrite.services.Account
+import io.appwrite.enums.IdTokenProvider
+
+val client = Client(context)
+ .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint
+ .setProject("") // Your project ID
+
+val account = Account(client)
+
+val result = account.createIdTokenSession(
+ provider = IdTokenProvider.APPLE,
+ idToken = "",
+ nonce = "", // (optional)
+ accessToken = "", // (optional)
+ accessTokenExpiry = 0, // (optional)
+ name = "", // (optional)
+)
+```
diff --git a/docs/examples/kotlin/account/create-recovery-otp.md b/docs/examples/kotlin/account/create-recovery-otp.md
new file mode 100644
index 00000000..754baa87
--- /dev/null
+++ b/docs/examples/kotlin/account/create-recovery-otp.md
@@ -0,0 +1,16 @@
+```kotlin
+import io.appwrite.Client
+import io.appwrite.coroutines.CoroutineCallback
+import io.appwrite.services.Account
+
+val client = Client(context)
+ .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint
+ .setProject("") // Your project ID
+
+val account = Account(client)
+
+val result = account.createRecoveryOTP(
+ email = "email@example.com",
+ phrase = false, // (optional)
+)
+```
diff --git a/docs/examples/kotlin/account/update-email-verification-otp.md b/docs/examples/kotlin/account/update-email-verification-otp.md
new file mode 100644
index 00000000..639605bc
--- /dev/null
+++ b/docs/examples/kotlin/account/update-email-verification-otp.md
@@ -0,0 +1,16 @@
+```kotlin
+import io.appwrite.Client
+import io.appwrite.coroutines.CoroutineCallback
+import io.appwrite.services.Account
+
+val client = Client(context)
+ .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint
+ .setProject("") // Your project ID
+
+val account = Account(client)
+
+val result = account.updateEmailVerificationOTP(
+ userId = "",
+ secret = "",
+)
+```
diff --git a/docs/examples/kotlin/account/update-recovery-otp.md b/docs/examples/kotlin/account/update-recovery-otp.md
new file mode 100644
index 00000000..00b2ff0a
--- /dev/null
+++ b/docs/examples/kotlin/account/update-recovery-otp.md
@@ -0,0 +1,17 @@
+```kotlin
+import io.appwrite.Client
+import io.appwrite.coroutines.CoroutineCallback
+import io.appwrite.services.Account
+
+val client = Client(context)
+ .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint
+ .setProject("") // Your project ID
+
+val account = Account(client)
+
+val result = account.updateRecoveryOTP(
+ userId = "",
+ secret = "",
+ password = "password",
+)
+```
diff --git a/example/build.gradle.kts b/example/build.gradle.kts
index 8f1ca9b8..2b339ac0 100644
--- a/example/build.gradle.kts
+++ b/example/build.gradle.kts
@@ -41,9 +41,9 @@ dependencies {
implementation("androidx.appcompat:appcompat:1.8.0")
implementation("com.google.android.material:material:1.14.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.2")
- implementation("androidx.navigation:navigation-fragment-ktx:2.10.0")
+ implementation("androidx.navigation:navigation-fragment-ktx:2.10.1")
implementation("androidx.fragment:fragment-ktx:1.9.0")
- implementation("androidx.navigation:navigation-ui-ktx:2.10.0")
+ implementation("androidx.navigation:navigation-ui-ktx:2.10.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.11.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.11.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0")
diff --git a/library/build.gradle.kts b/library/build.gradle.kts
index a526d1f2..45641217 100644
--- a/library/build.gradle.kts
+++ b/library/build.gradle.kts
@@ -94,7 +94,7 @@ dependencies {
testImplementation("junit:junit:4.13.2")
testImplementation("androidx.test.ext:junit-ktx:1.3.0")
testImplementation("androidx.test:core-ktx:1.7.0")
- testImplementation("org.robolectric:robolectric:4.16.1")
+ testImplementation("org.robolectric:robolectric:4.17")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0")
}
diff --git a/library/src/main/java/io/appwrite/Client.kt b/library/src/main/java/io/appwrite/Client.kt
index 8b5455ea..40431217 100644
--- a/library/src/main/java/io/appwrite/Client.kt
+++ b/library/src/main/java/io/appwrite/Client.kt
@@ -96,8 +96,8 @@ class Client @JvmOverloads constructor(
"x-sdk-name" to "Android",
"x-sdk-platform" to "client",
"x-sdk-language" to "android",
- "x-sdk-version" to "27.2.0",
- "x-appwrite-response-format" to "2.0.0"
+ "x-sdk-version" to "28.0.0",
+ "x-appwrite-response-format" to "2.3.0"
)
config = mutableMapOf()
@@ -176,21 +176,6 @@ class Client @JvmOverloads constructor(
return this
}
- /**
- * Set DevKey
- *
- * Your secret dev API key
- *
- * @param {string} devkey
- *
- * @return this
- */
- fun setDevKey(value: String): Client {
- config["devKey"] = value
- addHeader("x-appwrite-dev-key", value)
- return this
- }
-
/**
* Set Cookie
*
diff --git a/library/src/main/java/io/appwrite/enums/IdTokenProvider.kt b/library/src/main/java/io/appwrite/enums/IdTokenProvider.kt
new file mode 100644
index 00000000..fb5d6f12
--- /dev/null
+++ b/library/src/main/java/io/appwrite/enums/IdTokenProvider.kt
@@ -0,0 +1,12 @@
+package io.appwrite.enums
+
+import com.google.gson.annotations.SerializedName
+
+enum class IdTokenProvider(val value: String) {
+ @SerializedName("apple")
+ APPLE("apple"),
+ @SerializedName("google")
+ GOOGLE("google");
+
+ override fun toString() = value
+}
diff --git a/library/src/main/java/io/appwrite/enums/OAuthProvider.kt b/library/src/main/java/io/appwrite/enums/OAuthProvider.kt
index e71acdf2..4eb5cbfd 100644
--- a/library/src/main/java/io/appwrite/enums/OAuthProvider.kt
+++ b/library/src/main/java/io/appwrite/enums/OAuthProvider.kt
@@ -47,6 +47,8 @@ enum class OAuthProvider(val value: String) {
GOOGLE("google"),
@SerializedName("huggingface")
HUGGINGFACE("huggingface"),
+ @SerializedName("kakao")
+ KAKAO("kakao"),
@SerializedName("keycloak")
KEYCLOAK("keycloak"),
@SerializedName("kick")
@@ -77,6 +79,8 @@ enum class OAuthProvider(val value: String) {
SPOTIFY("spotify"),
@SerializedName("stripe")
STRIPE("stripe"),
+ @SerializedName("tiktok")
+ TIKTOK("tiktok"),
@SerializedName("tradeshift")
TRADESHIFT("tradeshift"),
@SerializedName("tradeshiftBox")
diff --git a/library/src/main/java/io/appwrite/models/Execution.kt b/library/src/main/java/io/appwrite/models/Execution.kt
index 662c48ea..c698d9c6 100644
--- a/library/src/main/java/io/appwrite/models/Execution.kt
+++ b/library/src/main/java/io/appwrite/models/Execution.kt
@@ -113,7 +113,7 @@ data class Execution(
val errors: String,
/**
- * Resource(function/site) execution duration in seconds.
+ * Total time the resource(function/site) took to respond, in seconds.
*/
@SerializedName("duration")
val duration: Double,
diff --git a/library/src/main/java/io/appwrite/models/Identity.kt b/library/src/main/java/io/appwrite/models/Identity.kt
index ab70557a..011c32e4 100644
--- a/library/src/main/java/io/appwrite/models/Identity.kt
+++ b/library/src/main/java/io/appwrite/models/Identity.kt
@@ -67,6 +67,12 @@ data class Identity(
@SerializedName("providerRefreshToken")
val providerRefreshToken: String,
+ /**
+ * Identity Provider ID token (JWT) from the most recent native sign-in. Empty for identities created through the browser OAuth2 flow.
+ */
+ @SerializedName("providerIdToken")
+ val providerIdToken: String,
+
) {
fun toMap(): Map = mapOf(
"\$id" to id as Any,
@@ -79,6 +85,7 @@ data class Identity(
"providerAccessToken" to providerAccessToken as Any,
"providerAccessTokenExpiry" to providerAccessTokenExpiry as Any,
"providerRefreshToken" to providerRefreshToken as Any,
+ "providerIdToken" to providerIdToken as Any,
)
companion object {
@@ -96,6 +103,7 @@ data class Identity(
providerAccessToken = map["providerAccessToken"] as String,
providerAccessTokenExpiry = map["providerAccessTokenExpiry"] as String,
providerRefreshToken = map["providerRefreshToken"] as String,
+ providerIdToken = map["providerIdToken"] as String,
)
}
}
diff --git a/library/src/main/java/io/appwrite/models/Log.kt b/library/src/main/java/io/appwrite/models/Log.kt
deleted file mode 100644
index 586841c0..00000000
--- a/library/src/main/java/io/appwrite/models/Log.kt
+++ /dev/null
@@ -1,197 +0,0 @@
-package io.appwrite.models
-
-import com.google.gson.annotations.SerializedName
-import io.appwrite.extensions.jsonCast
-
-/**
- * Log
- */
-data class Log(
- /**
- * Event name.
- */
- @SerializedName("event")
- val event: String,
-
- /**
- * User ID of the actor recorded for this log. During impersonation, this is the original impersonator, not the impersonated target user.
- */
- @SerializedName("userId")
- val userId: String,
-
- /**
- * User email of the actor recorded for this log. During impersonation, this is the original impersonator.
- */
- @SerializedName("userEmail")
- val userEmail: String,
-
- /**
- * User name of the actor recorded for this log. During impersonation, this is the original impersonator.
- */
- @SerializedName("userName")
- val userName: String,
-
- /**
- * API mode when event triggered.
- */
- @SerializedName("mode")
- val mode: String,
-
- /**
- * User type who triggered the audit log. Possible values: user, admin, guest, hidden, keyProject, keyAccount, keyOrganization.
- */
- @SerializedName("userType")
- val userType: String,
-
- /**
- * IP session in use when the session was created.
- */
- @SerializedName("ip")
- val ip: String,
-
- /**
- * Log creation date in ISO 8601 format.
- */
- @SerializedName("time")
- val time: String,
-
- /**
- * Operating system code name. View list of [available options](https://github.com/appwrite/appwrite/blob/master/docs/lists/os.json).
- */
- @SerializedName("osCode")
- val osCode: String,
-
- /**
- * Operating system name.
- */
- @SerializedName("osName")
- val osName: String,
-
- /**
- * Operating system version.
- */
- @SerializedName("osVersion")
- val osVersion: String,
-
- /**
- * Client type.
- */
- @SerializedName("clientType")
- val clientType: String,
-
- /**
- * Client code name. View list of [available options](https://github.com/appwrite/appwrite/blob/master/docs/lists/clients.json).
- */
- @SerializedName("clientCode")
- val clientCode: String,
-
- /**
- * Client name.
- */
- @SerializedName("clientName")
- val clientName: String,
-
- /**
- * Client version.
- */
- @SerializedName("clientVersion")
- val clientVersion: String,
-
- /**
- * Client engine name.
- */
- @SerializedName("clientEngine")
- val clientEngine: String,
-
- /**
- * Client engine name.
- */
- @SerializedName("clientEngineVersion")
- val clientEngineVersion: String,
-
- /**
- * Device name.
- */
- @SerializedName("deviceName")
- val deviceName: String,
-
- /**
- * Device brand name.
- */
- @SerializedName("deviceBrand")
- val deviceBrand: String,
-
- /**
- * Device model name.
- */
- @SerializedName("deviceModel")
- val deviceModel: String,
-
- /**
- * Country two-character ISO 3166-1 alpha code.
- */
- @SerializedName("countryCode")
- val countryCode: String,
-
- /**
- * Country name.
- */
- @SerializedName("countryName")
- val countryName: String,
-
-) {
- fun toMap(): Map = mapOf(
- "event" to event as Any,
- "userId" to userId as Any,
- "userEmail" to userEmail as Any,
- "userName" to userName as Any,
- "mode" to mode as Any,
- "userType" to userType as Any,
- "ip" to ip as Any,
- "time" to time as Any,
- "osCode" to osCode as Any,
- "osName" to osName as Any,
- "osVersion" to osVersion as Any,
- "clientType" to clientType as Any,
- "clientCode" to clientCode as Any,
- "clientName" to clientName as Any,
- "clientVersion" to clientVersion as Any,
- "clientEngine" to clientEngine as Any,
- "clientEngineVersion" to clientEngineVersion as Any,
- "deviceName" to deviceName as Any,
- "deviceBrand" to deviceBrand as Any,
- "deviceModel" to deviceModel as Any,
- "countryCode" to countryCode as Any,
- "countryName" to countryName as Any,
- )
-
- companion object {
- @Suppress("UNCHECKED_CAST")
- fun from(
- map: Map,
- ) = Log(
- event = map["event"] as String,
- userId = map["userId"] as String,
- userEmail = map["userEmail"] as String,
- userName = map["userName"] as String,
- mode = map["mode"] as String,
- userType = map["userType"] as String,
- ip = map["ip"] as String,
- time = map["time"] as String,
- osCode = map["osCode"] as String,
- osName = map["osName"] as String,
- osVersion = map["osVersion"] as String,
- clientType = map["clientType"] as String,
- clientCode = map["clientCode"] as String,
- clientName = map["clientName"] as String,
- clientVersion = map["clientVersion"] as String,
- clientEngine = map["clientEngine"] as String,
- clientEngineVersion = map["clientEngineVersion"] as String,
- deviceName = map["deviceName"] as String,
- deviceBrand = map["deviceBrand"] as String,
- deviceModel = map["deviceModel"] as String,
- countryCode = map["countryCode"] as String,
- countryName = map["countryName"] as String,
- )
- }
-}
diff --git a/library/src/main/java/io/appwrite/models/LogList.kt b/library/src/main/java/io/appwrite/models/LogList.kt
deleted file mode 100644
index ef51d7a7..00000000
--- a/library/src/main/java/io/appwrite/models/LogList.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.appwrite.models
-
-import com.google.gson.annotations.SerializedName
-import io.appwrite.extensions.jsonCast
-
-/**
- * Logs List
- */
-data class LogList(
- /**
- * Total number of logs that matched your query.
- */
- @SerializedName("total")
- val total: Long,
-
- /**
- * List of logs.
- */
- @SerializedName("logs")
- val logs: List,
-
-) {
- fun toMap(): Map = mapOf(
- "total" to total as Any,
- "logs" to logs.map { it.toMap() } as Any,
- )
-
- companion object {
- @Suppress("UNCHECKED_CAST")
- fun from(
- map: Map,
- ) = LogList(
- total = (map["total"] as Number).toLong(),
- logs = (map["logs"] as List