diff --git a/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt b/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt index 2207f6618..ccd5e48f5 100644 --- a/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt +++ b/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt @@ -4,22 +4,127 @@ import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.FirebaseApp import dev.gitlive.firebase.FirebaseException +/** Returns the [FirebaseRemoteConfig] instance of the default [FirebaseApp]. */ expect val Firebase.remoteConfig: FirebaseRemoteConfig +/** Returns the [FirebaseRemoteConfig] instance of a given [FirebaseApp]. */ expect fun Firebase.remoteConfig(app: FirebaseApp): FirebaseRemoteConfig +/** + * Entry point for the Firebase Remote Config API. + * + * Callers should first get the singleton object using [Firebase.remoteConfig], and then call + * operations on that singleton object. The singleton contains the complete set of Remote Config + * parameter values available to your app. The singleton also stores values fetched from the Remote + * Config server until they are made available for use with a call to [activate]. + */ expect class FirebaseRemoteConfig { + /** + * Returns a [Map] of Firebase Remote Config key value pairs. + * + * Evaluates the values of the parameters in the following order: + * + * - The activated value, if the last successful [activate] contained the key. + * - The default value, if the key was set with [setDefaults]. + */ val all: Map + + /** + * Returns the state of this [FirebaseRemoteConfig] instance as a [FirebaseRemoteConfigInfo]. + */ val info: FirebaseRemoteConfigInfo + /** + * Asynchronously activates the most recently fetched configs, so that the fetched key value pairs + * take effect. + * + * @return true result if the current call activated the fetched + * configs; if the fetched configs were already activated by a previous call, it instead + * returns a false result. + */ suspend fun activate(): Boolean + + /** + * Ensures the last activated config are available to the app. + */ suspend fun ensureInitialized() + + /** + * Starts fetching configs, adhering to the specified minimum fetch interval. + * + * The fetched configs only take effect after the next [activate] call. + * + * Depending on the time elapsed since the last fetch from the Firebase Remote Config backend, + * configs are either served from local storage, or fetched from the backend. + * + * Note: Also initializes the Firebase installations SDK that creates installation IDs to + * identify Firebase installations and periodically sends data to Firebase servers. Remote Config + * requires installation IDs for Fetch requests. To stop the periodic sync, call [FirebaseInstallations.delete]. Sending a Fetch request + * after deletion will create a new installation ID for this Firebase installation and resume the + * periodic sync. + * + * @param minimumFetchIntervalInSeconds If configs in the local storage were fetched more than + * this many seconds ago, configs are served from the backend instead of local storage. + */ suspend fun fetch(minimumFetchIntervalInSeconds: Long? = null) + + /** + * Asynchronously fetches and then activates the fetched configs. + * + * If the time elapsed since the last fetch from the Firebase Remote Config backend is more + * than the default minimum fetch interval, configs are fetched from the backend. + * + * After the fetch is complete, the configs are activated so that the fetched key value pairs + * take effect. + * + * @return [Boolean] with a true result if the current call activated the fetched + * configs; if no configs were fetched from the backend and the local fetched configs have + * already been activated, returns a [Boolean] with a false result. + */ suspend fun fetchAndActivate(): Boolean + + /** + * Returns a [Set] of all Firebase Remote Config parameter keys with the given prefix. + * + * @param prefix The key prefix to look for. If the prefix is empty, all keys are returned. + * @return [Set] of Remote Config parameter keys that start with the specified prefix. + */ fun getKeysByPrefix(prefix: String): Set + + /** + * Returns the parameter value for the given key as a [FirebaseRemoteConfigValue]. + * + * Evaluates the value of the parameter in the following order: + * + * - The activated value, if the last successful [activate] contained the key. + * - The default value, if the key was set with [setDefaults]. + * - A [FirebaseRemoteConfigValue] that returns the static value for each type. + * + * @param key A Firebase Remote Config parameter key. + * @return [FirebaseRemoteConfigValue] representing the value of the Firebase Remote Config + * parameter with the given key. + */ fun getValue(key: String): FirebaseRemoteConfigValue + + /** + * Deletes all activated, fetched and defaults configs and resets all Firebase Remote Config + * settings. + */ suspend fun reset() + + /** + * Asynchronously changes the settings for this [FirebaseRemoteConfig] instance. + * + * @param init A builder to set the settings. + */ suspend fun settings(init: FirebaseRemoteConfigSettings.() -> Unit) + + /** + * Asynchronously sets default configs using the given [Map]. + * + * @param defaults [Map] of key value pairs representing Firebase Remote Config parameter + * keys and values. + */ suspend fun setDefaults(vararg defaults: Pair) } @@ -36,7 +141,22 @@ inline operator fun FirebaseRemoteConfig.get(key: String): T { } as T } +/** + * Exception that gets thrown when an operation on Firebase Remote Config fails. + */ expect open class FirebaseRemoteConfigException : FirebaseException + +/** + * Exception that gets thrown when an operation on Firebase Remote Config fails. + */ expect class FirebaseRemoteConfigClientException : FirebaseRemoteConfigException + +/** + * Exception that gets thrown when an operation on Firebase Remote Config fails. + */ expect class FirebaseRemoteConfigFetchThrottledException : FirebaseRemoteConfigException + +/** + * Exception that gets thrown when an operation on Firebase Remote Config fails. + */ expect class FirebaseRemoteConfigServerException : FirebaseRemoteConfigException diff --git a/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigInfo.kt b/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigInfo.kt index 0bfc2d1d0..7eae3e53e 100644 --- a/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigInfo.kt +++ b/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigInfo.kt @@ -1,9 +1,53 @@ package dev.gitlive.firebase.remoteconfig +/** Wraps the current state of the [FirebaseRemoteConfig] singleton object. */ data class FirebaseRemoteConfigInfo( + /** + * Gets the current settings of the [FirebaseRemoteConfig] singleton object. + * + * @return A [FirebaseRemoteConfig] object indicating the current settings. + */ val configSettings: FirebaseRemoteConfigSettings, + + /** + * Gets the timestamp (milliseconds since epoch) of the last successful fetch, regardless of + * whether the fetch was activated or not. + * + * @return -1 if no fetch attempt has been made yet. Otherwise, returns the timestamp of the last + * successful fetch operation. + */ val fetchTimeMillis: Long, + + /** + * Gets the status of the most recent fetch attempt. + * + * @return Will return one of [FetchStatus.Success], [FetchStatus.Failure], [FetchStatus.Throttled], or [FetchStatus.NoFetchYet] + */ val lastFetchStatus: FetchStatus, ) -enum class FetchStatus { Success, Failure, Throttled, NoFetchYet } +enum class FetchStatus { + /** + * Indicates that the most recent fetch of parameter values from the Firebase Remote Config server + * was completed successfully. + */ + Success, + + /** + * Indicates that the most recent attempt to fetch parameter values from the Firebase Remote + * Config server has failed. + */ + Failure, + + /** + * Indicates that the most recent attempt to fetch parameter values from the Firebase Remote + * Config server was throttled. + */ + Throttled, + + /** + * Indicates that the FirebaseRemoteConfig singleton object has not yet attempted to fetch + * parameter values from the Firebase Remote Config server. + */ + NoFetchYet +} diff --git a/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigSettings.kt b/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigSettings.kt index 8eabe5f9f..4300dd7a7 100644 --- a/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigSettings.kt +++ b/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigSettings.kt @@ -5,7 +5,16 @@ private const val CONNECTION_TIMEOUT_IN_SECONDS = 60L // https://firebase.google.com/docs/remote-config/get-started?hl=en&platform=android#throttling private const val DEFAULT_FETCH_INTERVAL_IN_SECONDS = 12 * 3600L +/** Wraps the settings for [FirebaseRemoteConfig] operations. */ data class FirebaseRemoteConfigSettings( + /** + * Returns the fetch timeout in seconds. + * + * The timeout specifies how long the client should wait for a connection to the Firebase + * Remote Config server. + */ var fetchTimeoutInSeconds: Long = CONNECTION_TIMEOUT_IN_SECONDS, + + /** Returns the minimum interval between successive fetches calls in seconds. */ var minimumFetchIntervalInSeconds: Long = DEFAULT_FETCH_INTERVAL_IN_SECONDS, ) diff --git a/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigValue.kt b/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigValue.kt index 8cf474661..5da6e38d3 100644 --- a/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigValue.kt +++ b/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfigValue.kt @@ -1,12 +1,57 @@ package dev.gitlive.firebase.remoteconfig +/** Wrapper for a Remote Config parameter value, with methods to get it as different types. */ expect class FirebaseRemoteConfigValue { + /** + * Gets the value as a [Boolean]. + * + * @return [Boolean] representation of this parameter value. + */ fun asBoolean(): Boolean + + /** + * Gets the value as a [ByteArray]. + * + * @return [ByteArray] representation of this parameter value. + */ fun asByteArray(): ByteArray + + /** + * Gets the value as a [Double]. + * + * @return [Double] representation of this parameter value. + */ fun asDouble(): Double + + /** + * Gets the value as a [Long]. + * + * @return [Long] representation of this parameter value. + */ fun asLong(): Long + + /** + * Gets the value as a [String]. + * + * @return [String] representation of this parameter value. + */ fun asString(): String + + /** + * Indicates at which source this value came from. + * + * @return [ValueSource.Remote] if the value was retrieved from the server, [ValueSource.Default] if the value was set as a default, or [ValueSource.Stataic] if no value was found and a static default value was returned instead. + */ fun getSource(): ValueSource } -enum class ValueSource { Static, Default, Remote } +enum class ValueSource { + /** Indicates that the value returned is the static default value. */ + Static, + + /** Indicates that the value returned was retrieved from the defaults set by the client. */ + Default, + + /** Indicates that the value returned was retrieved from the Firebase Remote Config server. */ + Remote +} diff --git a/firebase-crashlytics/src/commonMain/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt b/firebase-crashlytics/src/commonMain/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt index 954991df7..3770ab2f1 100644 --- a/firebase-crashlytics/src/commonMain/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt +++ b/firebase-crashlytics/src/commonMain/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt @@ -10,21 +10,229 @@ expect val Firebase.crashlytics: FirebaseCrashlytics /** Returns the [FirebaseCrashlytics] instance of a given [FirebaseApp]. */ expect fun Firebase.crashlytics(app: FirebaseApp): FirebaseCrashlytics +/** + * The Firebase Crashlytics API provides methods to annotate and manage fatal crashes, non-fatal + * errors, and ANRs captured and reported to Firebase Crashlytics. + * + * By default, Firebase Crashlytics is automatically initialized. + * + * Call [Firebase.crashlytics] to get the singleton instance of + * [FirebaseCrashlytics]. + */ expect class FirebaseCrashlytics { + /** + * Records a non-fatal report to send to Crashlytics. + * + * @param exception a [Throwable] to be recorded as a non-fatal event. + */ fun recordException(exception: Throwable) + + /** + * Logs a message that's included in the next fatal, non-fatal, or ANR report. + * + * Logs are visible in the session view on the Firebase Crashlytics console. + * + * Newline characters are stripped and extremely long messages are truncated. The maximum log + * size is 64k. If exceeded, the log rolls such that messages are removed, starting from the + * oldest. + * + * @param message the message to be logged + */ fun log(message: String) + + /** + * Records a user ID (identifier) that's associated with subsequent fatal, non-fatal, and ANR + * reports. + * + * The user ID is visible in the session view on the Firebase Crashlytics console. + * + * Identifiers longer than 1024 characters will be truncated. + * + * @param userId a unique identifier for the current user + */ fun setUserId(userId: String) + + /** + * Sets a custom key and value that are associated with subsequent fatal, non-fatal, and ANR + * reports. + * + * Multiple calls to this method with the same key update the value for that key. + * + * The value of any key at the time of a fatal, non-fatal, or ANR event is associated with that + * event. + * + * Keys and associated values are visible in the session view on the Firebase Crashlytics + * console. + * + * Accepts a maximum of 64 key/value pairs. New keys beyond that limit are ignored. Keys or + * values that exceed 1024 characters are truncated. + * + * @param key A unique key + * @param value A value to be associated with the given key + */ fun setCustomKey(key: String, value: String) + + /** + * Sets a custom key and value that are associated with subsequent fatal, non-fatal, and ANR + * reports. + * + * Multiple calls to this method with the same key update the value for that key. + * + * The value of any key at the time of a fatal, non-fatal, or ANR event is associated with that + * event. + * + * Keys and associated values are visible in the session view on the Firebase Crashlytics + * console. + * + * Accepts a maximum of 64 key/value pairs. New keys beyond that limit are ignored. Keys or + * values that exceed 1024 characters are truncated. + * + * @param key A unique key + * @param value A value to be associated with the given key + */ fun setCustomKey(key: String, value: Boolean) + + /** + * Sets a custom key and value that are associated with subsequent fatal, non-fatal, and ANR + * reports. + * + * Multiple calls to this method with the same key update the value for that key. + * + * The value of any key at the time of a fatal, non-fatal, or ANR event is associated with that + * event. + * + * Keys and associated values are visible in the session view on the Firebase Crashlytics + * console. + * + * Accepts a maximum of 64 key/value pairs. New keys beyond that limit are ignored. Keys or + * values that exceed 1024 characters are truncated. + * + * @param key A unique key + * @param value A value to be associated with the given key + */ fun setCustomKey(key: String, value: Double) + + /** + * Sets a custom key and value that are associated with subsequent fatal, non-fatal, and ANR + * reports. + * + * Multiple calls to this method with the same key update the value for that key. + * + * The value of any key at the time of a fatal, non-fatal, or ANR event is associated with that + * event. + * + * Keys and associated values are visible in the session view on the Firebase Crashlytics + * console. + * + * Accepts a maximum of 64 key/value pairs. New keys beyond that limit are ignored. Keys or + * values that exceed 1024 characters are truncated. + * + * @param key A unique key + * @param value A value to be associated with the given key + */ fun setCustomKey(key: String, value: Float) + + /** + * Sets a custom key and value that are associated with subsequent fatal, non-fatal, and ANR + * reports. + * + * Multiple calls to this method with the same key update the value for that key. + * + * The value of any key at the time of a fatal, non-fatal, or ANR event is associated with that + * event. + * + * Keys and associated values are visible in the session view on the Firebase Crashlytics + * console. + * + * Accepts a maximum of 64 key/value pairs. New keys beyond that limit are ignored. Keys or + * values that exceed 1024 characters are truncated. + * + * @param key A unique key + * @param value A value to be associated with the given key + */ fun setCustomKey(key: String, value: Int) + + /** + * Sets a custom key and value that are associated with subsequent fatal, non-fatal, and ANR + * reports. + * + * Multiple calls to this method with the same key update the value for that key. + * + * The value of any key at the time of a fatal, non-fatal, or ANR event is associated with that + * event. + * + * Keys and associated values are visible in the session view on the Firebase Crashlytics + * console. + * + * Accepts a maximum of 64 key/value pairs. New keys beyond that limit are ignored. Keys or + * values that exceed 1024 characters are truncated. + * + * @param key A unique key + * @param value A value to be associated with the given key + */ fun setCustomKey(key: String, value: Long) + + /** + * Sets multiple custom keys and values that are associated with subsequent fatal, non-fatal, and + * ANR reports. This method is intended as an alternative to [setCustomKey] in order to + * reduce the computational load of writing out multiple key/value pairs at the same time. + * + * Multiple calls to this method with the same key update the value for that key. + * + * The value of any key at the time of a fatal, non-fatal, or ANR event is associated with that + * event. + * + * Keys and associated values are visible in the session view on the Firebase Crashlytics + * console. + * + * Accepts a maximum of 64 key/value pairs. If calling this method results in the number of + * custom keys exceeding this limit, only some of the keys will be logged (however many are needed + * to get to 64). Which keys are logged versus dropped is unpredictable as there is no intrinsic + * sorting of keys. Keys or values that exceed 1024 characters are truncated. + * + * @param customKeys A dictionary of keys and the values to associate with each key + */ fun setCustomKeys(customKeys: Map) + + /** + * Enables or disables the automatic data collection configuration for Crashlytics. + * + * If this is set, it overrides any automatic data collection settings configured in the + * AndroidManifest.xml as well as any Firebase-wide settings. + * + * If automatic data collection is disabled for Crashlytics, crash reports are stored on the + * device. Use [sendUnsentReports] to upload existing reports even when automatic data collection is + * disabled. Use [deleteUnsentReports] to delete any reports stored on the device without + * sending them to Crashlytics. + * + * @param enabled whether to enable automatic data collection. When set to `false`, the new + * value does not apply until the next run of the app. To disable data collection by default + * for all app runs, add the `firebase_crashlytics_collection_enabled` flag to your + * app's AndroidManifest.xml. + */ fun setCrashlyticsCollectionEnabled(enabled: Boolean) + + /** + * Checks whether the app crashed on its previous run. + * + * @return true if a crash was recorded during the previous run of the app. + */ fun didCrashOnPreviousExecution(): Boolean + + /** + * If automatic data collection is disabled, this method queues up all the reports on a device to + * send to Crashlytics. Otherwise, this method is a no-op. + */ fun sendUnsentReports() + + /** + * If automatic data collection is disabled, this method queues up all the reports on a device for + * deletion. Otherwise, this method is a no-op. + */ fun deleteUnsentReports() } +/** + * Exception that gets thrown when an operation on Firebase Crashlytics fails. + */ expect open class FirebaseCrashlyticsException : FirebaseException \ No newline at end of file diff --git a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt index 8672bd8a4..a85a3bd58 100644 --- a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -33,51 +33,256 @@ expect fun Firebase.database(app: FirebaseApp): FirebaseDatabase /** Returns the [FirebaseDatabase] instance of the given [FirebaseApp] and [url]. */ expect fun Firebase.database(app: FirebaseApp, url: String): FirebaseDatabase +/** + * The entry point for accessing a Firebase Database. You can get an instance by calling [Firebase.database]. To access a location in the database and read or write data, use [FirebaseDatabase.reference]. + */ expect class FirebaseDatabase { - + /** + * Gets a DatabaseReference for the provided path. + * + * @param path Path to a location in your FirebaseDatabase. + * @return A DatabaseReference pointing to the specified path. + */ fun reference(path: String): DatabaseReference + + /** + * Gets a DatabaseReference for the database root node. + * + * @return A DatabaseReference pointing to the root node. + */ fun reference(): DatabaseReference fun setLoggingEnabled(enabled: Boolean) + + /** + * The Firebase Database client will cache synchronized data and keep track of all writes you've + * initiated while your application is running. It seamlessly handles intermittent network + * connections and re-sends write operations when the network connection is restored. + * + * However by default your write operations and cached data are only stored in-memory and will + * be lost when your app restarts. By setting this value to `true`, the data will be persisted to + * on-device (disk) storage and will thus be available again when the app is restarted (even when + * there is no network connectivity at that time). Note that this method must be called before + * creating your first Database reference and only needs to be called once per application. + * + * @param enabled Set to true to enable disk persistence, set to false to disable it. + */ fun setPersistenceEnabled(enabled: Boolean) + + /** + * By default Firebase Database will use up to 10MB of disk space to cache data. If the cache + * grows beyond this size, Firebase Database will start removing data that hasn't been recently + * used. If you find that your application caches too little or too much data, call this method to + * change the cache size. This method must be called before creating your first Database reference + * and only needs to be called once per application. + * + * Note that the specified cache size is only an approximation and the size on disk may + * temporarily exceed it at times. Cache sizes smaller than 1 MB or greater than 100 MB are not + * supported. + * + * @param cacheSizeInBytes The new size of the cache in bytes. + */ fun setPersistenceCacheSizeBytes(cacheSizeInBytes: Long) + + /** + * Modifies this FirebaseDatabase instance to communicate with the Realtime Database emulator. + * + *

