diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d7ecb637d..1fd0cb6be 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: php-version: ['8.2'] - sdk: [Android5Java17, Android14Java17, CLINode16, CLINode18, DartBeta, DartStable, Deno1193, Deno1303, DotNet60, DotNet70, FlutterStable, FlutterBeta, Go112, Go118, KotlinJava8, KotlinJava11, KotlinJava17, Node16, Node18, Node20, PHP74, PHP80, Python38, Python39, Python310, Ruby27, Ruby30, Ruby31, AppleSwift55, Swift55, WebChromium, WebNode] + sdk: [Android5Java17, Android14Java17, CLINode16, CLINode18, DartBeta, DartStable, Deno1193, Deno1303, DotNet60, DotNet70, FlutterStable, FlutterBeta, Go112, Go118, KotlinJava8, KotlinJava11, KotlinJava17, Node16, Node18, Node20, PHP74, PHP80, Python38, Python39, Python310, Ruby27, Ruby30, Ruby31, AppleSwift56, Swift56, WebChromium, WebNode] steps: - name: Checkout repository diff --git a/src/SDK/Language/Android.php b/src/SDK/Language/Android.php index 5bbd8f724..0dec10358 100644 --- a/src/SDK/Language/Android.php +++ b/src/SDK/Language/Android.php @@ -145,11 +145,6 @@ public function getFiles(): array 'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/extensions/CollectionExtensions.kt', 'template' => '/android/library/src/main/java/io/package/extensions/CollectionExtensions.kt.twig', ], - [ - 'scope' => 'default', - 'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/json/PreciseNumberAdapter.kt', - 'template' => '/android/library/src/main/java/io/package/json/PreciseNumberAdapter.kt.twig', - ], [ 'scope' => 'default', 'destination' => '/library/src/main/java/{{ sdk.namespace | caseSlash }}/models/InputFile.kt', diff --git a/src/SDK/Language/Apple.php b/src/SDK/Language/Apple.php index 0e17c47f9..9beff4b80 100644 --- a/src/SDK/Language/Apple.php +++ b/src/SDK/Language/Apple.php @@ -33,7 +33,7 @@ public function getFiles(): array [ 'scope' => 'default', 'destination' => 'Package.swift', - 'template' => 'swift/Package.swift.twig', + 'template' => 'apple/Package.swift.twig', ], [ 'scope' => 'method', @@ -246,16 +246,6 @@ public function getFiles(): array 'destination' => '/Sources/{{ spec.title | caseUcfirst}}/WebSockets/WebSocketClientError.swift', 'template' => '/swift/Sources/WebSockets/WebSocketClientError.swift.twig', ], - [ - 'scope' => 'default', - 'destination' => '/Sources/{{ spec.title | caseUcfirst}}/{{ spec.title | caseUcfirst }}Delegate.swift', - 'template' => '/swift/Sources/Delegate.swift.twig', - ], - [ - 'scope' => 'default', - 'destination' => '/Sources/{{ spec.title | caseUcfirst}}/NotificationHandler.swift', - 'template' => '/swift/Sources/NotificationHandler.swift.twig', - ], // Config for project example-swiftui [ 'scope' => 'default', diff --git a/src/SDK/Language/Kotlin.php b/src/SDK/Language/Kotlin.php index 1331d8617..ac423e356 100644 --- a/src/SDK/Language/Kotlin.php +++ b/src/SDK/Language/Kotlin.php @@ -386,11 +386,6 @@ public function getFiles(): array 'template' => '/kotlin/src/main/kotlin/io/appwrite/extensions/TypeExtensions.kt.twig', 'minify' => false, ], - [ - 'scope' => 'default', - 'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/json/PreciseNumberAdapter.kt', - 'template' => '/kotlin/src/main/kotlin/io/appwrite/json/PreciseNumberAdapter.kt.twig', - ], [ 'scope' => 'default', 'destination' => '/src/main/kotlin/{{ sdk.namespace | caseSlash }}/services/Service.kt', diff --git a/templates/android/library/src/main/java/io/package/extensions/JsonExtensions.kt.twig b/templates/android/library/src/main/java/io/package/extensions/JsonExtensions.kt.twig index 9f6faec45..14ad26726 100644 --- a/templates/android/library/src/main/java/io/package/extensions/JsonExtensions.kt.twig +++ b/templates/android/library/src/main/java/io/package/extensions/JsonExtensions.kt.twig @@ -2,14 +2,12 @@ package {{ sdk.namespace | caseDot }}.extensions import com.google.gson.Gson import com.google.gson.GsonBuilder +import com.google.gson.ToNumberPolicy import com.google.gson.reflect.TypeToken -import {{ sdk.namespace | caseDot }}.json.PreciseNumberAdapter val gson: Gson = GsonBuilder() - .registerTypeAdapter( - object : TypeToken>() {}.type, - PreciseNumberAdapter() - ) + .setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) + .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) .create() fun Any.toJson(): String = diff --git a/templates/android/library/src/main/java/io/package/json/PreciseNumberAdapter.kt.twig b/templates/android/library/src/main/java/io/package/json/PreciseNumberAdapter.kt.twig deleted file mode 100644 index 09cb786cf..000000000 --- a/templates/android/library/src/main/java/io/package/json/PreciseNumberAdapter.kt.twig +++ /dev/null @@ -1,64 +0,0 @@ -package {{ sdk.namespace | caseDot }}.json - -import com.google.gson.Gson -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonToken.* -import com.google.gson.stream.JsonWriter -import java.io.IOException - -internal class PreciseNumberAdapter : TypeAdapter() { - - private val delegate = Gson() - .getAdapter(Any::class.java) - - @Throws(IOException::class) - override fun write(out: JsonWriter?, value: Any?) { - delegate.write(out, value) - } - - @Throws(IOException::class) - override fun read(input: JsonReader): Any? { - return when (input.peek()) { - BEGIN_ARRAY -> { - val list = mutableListOf() - input.beginArray() - while (input.hasNext()) { - list.add(read(input)) - } - input.endArray() - list - } - BEGIN_OBJECT -> { - val map = mutableMapOf() - input.beginObject() - while (input.hasNext()) { - map[input.nextName()] = read(input) - } - input.endObject() - map - } - STRING -> { - input.nextString() - } - NUMBER -> { - val numberString = input.nextString() - if (numberString.indexOf('.') != -1) { - numberString.toDouble() - } else { - numberString.toLong() - } - } - BOOLEAN -> { - input.nextBoolean() - } - NULL -> { - input.nextNull() - null - } - else -> { - throw IllegalStateException() - } - } - } -} \ No newline at end of file diff --git a/templates/apple/Package.swift.twig b/templates/apple/Package.swift.twig new file mode 100644 index 000000000..132ae387f --- /dev/null +++ b/templates/apple/Package.swift.twig @@ -0,0 +1,67 @@ +// swift-tools-version:5.1 + +import PackageDescription + +let package = Package( + name: "{{spec.title | caseUcfirst}}", + platforms: [ + .iOS("15.0"), + .macOS("11.0"), + .watchOS("7.0"), + .tvOS("13.0"), + ], + products: [ + .library( + name: "{{spec.title | caseUcfirst}}", + targets: [ + "{{spec.title | caseUcfirst}}", + "{{spec.title | caseUcfirst}}Enums", + "{{spec.title | caseUcfirst}}Models", + "JSONCodable" + ] + ), + ], + dependencies: [ + .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.9.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.32.0"), + ], + targets: [ + .target( + name: "{{spec.title | caseUcfirst}}", + dependencies: [ + .product(name: "AsyncHTTPClient", package: "async-http-client"), + .product(name: "NIOWebSocket", package: "swift-nio"), + {%~ if spec.definitions is not empty %} + "{{spec.title | caseUcfirst}}Models", + {%~ endif %} + {%~ if spec.enums is not empty %} + "{{spec.title | caseUcfirst}}Enums", + {%~ endif %} + "JSONCodable" + ] + ), + {%~ if spec.definitions is not empty %} + .target( + name: "{{spec.title | caseUcfirst}}Models", + dependencies: [ + "JSONCodable" + ] + ), + {%~ endif %} + {%~ if spec.enums is not empty %} + .target( + name: "{{spec.title | caseUcfirst}}Enums" + ), + {%~ endif %} + .target( + name: "JSONCodable" + ), + .testTarget( + name: "{{spec.title | caseUcfirst}}Tests", + dependencies: [ + "{{ spec.title | caseUcfirst }}" + ] + ) + ], + swiftLanguageVersions: [.v5] +) \ No newline at end of file diff --git a/templates/apple/Sources/Client.swift.twig b/templates/apple/Sources/Client.swift.twig index 7a07a5a83..54db98633 100644 --- a/templates/apple/Sources/Client.swift.twig +++ b/templates/apple/Sources/Client.swift.twig @@ -53,8 +53,6 @@ open class Client { http = Client.createHTTP() addUserAgentHeader() addOriginHeader() - - NotificationHandler.shared.client = self } private static func createHTTP( @@ -88,7 +86,6 @@ open class Client { decompression: .enabled(limit: .none) ) ) - } deinit { @@ -165,19 +162,6 @@ open class Client { return self } - /// - /// Set push provider ID. - /// - /// @param String endpoint - /// - /// @return this - /// - open func setPushProviderId(_ providerId: String) -> Client { - NotificationHandler.shared.providerId = providerId - - return self - } - /// /// Add header /// diff --git a/templates/dart/lib/query.dart.twig b/templates/dart/lib/query.dart.twig index 01a0c3bdf..4fe939e8c 100644 --- a/templates/dart/lib/query.dart.twig +++ b/templates/dart/lib/query.dart.twig @@ -84,7 +84,7 @@ class Query { Query._('contains', attribute, value).toString(); static String or(List queries) => - Query._('and', null, queries.map((query) => jsonDecode(query)).toList()).toString(); + Query._('or', null, queries.map((query) => jsonDecode(query)).toList()).toString(); static String and(List queries) => Query._('and', null, queries.map((query) => jsonDecode(query)).toList()).toString(); diff --git a/templates/dotnet/src/Appwrite/Query.cs.twig b/templates/dotnet/src/Appwrite/Query.cs.twig index 99eb2eec9..3b26a02a1 100644 --- a/templates/dotnet/src/Appwrite/Query.cs.twig +++ b/templates/dotnet/src/Appwrite/Query.cs.twig @@ -17,10 +17,16 @@ namespace Appwrite this.method = method; this.attribute = attribute; - if (values == null || values is IList) + if (values is IList valuesList) + { + this.values = new List(); + foreach (var value in valuesList) + { + this.values.Add(value); // Automatically boxes if value is a value type + } + } + else if (values != null) { - this.values = (List?)values; - } else { this.values = new List { values }; } } diff --git a/templates/kotlin/build.gradle.twig b/templates/kotlin/build.gradle.twig index 2eb330f26..b5feaed62 100644 --- a/templates/kotlin/build.gradle.twig +++ b/templates/kotlin/build.gradle.twig @@ -38,7 +38,7 @@ dependencies { implementation("com.squareup.okhttp3:logging-interceptor") implementation("com.google.code.gson:gson:2.9.0") - testImplementation 'org.jetbrains.kotlin:kotlin-test-junit' + testImplementation("org.jetbrains.kotlin:kotlin-test-junit") } test { diff --git a/templates/kotlin/gradle/wrapper/gradle-wrapper.properties b/templates/kotlin/gradle/wrapper/gradle-wrapper.properties index 2bbac7dd7..60c76b340 100644 --- a/templates/kotlin/gradle/wrapper/gradle-wrapper.properties +++ b/templates/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/templates/kotlin/src/main/kotlin/io/appwrite/Client.kt.twig b/templates/kotlin/src/main/kotlin/io/appwrite/Client.kt.twig index 7181175ca..6be1ed132 100644 --- a/templates/kotlin/src/main/kotlin/io/appwrite/Client.kt.twig +++ b/templates/kotlin/src/main/kotlin/io/appwrite/Client.kt.twig @@ -1,10 +1,8 @@ package {{ sdk.namespace | caseDot }} -import com.google.gson.GsonBuilder -import com.google.gson.reflect.TypeToken import {{ sdk.namespace | caseDot }}.exceptions.{{ spec.title | caseUcfirst }}Exception import {{ sdk.namespace | caseDot }}.extensions.fromJson -import {{ sdk.namespace | caseDot }}.json.PreciseNumberAdapter +import {{ sdk.namespace | caseDot }}.extensions.toJson import {{ sdk.namespace | caseDot }}.models.InputFile import {{ sdk.namespace | caseDot }}.models.UploadProgress import kotlinx.coroutines.CoroutineScope @@ -47,11 +45,6 @@ class Client @JvmOverloads constructor( private val job = Job() - private val gson = GsonBuilder().registerTypeAdapter( - object : TypeToken>(){}.type, - PreciseNumberAdapter() - ).create() - lateinit var http: OkHttpClient private val headers: MutableMap @@ -252,7 +245,8 @@ class Client @JvmOverloads constructor( } builder.build() } else { - gson.toJson(filteredParams) + filteredParams + .toJson() .toRequestBody("application/json".toMediaType()) } @@ -422,10 +416,8 @@ class Client @JvmOverloads constructor( .use(BufferedReader::readText) val error = if (response.headers["content-type"]?.contains("application/json") == true) { - val map = gson.fromJson>( - body, - object : TypeToken>(){}.type - ) + val map = body.fromJson>() + {{ spec.title | caseUcfirst }}Exception( map["message"] as? String ?: "", (map["code"] as Number).toInt(), @@ -464,10 +456,7 @@ class Client @JvmOverloads constructor( it.resume(true as T) return } - val map = gson.fromJson>( - body, - object : TypeToken>(){}.type - ) + val map = body.fromJson>() it.resume( converter?.invoke(map) ?: map as T ) diff --git a/templates/kotlin/src/main/kotlin/io/appwrite/extensions/JsonExtensions.kt.twig b/templates/kotlin/src/main/kotlin/io/appwrite/extensions/JsonExtensions.kt.twig index 48e536b3a..14ad26726 100644 --- a/templates/kotlin/src/main/kotlin/io/appwrite/extensions/JsonExtensions.kt.twig +++ b/templates/kotlin/src/main/kotlin/io/appwrite/extensions/JsonExtensions.kt.twig @@ -1,8 +1,14 @@ package {{ sdk.namespace | caseDot }}.extensions import com.google.gson.Gson - -val gson = Gson() +import com.google.gson.GsonBuilder +import com.google.gson.ToNumberPolicy +import com.google.gson.reflect.TypeToken + +val gson: Gson = GsonBuilder() + .setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) + .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) + .create() fun Any.toJson(): String = gson.toJson(this) diff --git a/templates/kotlin/src/main/kotlin/io/appwrite/json/PreciseNumberAdapter.kt.twig b/templates/kotlin/src/main/kotlin/io/appwrite/json/PreciseNumberAdapter.kt.twig deleted file mode 100644 index 09cb786cf..000000000 --- a/templates/kotlin/src/main/kotlin/io/appwrite/json/PreciseNumberAdapter.kt.twig +++ /dev/null @@ -1,64 +0,0 @@ -package {{ sdk.namespace | caseDot }}.json - -import com.google.gson.Gson -import com.google.gson.TypeAdapter -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonToken.* -import com.google.gson.stream.JsonWriter -import java.io.IOException - -internal class PreciseNumberAdapter : TypeAdapter() { - - private val delegate = Gson() - .getAdapter(Any::class.java) - - @Throws(IOException::class) - override fun write(out: JsonWriter?, value: Any?) { - delegate.write(out, value) - } - - @Throws(IOException::class) - override fun read(input: JsonReader): Any? { - return when (input.peek()) { - BEGIN_ARRAY -> { - val list = mutableListOf() - input.beginArray() - while (input.hasNext()) { - list.add(read(input)) - } - input.endArray() - list - } - BEGIN_OBJECT -> { - val map = mutableMapOf() - input.beginObject() - while (input.hasNext()) { - map[input.nextName()] = read(input) - } - input.endObject() - map - } - STRING -> { - input.nextString() - } - NUMBER -> { - val numberString = input.nextString() - if (numberString.indexOf('.') != -1) { - numberString.toDouble() - } else { - numberString.toLong() - } - } - BOOLEAN -> { - input.nextBoolean() - } - NULL -> { - input.nextNull() - null - } - else -> { - throw IllegalStateException() - } - } - } -} \ No newline at end of file diff --git a/templates/php/src/Query.php.twig b/templates/php/src/Query.php.twig index aa161e1ab..cff70af56 100644 --- a/templates/php/src/Query.php.twig +++ b/templates/php/src/Query.php.twig @@ -44,7 +44,7 @@ class Query implements \JsonSerializable */ public static function equal(string $attribute, $value): string { - return new Query('equal', $attribute, $value).__toString(); + return (new Query('equal', $attribute, $value))->__toString(); } /** @@ -56,7 +56,7 @@ class Query implements \JsonSerializable */ public static function notEqual(string $attribute, $value): string { - return new Query('notEqual', $attribute, $value).__toString(); + return (new Query('notEqual', $attribute, $value))->__toString(); } /** @@ -68,7 +68,7 @@ class Query implements \JsonSerializable */ public static function lessThan(string $attribute, $value): string { - return new Query('lessThan', $attribute, $value).__toString(); + return (new Query('lessThan', $attribute, $value))->__toString(); } /** @@ -80,7 +80,7 @@ class Query implements \JsonSerializable */ public static function lessThanEqual(string $attribute, $value): string { - return new Query('lessThanEqual', $attribute, $value).__toString(); + return (new Query('lessThanEqual', $attribute, $value))->__toString(); } /** @@ -92,7 +92,7 @@ class Query implements \JsonSerializable */ public static function greaterThan(string $attribute, $value): string { - return new Query('greaterThan', $attribute, $value).__toString(); + return (new Query('greaterThan', $attribute, $value))->__toString(); } /** @@ -104,7 +104,7 @@ class Query implements \JsonSerializable */ public static function greaterThanEqual(string $attribute, $value): string { - return new Query('greaterThanEqual', $attribute, $value).__toString(); + return (new Query('greaterThanEqual', $attribute, $value))->__toString(); } /** @@ -116,7 +116,7 @@ class Query implements \JsonSerializable */ public static function search(string $attribute, string $value): string { - return new Query('search', $attribute, $value).__toString(); + return (new Query('search', $attribute, $value))->__toString(); } /** @@ -127,7 +127,7 @@ class Query implements \JsonSerializable */ public static function isNull(string $attribute): string { - return new Query('isNull', $attribute, null).__toString(); + return (new Query('isNull', $attribute, null))->__toString(); } /** @@ -138,7 +138,7 @@ class Query implements \JsonSerializable */ public static function isNotNull(string $attribute): string { - return new Query('isNotNull', $attribute, null).__toString(); + return (new Query('isNotNull', $attribute, null))->__toString(); } /** @@ -151,7 +151,7 @@ class Query implements \JsonSerializable */ public static function between(string $attribute, $start, $end): string { - return new Query('between', $attribute, [$start, $end]).__toString(); + return (new Query('between', $attribute, [$start, $end]))->__toString(); } /** @@ -163,7 +163,7 @@ class Query implements \JsonSerializable */ public static function startsWith(string $attribute, string $value): string { - return new Query('startsWith', $attribute, $value).__toString(); + return (new Query('startsWith', $attribute, $value))->__toString(); } /** @@ -175,7 +175,7 @@ class Query implements \JsonSerializable */ public static function endsWith(string $attribute, string $value): string { - return new Query('endsWith', $attribute, $value).__toString(); + return (new Query('endsWith', $attribute, $value))->__toString(); } /** @@ -186,7 +186,7 @@ class Query implements \JsonSerializable */ public static function select(array $attributes): string { - return new Query('select', null, $attributes).__toString(); + return (new Query('select', null, $attributes))->__toString(); } /** @@ -197,7 +197,7 @@ class Query implements \JsonSerializable */ public static function cursorAfter(string $documentId): string { - return new Query('cursorAfter', null, $documentId).__toString(); + return (new Query('cursorAfter', null, $documentId))->__toString(); } /** @@ -208,7 +208,7 @@ class Query implements \JsonSerializable */ public static function cursorBefore(string $documentId): string { - return new Query('cursorBefore', null, $documentId).__toString(); + return (new Query('cursorBefore', null, $documentId))->__toString(); } /** @@ -219,7 +219,7 @@ class Query implements \JsonSerializable */ public static function orderAsc(string $attribute): string { - return new Query('orderAsc', $attribute, null).__toString(); + return (new Query('orderAsc', $attribute, null))->__toString(); } /** @@ -230,7 +230,7 @@ class Query implements \JsonSerializable */ public static function orderDesc(string $attribute): string { - return new Query('orderDesc', $attribute, null).__toString(); + return (new Query('orderDesc', $attribute, null))->__toString(); } /** @@ -241,7 +241,7 @@ class Query implements \JsonSerializable */ public static function limit(int $limit): string { - return new Query('limit', null, $limit).__toString(); + return (new Query('limit', null, $limit))->__toString(); } /** @@ -252,7 +252,7 @@ class Query implements \JsonSerializable */ public static function offset(int $offset): string { - return new Query('offset', null, $offset).__toString(); + return (new Query('offset', null, $offset))->__toString(); } /** @@ -264,7 +264,7 @@ class Query implements \JsonSerializable */ public static function contains(string $attribute, string $value): string { - return new Query('contains', $attribute, $value).__toString(); + return (new Query('contains', $attribute, $value))->__toString(); } /** @@ -278,7 +278,7 @@ class Query implements \JsonSerializable foreach ($queries as &$query) { $query = \json_decode($query, true); } - return new Query('or', null, $queries).__toString(); + return (new Query('or', null, $queries))->__toString(); } /** @@ -292,6 +292,6 @@ class Query implements \JsonSerializable foreach ($queries as &$query) { $query = \json_decode($query, true); } - return new Query('and', null, $queries).__toString(); + return (new Query('and', null, $queries))->__toString(); } } diff --git a/templates/swift/Package.swift.twig b/templates/swift/Package.swift.twig index 7a1a09a3d..132ae387f 100644 --- a/templates/swift/Package.swift.twig +++ b/templates/swift/Package.swift.twig @@ -24,7 +24,6 @@ let package = Package( dependencies: [ .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.9.0"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.32.0"), - .package(url: "https://github.com/firebase/firebase-ios-sdk.git", from: "10.4.0") ], targets: [ .target( @@ -32,7 +31,6 @@ let package = Package( dependencies: [ .product(name: "AsyncHTTPClient", package: "async-http-client"), .product(name: "NIOWebSocket", package: "swift-nio"), - .product(name: "FirebaseMessaging", package: "firebase-ios-sdk"), {%~ if spec.definitions is not empty %} "{{spec.title | caseUcfirst}}Models", {%~ endif %} diff --git a/templates/swift/Sources/Delegate.swift.twig b/templates/swift/Sources/Delegate.swift.twig deleted file mode 100644 index fad2d1f23..000000000 --- a/templates/swift/Sources/Delegate.swift.twig +++ /dev/null @@ -1,4 +0,0 @@ -import SwiftUI -import FirebaseMessaging - -@objc public protocol AppwriteDelegate: UNUserNotificationCenterDelegate, MessagingDelegate {} diff --git a/templates/swift/Sources/NotificationHandler.swift.twig b/templates/swift/Sources/NotificationHandler.swift.twig deleted file mode 100644 index 9fb79fcf2..000000000 --- a/templates/swift/Sources/NotificationHandler.swift.twig +++ /dev/null @@ -1,275 +0,0 @@ -import JSONCodable -import FirebaseCore -import FirebaseMessaging -import AsyncHTTPClient - -#if os(iOS) || os(tvOS) -import UIKit -public typealias Application = UIApplication -#elseif os(macOS) -import AppKit -public typealias Application = NSApplication -#elseif os(watchOS) -import WatchKit -public typealias Application = WKApplication -#endif - -public enum Provider { - case fcm, apns -} - -open class NotificationHandler { - public static let shared = NotificationHandler() - - internal var provider: Provider = .apns - internal var client: Client? = nil - internal var account: Account? = nil - internal var providerId: String? = nil - - public func setup( - _ application: Application, - delegate: AppwriteDelegate, - provider: Provider - ) { - self.provider = provider - - FirebaseApp.configure() - - FirebaseMessaging.Messaging.messaging().delegate = delegate - - UNUserNotificationCenter.current().delegate = delegate - - NotificationCenter.default.addObserver( - self, - selector: #selector(tokenRefreshNotification), - name: Notification.Name.MessagingRegistrationTokenRefreshed, - object: nil - ) - - Client.cookieListener = { (existing, new) in - let group = DispatchGroup() - - group.enter() - - Task { - guard let token = try? await FirebaseMessaging.Messaging.messaging().token() else { - return - } - - await self.updateTarget( - existingCookies: existing, - newCookies: new, - token: token - ) - - group.leave() - } - - group.wait() - } - - let options: UNAuthorizationOptions = [.alert, .badge, .sound] - - UNUserNotificationCenter.current().requestAuthorization( - options: options, - completionHandler: { granted, error in - if (granted) { - DispatchQueue.main.async { - application.registerForRemoteNotifications() - } - } - } - ) - } - - @objc func tokenRefreshNotification(_ notification: Notification) { - guard let token = notification.object as? String else { - return - } - - handleFCMToken(token) - } - - public func handleAPNSToken(_ token: Data) { - switch (provider) { - case .fcm: - FirebaseMessaging.Messaging.messaging().apnsToken = token - case .apns: - Task { - await self.updateTarget( - existingCookies: [], - newCookies: [], - token: token.map { String(format: "%.2hhx", $0) }.joined() - - ) - } - } - } - - public func handleFCMToken(_ token: String) { - Task { - await self.updateTarget( - existingCookies: [], - newCookies: [], - token: token - ) - } - } - - public func updateTarget( - existingCookies: [String], - newCookies: [String], - token: String - ) async { - if (client == nil) { - return - } - - if (account == nil) { - account = Account(client!) - } - - let currentToken = UserDefaults.standard.string(forKey: "pushToken") - var currentTargetId = UserDefaults.standard.string(forKey: "targetId") ?? "" - - var existing = [String]() - if (existingCookies.isEmpty) { - let domain = URL(string: client!.endPoint)!.host! - let cookies = UserDefaults.standard.stringArray(forKey: domain) ?? [] - - cookies.forEach { - existing.append($0) - } - } - - var existingUser: [String: Any]? = nil - if (existing.isEmpty && !newCookies.isEmpty) { - existingUser = try? await request( - method: "GET", - path: "/account", - headers: ["cookie": newCookies.joined(separator: "; ")] - ) - } else if (!existing.isEmpty) { - existingUser = try? await request( - method: "GET", - path: "/account", - headers: ["cookie": existingCookies.joined(separator: "; ")] - ) - } - - if (existingUser == nil) { - return - } - - var newUser: [String: Any]? = nil - if (!newCookies.isEmpty) { - newUser = try? await request( - method: "GET", - path: "/account", - headers: ["cookie": newCookies.joined(separator: "; ")] - ) - } - - let existingUserId = existingUser?["$id"] as? String - let newUserId = newUser?["$id"] as? String - - if ( - token == currentToken - && (!existingCookies.isEmpty && existingUserId == newUserId) - ) { - return - } - - UserDefaults.standard.set(token, forKey: "pushToken") - - if (!existing.isEmpty && existingUserId != newUserId) { - if (!currentTargetId.isEmpty) { - let result = try? await request( - method: "DELETE", - path: "/account/targets/$currentTargetId/push", - headers: ["cookie": existing.joined(separator: "; ")] - ) - - if (result == nil) { - return - } - - UserDefaults.standard.removeObject(forKey: "targetId") - currentTargetId = "" - } - } - - let target: [String: Any]? - - var params = [ - "targetId": ID.unique(), - "identifier": token - ] - - if (providerId != nil) { - params["providerId"] = providerId! - } - - if ((currentTargetId.isEmpty && existing.isEmpty) || existingUserId != newUserId) { - target = try? await request( - method: "POST", - path: "/account/targets/push", - headers: ["cookie": newCookies.joined(separator: "; ")], - body: params - ) - } else { - target = try? await request( - method: "POST", - path: "/account/targets/push", - headers: ["cookie": existing.joined(separator: "; ")], - body: params - ) - } - - if (target == nil) { - return - } - - UserDefaults.standard.set(target?["$id"] ?? "", forKey: "targetId") - } - - private func request( - method: String, - path: String, - headers: [String: String], - body: [String: Any]? = nil - ) async throws -> [String: Any]? { - var request = HTTPClientRequest(url: client!.endPoint + path) - - request.method = .RAW(value: method) - - for (key, value) in client!.headers.merging(headers, uniquingKeysWith: { $1 }) { - request.headers.add(name: key, value: value) - } - - if (body != nil) { - guard let json = try? JSONSerialization.data(withJSONObject: body!, options: []) else { - return nil - } - - request.body = .bytes(json) - } - - guard let response = try? await client!.http.execute( - request, - timeout: .seconds(30) - ) else { - return nil - } - - guard let data = try? await response.body.collect(upTo: Int.max) else { - return nil - } - - guard let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { - return nil - } - - return dict - } -} diff --git a/templates/swift/Sources/Query.swift.twig b/templates/swift/Sources/Query.swift.twig index ccdd3d150..20c499eea 100644 --- a/templates/swift/Sources/Query.swift.twig +++ b/templates/swift/Sources/Query.swift.twig @@ -66,15 +66,15 @@ public struct Query : Codable, CustomStringConvertible { case let valueArray as [QueryValue]: return valueArray case let stringArray as [String]: - return stringArray.map { QueryValue.string($0) } + return stringArray.map { .string($0) } case let intArray as [Int]: - return intArray.map { QueryValue.int($0) } + return intArray.map { .int($0) } case let doubleArray as [Double]: - return doubleArray.map { QueryValue.double($0) } + return doubleArray.map { .double($0) } case let boolArray as [Bool]: - return boolArray.map { QueryValue.bool($0) } + return boolArray.map { .bool($0) } case let queryArray as [Query]: - return queryArray.map { QueryValue.query($0) } + return queryArray.map { .query($0) } case let stringValue as String: return [.string(stringValue)] case let intValue as Int: @@ -89,7 +89,7 @@ public struct Query : Codable, CustomStringConvertible { return nil } } - + enum CodingKeys: String, CodingKey { case method case attribute @@ -121,7 +121,7 @@ public struct Query : Codable, CustomStringConvertible { return Query( method: "equal", attribute: attribute, - values: [value] + values: Query.parseValue(value) ).description } @@ -129,7 +129,7 @@ public struct Query : Codable, CustomStringConvertible { return Query( method: "notEqual", attribute: attribute, - values: [value] + values: Query.parseValue(value) ).description } @@ -137,7 +137,7 @@ public struct Query : Codable, CustomStringConvertible { return Query( method: "lessThan", attribute: attribute, - values: [value] + values: Query.parseValue(value) ).description } @@ -145,7 +145,7 @@ public struct Query : Codable, CustomStringConvertible { return Query( method: "lessThanEqual", attribute: attribute, - values: [value] + values: Query.parseValue(value) ).description } @@ -153,7 +153,7 @@ public struct Query : Codable, CustomStringConvertible { return Query( method: "greaterThan", attribute: attribute, - values: [value] + values: Query.parseValue(value) ).description } @@ -161,7 +161,7 @@ public struct Query : Codable, CustomStringConvertible { return Query( method: "greaterThanEqual", attribute: attribute, - values: [value] + values: Query.parseValue(value) ).description } @@ -280,7 +280,7 @@ public struct Query : Codable, CustomStringConvertible { return Query( method: "contains", attribute: attribute, - values: value + values: Query.parseValue(value) ).description } @@ -313,4 +313,12 @@ public struct Query : Codable, CustomStringConvertible { values: decodedQueries ).description } -} \ No newline at end of file + + private static func parseValue(_ value: Any) -> [Any] { + if let value = value as? [Any] { + return value + } else { + return [value] + } + } +} diff --git a/tests/AppleSwift55Test.php b/tests/AppleSwift56Test.php similarity index 96% rename from tests/AppleSwift55Test.php rename to tests/AppleSwift56Test.php index c49fbc9eb..cfca2c18e 100644 --- a/tests/AppleSwift55Test.php +++ b/tests/AppleSwift56Test.php @@ -2,7 +2,7 @@ namespace Tests; -class AppleSwift55Test extends Base +class AppleSwift56Test extends Base { protected string $sdkName = 'swift'; protected string $sdkPlatform = 'client'; diff --git a/tests/Base.php b/tests/Base.php index ef071dbb7..7d8dc0392 100644 --- a/tests/Base.php +++ b/tests/Base.php @@ -49,6 +49,10 @@ abstract class Base extends TestCase 'POST:/v1/mock/tests/general/enum:passed', ]; + protected const UPLOAD_RESPONSE = [ + 'POST:/v1/mock/tests/general/upload:passed', + ]; + protected const UPLOAD_RESPONSES = [ 'POST:/v1/mock/tests/general/upload:passed', 'POST:/v1/mock/tests/general/upload:passed', @@ -204,23 +208,14 @@ public function testHTTPSuccess(): void echo \implode("\n", $output); - # Some languages deserialize JSON with sorted keys, other not. - # We use this custom assertion to normalise the lines with JSON before comparison. - $this->assertEqualsWithJsonLines($this->expectedOutput, $output); - } - - private function assertEqualsWithJsonLines($expectedLines, $actualLines) - { - for ($i = 0; $i < \count($expectedLines); $i++) { - $this->assertArrayHasKey($i, $actualLines, "Missing line {$i}: {$expectedLines[$i]}"); - - $expectedLine = $expectedLines[$i]; - $actualLine = $actualLines[$i]; - - if (\str_starts_with($expectedLine, '{')) { - $this->assertEquals(\json_decode($expectedLine), \json_decode($actualLine)); + foreach ($this->expectedOutput as $index => $expected) { + if (\str_starts_with($expected, '{')) { + $this->assertEquals( + \json_decode($expected, true), + \json_decode($output[$index], true) + ); } else { - $this->assertEquals($expectedLine, $actualLine); + $this->assertEquals($expected, $output[$index]); } } } diff --git a/tests/Go112Test.php b/tests/Go112Test.php index 58b2a0898..ab3e11601 100644 --- a/tests/Go112Test.php +++ b/tests/Go112Test.php @@ -21,6 +21,7 @@ class Go112Test extends Base ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, + ...Base::UPLOAD_RESPONSE, ...Base::DOWNLOAD_RESPONSES, ...Base::EXCEPTION_RESPONSES, ]; diff --git a/tests/Go118Test.php b/tests/Go118Test.php index 70e03f069..3b21030f0 100644 --- a/tests/Go118Test.php +++ b/tests/Go118Test.php @@ -21,6 +21,7 @@ class Go118Test extends Base ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, + ...Base::UPLOAD_RESPONSE, ...Base::DOWNLOAD_RESPONSES, ...Base::EXCEPTION_RESPONSES, ]; diff --git a/tests/Swift55Test.php b/tests/Swift56Test.php similarity index 90% rename from tests/Swift55Test.php rename to tests/Swift56Test.php index f10c97955..22172ed0f 100644 --- a/tests/Swift55Test.php +++ b/tests/Swift56Test.php @@ -2,7 +2,7 @@ namespace Tests; -class Swift55Test extends Base +class Swift56Test extends Base { protected string $sdkName = 'swift'; protected string $sdkPlatform = 'server'; @@ -16,7 +16,7 @@ class Swift55Test extends Base 'cp tests/languages/swift/Tests.swift tests/sdks/swift/Tests/AppwriteTests/Tests.swift', ]; protected string $command = - 'docker run --network="mockapi" --rm -v $(pwd):/app -w /app/tests/sdks/swift swiftarm/swift:5.5.2-focal-multi-arch swift test'; + 'docker run --network="mockapi" --rm -v $(pwd):/app -w /app/tests/sdks/swift swift:5.6-focal swift test'; protected array $expectedOutput = [ ...Base::FOO_RESPONSES, diff --git a/tests/WebNodeTest.php b/tests/WebNodeTest.php index fd9a4e03f..afeb0eb8b 100644 --- a/tests/WebNodeTest.php +++ b/tests/WebNodeTest.php @@ -25,8 +25,8 @@ class WebNodeTest extends Base ...Base::FOO_RESPONSES, ...Base::BAR_RESPONSES, ...Base::GENERAL_RESPONSES, - ...Base::ENUM_RESPONSES, ...Base::UPLOAD_RESPONSES, + ...Base::ENUM_RESPONSES, ...Base::EXCEPTION_RESPONSES, ...Base::REALTIME_RESPONSES, ...Base::QUERY_HELPER_RESPONSES, diff --git a/tests/languages/kotlin/Tests.kt b/tests/languages/kotlin/Tests.kt index 890ededb2..2aff972de 100644 --- a/tests/languages/kotlin/Tests.kt +++ b/tests/languages/kotlin/Tests.kt @@ -74,9 +74,6 @@ class ServiceTest { val result = general.redirect() writeToFile((result as Map)["result"] as String) - mock = general.enum(MockType.FIRST) - writeToFile(mock.result) - try { mock = general.upload("string", 123, listOf("string in array"), InputFile.fromPath("../../resources/file.png")) writeToFile(mock.result)