Skip to content

Commit

Permalink
Merge pull request #319 from shepeliev/refactor-web-version-9
Browse files Browse the repository at this point in the history
Refactor Firebase JS externals using Web v9 modular SDK
  • Loading branch information
nbransby authored Sep 2, 2023
2 parents 4aeec01 + 7b78af4 commit b29db6f
Show file tree
Hide file tree
Showing 69 changed files with 1,500 additions and 1,147 deletions.
4 changes: 2 additions & 2 deletions firebase-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gitlive/firebase-app",
"version": "1.9.0",
"version": "1.10.0",
"description": "Wrapper around firebase for usage in Kotlin Multiplatform projects",
"main": "firebase-app.js",
"scripts": {
Expand All @@ -23,7 +23,7 @@
},
"homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk",
"dependencies": {
"@gitlive/firebase-common": "1.9.0",
"@gitlive/firebase-common": "1.10.0",
"firebase": "9.19.1",
"kotlin": "1.8.20",
"kotlinx-coroutines-core": "1.6.4"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@file:JsModule("firebase/app")
@file:JsNonModule

package dev.gitlive.firebase.externals

import kotlin.js.Promise

external fun initializeApp(options: Any, name: String = definedExternally): FirebaseApp

external fun getApp(name: String = definedExternally): FirebaseApp

external fun getApps(): Array<FirebaseApp>

external fun deleteApp(app: FirebaseApp): Promise<Unit>

external interface FirebaseApp {
val automaticDataCollectionEnabled: Boolean
val name: String
val options: FirebaseOptions
}

external interface FirebaseOptions {
val apiKey: String
val appId : String
val authDomain: String?
val databaseURL: String?
val measurementId: String?
val messagingSenderId: String?
val gaTrackingId: String?
val projectId: String?
val storageBucket: String?
}
23 changes: 15 additions & 8 deletions firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,42 @@

package dev.gitlive.firebase

import dev.gitlive.firebase.externals.deleteApp
import dev.gitlive.firebase.externals.getApp
import dev.gitlive.firebase.externals.getApps
import dev.gitlive.firebase.externals.initializeApp
import kotlin.js.json
import dev.gitlive.firebase.externals.FirebaseApp as JsFirebaseApp

actual val Firebase.app: FirebaseApp
get() = FirebaseApp(firebase.app())
get() = FirebaseApp(getApp())

actual fun Firebase.app(name: String): FirebaseApp =
FirebaseApp(firebase.app(name))
FirebaseApp(getApp(name))

actual fun Firebase.initialize(context: Any?): FirebaseApp? =
throw UnsupportedOperationException("Cannot initialize firebase without options in JS")

actual fun Firebase.initialize(context: Any?, options: FirebaseOptions, name: String): FirebaseApp =
FirebaseApp(firebase.initializeApp(options.toJson(), name))
FirebaseApp(initializeApp(options.toJson(), name))

actual fun Firebase.initialize(context: Any?, options: FirebaseOptions) =
FirebaseApp(firebase.initializeApp(options.toJson()))
FirebaseApp(initializeApp(options.toJson()))

actual class FirebaseApp internal constructor(val js: firebase.App) {
actual class FirebaseApp internal constructor(val js: JsFirebaseApp) {
actual val name: String
get() = js.name
actual val options: FirebaseOptions
get() = js.options.run {
FirebaseOptions(applicationId, apiKey, databaseUrl, gaTrackingId, storageBucket, projectId, messagingSenderId, authDomain)
FirebaseOptions(appId, apiKey, databaseURL, gaTrackingId, storageBucket, projectId, messagingSenderId, authDomain)
}

actual fun delete() = js.delete()
actual fun delete() {
deleteApp(js)
}
}

actual fun Firebase.apps(context: Any?) = firebase.apps.map { FirebaseApp(it) }
actual fun Firebase.apps(context: Any?) = getApps().map { FirebaseApp(it) }

private fun FirebaseOptions.toJson() = json(
"apiKey" to apiKey,
Expand Down
8 changes: 8 additions & 0 deletions firebase-auth/karma.config.d/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Some tests are fluky in GitHub Actions, so we increase the timeout.
config.set({
client: {
mocha: {
timeout: 5000
}
},
});
4 changes: 2 additions & 2 deletions firebase-auth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gitlive/firebase-auth",
"version": "1.9.0",
"version": "1.10.0",
"description": "Wrapper around firebase for usage in Kotlin Multiplatform projects",
"main": "firebase-auth.js",
"scripts": {
Expand All @@ -23,7 +23,7 @@
},
"homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk",
"dependencies": {
"@gitlive/firebase-app": "1.9.0",
"@gitlive/firebase-app": "1.10.0",
"firebase": "9.19.1",
"kotlin": "1.8.20",
"kotlinx-coroutines-core": "1.6.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
package dev.gitlive.firebase.auth

import dev.gitlive.firebase.*
import kotlinx.coroutines.test.TestResult
import kotlin.random.Random
import kotlin.test.*

expect val emulatorHost: String
expect val context: Any
expect fun runTest(test: suspend () -> Unit)
expect fun runTest(test: suspend () -> Unit): TestResult

class FirebaseAuthTest {

Expand Down
47 changes: 25 additions & 22 deletions firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@
package dev.gitlive.firebase.auth

import dev.gitlive.firebase.*
import dev.gitlive.firebase.FirebaseApp
import dev.gitlive.firebase.auth.externals.*
import kotlinx.coroutines.await
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlin.js.json
import dev.gitlive.firebase.auth.externals.AuthResult as JsAuthResult

actual val Firebase.auth
get() = rethrow { dev.gitlive.firebase.auth; FirebaseAuth(firebase.auth()) }
get() = rethrow { FirebaseAuth(getAuth()) }

actual fun Firebase.auth(app: FirebaseApp) =
rethrow { dev.gitlive.firebase.auth; FirebaseAuth(firebase.auth(app.js)) }
rethrow { FirebaseAuth(getAuth(app.js)) }

actual class FirebaseAuth internal constructor(val js: firebase.auth.Auth) {
actual class FirebaseAuth internal constructor(val js: Auth) {

actual val currentUser: FirebaseUser?
get() = rethrow { js.currentUser?.let { FirebaseUser(it) } }
Expand All @@ -39,47 +42,47 @@ actual class FirebaseAuth internal constructor(val js: firebase.auth.Auth) {
get() = js.languageCode ?: ""
set(value) { js.languageCode = value }

actual suspend fun applyActionCode(code: String) = rethrow { js.applyActionCode(code).await() }
actual suspend fun confirmPasswordReset(code: String, newPassword: String) = rethrow { js.confirmPasswordReset(code, newPassword).await() }
actual suspend fun applyActionCode(code: String) = rethrow { applyActionCode(js, code).await() }
actual suspend fun confirmPasswordReset(code: String, newPassword: String) = rethrow { confirmPasswordReset(js, code, newPassword).await() }

actual suspend fun createUserWithEmailAndPassword(email: String, password: String) =
rethrow { AuthResult(js.createUserWithEmailAndPassword(email, password).await()) }
rethrow { AuthResult(createUserWithEmailAndPassword(js, email, password).await()) }

actual suspend fun fetchSignInMethodsForEmail(email: String): List<String> = rethrow { js.fetchSignInMethodsForEmail(email).await().asList() }
actual suspend fun fetchSignInMethodsForEmail(email: String): List<String> = rethrow { fetchSignInMethodsForEmail(js, email).await().asList() }

actual suspend fun sendPasswordResetEmail(email: String, actionCodeSettings: ActionCodeSettings?) =
rethrow { js.sendPasswordResetEmail(email, actionCodeSettings?.toJson()).await() }
rethrow { sendPasswordResetEmail(js, email, actionCodeSettings?.toJson()).await() }

actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) =
rethrow { js.sendSignInLinkToEmail(email, actionCodeSettings.toJson()).await() }
rethrow { sendSignInLinkToEmail(js, email, actionCodeSettings.toJson()).await() }

actual fun isSignInWithEmailLink(link: String) = rethrow { js.isSignInWithEmailLink(link) }
actual fun isSignInWithEmailLink(link: String) = rethrow { isSignInWithEmailLink(js, link) }

actual suspend fun signInWithEmailAndPassword(email: String, password: String) =
rethrow { AuthResult(js.signInWithEmailAndPassword(email, password).await()) }
rethrow { AuthResult(signInWithEmailAndPassword(js, email, password).await()) }

actual suspend fun signInWithCustomToken(token: String) =
rethrow { AuthResult(js.signInWithCustomToken(token).await()) }
rethrow { AuthResult(signInWithCustomToken(js, token).await()) }

actual suspend fun signInAnonymously() =
rethrow { AuthResult(js.signInAnonymously().await()) }
rethrow { AuthResult(signInAnonymously(js).await()) }

actual suspend fun signInWithCredential(authCredential: AuthCredential) =
rethrow { AuthResult(js.signInWithCredential(authCredential.js).await()) }
rethrow { AuthResult(signInWithCredential(js, authCredential.js).await()) }

actual suspend fun signInWithEmailLink(email: String, link: String) =
rethrow { AuthResult(js.signInWithEmailLink(email, link).await()) }
rethrow { AuthResult(signInWithEmailLink(js, email, link).await()) }

actual suspend fun signOut() = rethrow { js.signOut().await() }
actual suspend fun signOut() = rethrow { signOut(js).await() }

actual suspend fun updateCurrentUser(user: FirebaseUser) =
rethrow { js.updateCurrentUser(user.js).await() }
rethrow { updateCurrentUser(js, user.js).await() }

actual suspend fun verifyPasswordResetCode(code: String): String =
rethrow { js.verifyPasswordResetCode(code).await() }
rethrow { verifyPasswordResetCode(js, code).await() }

actual suspend fun <T : ActionCodeResult> checkActionCode(code: String): T = rethrow {
val result = js.checkActionCode(code).await()
val result = checkActionCode(js, code).await()
@Suppress("UNCHECKED_CAST")
return when(result.operation) {
"EMAIL_SIGNIN" -> ActionCodeResult.SignInWithEmailLink
Expand All @@ -98,15 +101,15 @@ actual class FirebaseAuth internal constructor(val js: firebase.auth.Auth) {
} as T
}

actual fun useEmulator(host: String, port: Int) = rethrow { js.useEmulator("http://$host:$port") }
actual fun useEmulator(host: String, port: Int) = rethrow { connectAuthEmulator(js, "http://$host:$port") }
}

actual class AuthResult internal constructor(val js: firebase.auth.AuthResult) {
actual class AuthResult internal constructor(val js: JsAuthResult) {
actual val user: FirebaseUser?
get() = rethrow { js.user?.let { FirebaseUser(it) } }
}

actual class AuthTokenResult(val js: firebase.auth.IdTokenResult) {
actual class AuthTokenResult(val js: IdTokenResult) {
// actual val authTimestamp: Long
// get() = js.authTime
actual val claims: Map<String, Any>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,70 @@
package dev.gitlive.firebase.auth

import dev.gitlive.firebase.firebase
import dev.gitlive.firebase.auth.externals.ApplicationVerifier
import dev.gitlive.firebase.auth.externals.EmailAuthProvider
import dev.gitlive.firebase.auth.externals.FacebookAuthProvider
import dev.gitlive.firebase.auth.externals.GithubAuthProvider
import dev.gitlive.firebase.auth.externals.GoogleAuthProvider
import dev.gitlive.firebase.auth.externals.PhoneAuthProvider
import dev.gitlive.firebase.auth.externals.TwitterAuthProvider
import kotlinx.coroutines.await
import kotlin.js.Json
import kotlin.js.json
import dev.gitlive.firebase.auth.externals.AuthCredential as JsAuthCredential
import dev.gitlive.firebase.auth.externals.OAuthProvider as JsOAuthProvider

actual open class AuthCredential(val js: firebase.auth.AuthCredential) {
actual open class AuthCredential(val js: JsAuthCredential) {
actual val providerId: String
get() = js.providerId
}

actual class PhoneAuthCredential(js: firebase.auth.AuthCredential) : AuthCredential(js)
actual class OAuthCredential(js: firebase.auth.AuthCredential) : AuthCredential(js)
actual class PhoneAuthCredential(js: JsAuthCredential) : AuthCredential(js)
actual class OAuthCredential(js: JsAuthCredential) : AuthCredential(js)

actual object EmailAuthProvider {
actual fun credential(email: String, password: String): AuthCredential =
AuthCredential(firebase.auth.EmailAuthProvider.credential(email, password))
AuthCredential(EmailAuthProvider.credential(email, password))

actual fun credentialWithLink(
email: String,
emailLink: String
): AuthCredential = AuthCredential(firebase.auth.EmailAuthProvider.credentialWithLink(email, emailLink))
): AuthCredential = AuthCredential(EmailAuthProvider.credentialWithLink(email, emailLink))
}

actual object FacebookAuthProvider {
actual fun credential(accessToken: String): AuthCredential =
AuthCredential(firebase.auth.FacebookAuthProvider.credential(accessToken))
AuthCredential(FacebookAuthProvider.credential(accessToken))
}

actual object GithubAuthProvider {
actual fun credential(token: String): AuthCredential =
AuthCredential(firebase.auth.GithubAuthProvider.credential(token))
AuthCredential(GithubAuthProvider.credential(token))
}

actual object GoogleAuthProvider {
actual fun credential(idToken: String?, accessToken: String?): AuthCredential {
require(idToken != null || accessToken != null) {
"Both parameters are optional but at least one must be present."
}
return AuthCredential(firebase.auth.GoogleAuthProvider.credential(idToken, accessToken))
return AuthCredential(GoogleAuthProvider.credential(idToken, accessToken))
}
}

actual class OAuthProvider(val js: firebase.auth.OAuthProvider) {
actual class OAuthProvider(val js: JsOAuthProvider) {

actual constructor(
provider: String,
scopes: List<String>,
customParameters: Map<String, String>,
auth: FirebaseAuth
) : this(firebase.auth.OAuthProvider(provider)) {
) : this(JsOAuthProvider(provider)) {
rethrow {
scopes.forEach { js.addScope(it) }
js.setCustomParameters(customParameters)
}
}
actual companion object {
actual fun credential(providerId: String, accessToken: String?, idToken: String?, rawNonce: String?): OAuthCredential = rethrow {
firebase.auth.OAuthProvider(providerId)
JsOAuthProvider(providerId)
.credential(
json(
"accessToken" to (accessToken ?: undefined),
Expand All @@ -71,11 +78,11 @@ actual class OAuthProvider(val js: firebase.auth.OAuthProvider) {
}
}

actual class PhoneAuthProvider(val js: firebase.auth.PhoneAuthProvider) {
actual class PhoneAuthProvider(val js: PhoneAuthProvider) {

actual constructor(auth: FirebaseAuth) : this(firebase.auth.PhoneAuthProvider(auth.js))
actual constructor(auth: FirebaseAuth) : this(PhoneAuthProvider(auth.js))

actual fun credential(verificationId: String, smsCode: String): PhoneAuthCredential = PhoneAuthCredential(firebase.auth.PhoneAuthProvider.credential(verificationId, smsCode))
actual fun credential(verificationId: String, smsCode: String): PhoneAuthCredential = PhoneAuthCredential(PhoneAuthProvider.credential(verificationId, smsCode))
actual suspend fun verifyPhoneNumber(phoneNumber: String, verificationProvider: PhoneVerificationProvider): AuthCredential = rethrow {
val verificationId = js.verifyPhoneNumber(phoneNumber, verificationProvider.verifier).await()
val verificationCode = verificationProvider.getVerificationCode(verificationId)
Expand All @@ -84,10 +91,10 @@ actual class PhoneAuthProvider(val js: firebase.auth.PhoneAuthProvider) {
}

actual interface PhoneVerificationProvider {
val verifier: firebase.auth.ApplicationVerifier
val verifier: ApplicationVerifier
suspend fun getVerificationCode(verificationId: String): String
}

actual object TwitterAuthProvider {
actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(firebase.auth.TwitterAuthProvider.credential(token, secret))
actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(TwitterAuthProvider.credential(token, secret))
}
Loading

0 comments on commit b29db6f

Please sign in to comment.