Note: Call this method before using the instance to do any database operations. + * + * @param host the emulator host (for example, 10.0.2.2) + * @param port the emulator port (for example, 9000) + */ fun useEmulator(host: String, port: Int) + /** + * Shuts down our connection to the Firebase Database backend until [goOnline] is called. + */ fun goOffline() + /** + * Resumes our connection to the Firebase Database backend after a previous [goOffline]. + * call. + */ fun goOnline() } +/** + * Used to emit events about changes in the child locations of a given [Query] when using the + * [childEvents] Flow. + */ data class ChildEvent internal constructor( val snapshot: DataSnapshot, val type: Type, val previousChildName: String? ) { enum class Type { + /** + * Emitted when a new child is added to the location. + * + * @param snapshot An immutable snapshot of the data at the new child location + * @param previousChildName The key name of sibling location ordered before the new child. This + * ``` + * will be null for the first child node of a location. + * ``` + */ ADDED, + + /** + * Emitted when the data at a child location has changed. + * + * @param snapshot An immutable snapshot of the data at the new data at the child location + * @param previousChildName The key name of sibling location ordered before the child. This will + * ``` + * be null for the first child node of a location. + * ``` + */ CHANGED, + + /** + * Emitted when a child location's priority changes. + * + * @param snapshot An immutable snapshot of the data at the location that moved. + * @param previousChildName The key name of the sibling location ordered before the child + * ``` + * location. This will be null if this location is ordered first. + * ``` + */ MOVED, + + /** + * Emitted when a child is removed from the location. + * + * @param snapshot An immutable snapshot of the data at the child that was removed. + */ REMOVED } } internal expect open class NativeQuery +/** + * The Query class (and its subclass, [DatabaseReference]) are used for reading data. + * Listeners are attached, and they will be triggered when the corresponding data changes. + * + * Instances of Query are obtained by calling [startAt], [endAt], or [limit] on a [DatabaseReference]. + */ expect open class Query internal constructor(nativeQuery: NativeQuery) { val valueEvents: Flow fun childEvents(vararg types: ChildEvent.Type = arrayOf(ADDED, CHANGED, MOVED, REMOVED)): Flow + + /** + * Creates a query in which child nodes are ordered by their keys. + * + * @return A query with the new constraint + */ fun orderByKey(): Query + + /** + * Creates a query in which nodes are ordered by their value + * + * @return A query with the new constraint + */ fun orderByValue(): Query + + /** + * Creates a query in which child nodes are ordered by the values of the specified path. + * + * @param path The path to the child node to use for sorting + * @return A query with the new constraint + */ fun orderByChild(path: String): Query + + /** + * Creates a query constrained to only return child nodes with a value greater than or equal to + * the given value, using the given `orderBy` directive or priority as default. + * + * @param value The value to start at, inclusive + * @return A query with the new constraint + */ fun startAt(value: String, key: String? = null): Query + + /** + * Creates a query constrained to only return child nodes with a value greater than or equal to + * the given value, using the given `orderBy` directive or priority as default. + * + * @param value The value to start at, inclusive + * @return A query with the new constraint + */ fun startAt(value: Double, key: String? = null): Query + + /** + * Creates a query constrained to only return child nodes with a value greater than or equal to + * the given value, using the given `orderBy` directive or priority as default. + * + * @param value The value to start at, inclusive + * @return A query with the new constraint + */ fun startAt(value: Boolean, key: String? = null): Query + + /** + * Creates a query constrained to only return child nodes with a value less than or equal to the + * given value, using the given `orderBy` directive or priority as default. + * + * @param value The value to end at, inclusive + * @return A query with the new constraint + */ fun endAt(value: String, key: String? = null): Query + + /** + * Creates a query constrained to only return child nodes with a value less than or equal to the + * given value, using the given `orderBy` directive or priority as default. + * + * @param value The value to end at, inclusive + * @return A query with the new constraint + */ fun endAt(value: Double, key: String? = null): Query + + /** + * Creates a query constrained to only return child nodes with a value less than or equal to the + * given value, using the given `orderBy` directive or priority as default. + * + * @param value The value to end at, inclusive + * @return A query with the new constraint + */ fun endAt(value: Boolean, key: String? = null): Query + + /** + * Creates a query with limit and anchor it to the start of the window. + * + * @param limit The maximum number of child nodes to return + * @return A query with the new constraint + */ fun limitToFirst(limit: Int): Query + + /** + * Creates a query with limit and anchor it to the end of the window. + * + * @param limit The maximum number of child nodes to return + * @return A query with the new constraint + */ fun limitToLast(limit: Int): Query + + /** + * Creates a query constrained to only return child nodes with the given value. + * + * @param value The value to query for + * @return A query with the new constraint + */ fun equalTo(value: String, key: String? = null): Query + + /** + * Creates a query constrained to only return child nodes with the given value. + * + * @param value The value to query for + * @return A query with the new constraint + */ fun equalTo(value: Double, key: String? = null): Query + + /** + * Creates a query constrained to only return child nodes with the given value. + * + * @param value The value to query for + * @return A query with the new constraint + */ fun equalTo(value: Boolean, key: String? = null): Query } @@ -95,11 +300,43 @@ internal expect class NativeDatabaseReference : NativeQuery { suspend fun runTransaction(strategy: KSerializer, buildSettings: EncodeDecodeSettingsBuilder.() -> Unit = {}, transactionUpdate: (currentData: T) -> T): DataSnapshot } +/** + * A Firebase reference represents a particular location in your Database and can be used for + * reading or writing data to that Database location. + * + * This class is the starting point for all Database operations. After you've initialized it with + * a URL, you can use it to read data, write data, and to create new DatabaseReferences. + */ class DatabaseReference internal constructor(@PublishedApi internal val nativeReference: NativeDatabaseReference) : Query(nativeReference) { - + /** + * @return The last token in the location pointed to by this reference or null if this reference + * points to the database root + */ val key: String? = nativeReference.key + + /** + * Create a reference to an auto-generated child location. The child key is generated client-side + * and incorporates an estimate of the server's time for sorting purposes. Locations generated on + * a single client will be sorted in the order that they are created, and will be sorted + * approximately in order across all clients. + * + * @return A DatabaseReference pointing to the new location + */ fun push(): DatabaseReference = DatabaseReference(nativeReference.push()) + + /** + * Get a reference to location relative to this one + * + * @param path The relative path from this reference to the new one that should be created + * @return A new DatabaseReference to the given path + */ fun child(path: String): DatabaseReference = DatabaseReference(nativeReference.child(path)) + + /** + * Provides access to disconnect operations at this location + * + * @return An object for managing disconnect operations at this location + */ fun onDisconnect(): OnDisconnect = OnDisconnect(nativeReference.onDisconnect()) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("setValue(value) { this.encodeDefaults = encodeDefaults }")) @@ -121,26 +358,113 @@ class DatabaseReference internal constructor(@PublishedApi internal val nativeRe suspend fun updateChildren(update: Map, encodeDefaults: Boolean) = updateChildren(update) { this.encodeDefaults = encodeDefaults } + + /** + * Update the specific child keys to the specified values. Passing null in a map to + * updateChildren() will remove the value at the specified location. + * + * @param update The paths to update and their new values + * @return The {@link Task} for this operation. + */ suspend inline fun updateChildren(update: Map, buildSettings: EncodeSettings.Builder.() -> Unit = {}) = nativeReference.updateEncodedChildren( encodeAsObject(update, buildSettings)) + /** + * Set the value at this location to 'null' + * + * @return The {@link Task} for this operation. + */ suspend fun removeValue() = nativeReference.removeValue() + /** + * Run a transaction on the data at this location. + * + * @param handler An object to handle running the transaction + */ suspend fun runTransaction(strategy: KSerializer, buildSettings: EncodeDecodeSettingsBuilder.() -> Unit = {}, transactionUpdate: (currentData: T) -> T): DataSnapshot = nativeReference.runTransaction(strategy, buildSettings, transactionUpdate) } +/** + * A DataSnapshot instance contains data from a Firebase Database location. Any time you read + * Database data, you receive the data as a DataSnapshot. + * + * They are efficiently-generated immutable copies of the data at a Firebase Database location. They + * can't be modified and will never change. To modify data at a location, use a
+ * [DatabaseReference] reference (e.g. with [DatabaseReference.setValue]). + */ expect class DataSnapshot { + /** + * Returns true if the snapshot contains a non-null value. + * + * @return True if the snapshot contains a non-null value, otherwise false + */ val exists: Boolean + + /** + * @return The key name for the source location of this snapshot or null if this snapshot points + * to the database root. + */ val key: String? + + /** + * Used to obtain a reference to the source location for this snapshot. + * + * @return A DatabaseReference corresponding to the location that this snapshot came from + */ val ref: DatabaseReference + + /** + * [value] returns the data contained in this snapshot as native types. + * + * @return The data contained in this snapshot as native types or null if there is no data at this + * location. + */ val value: Any? + + /** + * [value] returns the data contained in this snapshot as native types. + * + * @return The data contained in this snapshot as native types or null if there is no data at this + * location. + */ inline fun value(): T + + /** + * [value] returns the data contained in this snapshot as native types. + * + * @return The data contained in this snapshot as native types or null if there is no data at this + * location. + */ inline fun value(strategy: DeserializationStrategy, buildSettings: DecodeSettings.Builder.() -> Unit = {}): T + + fun child(path: String): DataSnapshot + + /** + * Indicates whether this snapshot has any children + * + * @return True if the snapshot has any children, otherwise false + */ val hasChildren: Boolean + + /** + * Gives access to all of the immediate children of this snapshot. Can be used in native for + * loops: + * + * ``` + * for (DataSnapshot child : parent.getChildren()) { + *     ... + * } + * ``` + * + * @return The immediate children of this snapshot + */ val children: Iterable } +/** + * Exception that gets thrown when an operation on Firebase Database fails. + */ expect class DatabaseException(message: String?, cause: Throwable?) : RuntimeException @PublishedApi @@ -151,22 +475,93 @@ internal expect class NativeOnDisconnect { suspend fun updateEncodedChildren(encodedUpdate: EncodedObject) } +/** + * The OnDisconnect class is used to manage operations that will be run on the server when this + * client disconnects. It can be used to add or remove data based on a client's connection status. + * It is very useful in applications looking for 'presence' functionality. + * + * Instances of this class are obtained by calling [DatabaseReference.onDisconnect] + * on a Firebase Database ref. + */ class OnDisconnect internal constructor(@PublishedApi internal val native: NativeOnDisconnect) { + /** + * Remove the value at this location when the client disconnects + * + * @return The {@link Task} for this operation. + */ suspend fun removeValue() = native.removeValue() + + /** + * Cancel any disconnect operations that are queued up at this location + */ suspend fun cancel() = native.cancel() + + /** + * Ensure the data at this location is set to the specified value when the client is disconnected + * (due to closing the browser, navigating to a new page, or network issues). + * + * This method is especially useful for implementing "presence" systems, where a value should be + * changed or cleared when a user disconnects so that they appear "offline" to other users. + * + * @param value The value to be set when a disconnect occurs or null to delete the existing value + */ @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("setValue(value) { this.encodeDefaults = encodeDefaults }")) suspend inline fun setValue(value: T?, encodeDefaults: Boolean) = setValue(value) { this.encodeDefaults = encodeDefaults } + + + /** + * Ensure the data at this location is set to the specified value when the client is disconnected + * (due to closing the browser, navigating to a new page, or network issues). + * + * This method is especially useful for implementing "presence" systems, where a value should be + * changed or cleared when a user disconnects so that they appear "offline" to other users. + * + * @param value The value to be set when a disconnect occurs or null to delete the existing value + */ suspend inline fun setValue(value: T?, buildSettings: EncodeSettings.Builder.() -> Unit = {}) = native.setValue(encode(value, buildSettings)) + + + /** + * Ensure the data at this location is set to the specified value when the client is disconnected + * (due to closing the browser, navigating to a new page, or network issues). + * + * This method is especially useful for implementing "presence" systems, where a value should be + * changed or cleared when a user disconnects so that they appear "offline" to other users. + * + * @param value The value to be set when a disconnect occurs or null to delete the existing value + */ @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("setValue(strategy, value) { this.encodeDefaults = encodeDefaults }")) suspend fun setValue(strategy: SerializationStrategy, value: T, encodeDefaults: Boolean) = setValue(strategy, value) { this.encodeDefaults = encodeDefaults } + + + /** + * Ensure the data at this location is set to the specified value when the client is disconnected + * (due to closing the browser, navigating to a new page, or network issues). + * + * This method is especially useful for implementing "presence" systems, where a value should be + * changed or cleared when a user disconnects so that they appear "offline" to other users. + * + * @param value The value to be set when a disconnect occurs or null to delete the existing value + */ suspend inline fun setValue(strategy: SerializationStrategy, value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}) = setValue(encode(strategy, value, buildSettings)) + /** + * Ensure the data has the specified child values updated when the client is disconnected + * + * @param update The paths to update, along with their desired values + */ suspend inline fun updateChildren(update: Map, buildSettings: EncodeSettings.Builder.() -> Unit = {}) = native.updateEncodedChildren( encodeAsObject(update, buildSettings) ) + + /** + * Ensure the data has the specified child values updated when the client is disconnected + * + * @param update The paths to update, along with their desired values + */ @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("updateChildren(update) { this.encodeDefaults = encodeDefaults }")) suspend fun updateChildren(update: Map, encodeDefaults: Boolean) = updateChildren(update) { this.encodeDefaults = encodeDefaults