Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Firebase JS externals using Web v9 modular SDK #319

Merged
merged 34 commits into from
Sep 2, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
21d2264
Refactor app and database modules
shepeliev Jun 10, 2022
ea7fa10
Refactor auth
shepeliev Jun 11, 2022
3846b15
Refactor firestore
shepeliev Jun 12, 2022
9e4422e
Revert formatting
shepeliev Jun 12, 2022
bd371ed
Revert formatting
shepeliev Jun 12, 2022
6d3b904
Refactor remote config
shepeliev Jun 13, 2022
99bceab
Refactor functions
shepeliev Jun 13, 2022
322d629
Refactor installations
shepeliev Jun 13, 2022
17e4ef3
Fix firestore tests
shepeliev Jun 13, 2022
fb4bd1c
Fix DataSnapshot size
shepeliev Jun 13, 2022
ab53f6f
Fix database tests
shepeliev Jun 13, 2022
b6eb04b
Revert non-js changes
shepeliev Jun 13, 2022
4ff80b2
Fix infinite test task
shepeliev Jun 13, 2022
79e4635
Split externals
shepeliev Jun 14, 2022
94ef863
Fix Android test results path
shepeliev Jun 14, 2022
e6fccc3
Increase waiting connection to database timeout
shepeliev Jun 14, 2022
1d36094
Merge branch 'master' into refactor-web-version-9
shepeliev Aug 21, 2022
18364b3
Merge branch 'master' into refactor-web-version-9
shepeliev Apr 5, 2023
a9b9f33
Fixes after merging upstream master branch
shepeliev Apr 6, 2023
117ac63
Enable firebase-database JS tests
shepeliev Apr 6, 2023
541ea99
Move externals to related modules
shepeliev Apr 6, 2023
dd2e9d7
Add externals signInWithPopup, signInWithRedirect, getRedirectResult
shepeliev Apr 6, 2023
d3b1721
Merge branch 'master' into refactor-web-version-9
shepeliev Apr 9, 2023
5bb89b8
Resolve conflicts
shepeliev Apr 9, 2023
d66734c
Fix tests
shepeliev Apr 9, 2023
de2d1e2
Switch firebase-database tests to karma
shepeliev Apr 9, 2023
6e65b8a
Increase JS tests timeout
shepeliev Apr 10, 2023
f6d0156
Rename externals.kt to Unsubscribe.kt
shepeliev Apr 10, 2023
fddc5b8
Increase JS tests timeout for modules that interact with Firebase emu…
shepeliev Apr 10, 2023
cfbc082
Merge branch 'master' into refactor-web-version-9
shepeliev Apr 10, 2023
ff85d16
Merge branch 'master' into refactor-web-version-9-shepeliev
nbransby Aug 28, 2023
be096bd
upgrade storage to js v9
nbransby Aug 28, 2023
a7e335f
fix js tests
nbransby Aug 28, 2023
7b78af4
Merge branch 'master' into refactor-web-version-9
nbransby Sep 2, 2023
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
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
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
Copy link
Contributor Author

@shepeliev shepeliev Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are using kotlinx-coroutines-test the result of runTest must be immediately returned from each test in JS.


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