in FirebaseAdmin/FirebaseAdmin/FirebaseApp.cs [32:340]
internal delegate TResult ServiceFactory<out TResult>()
where TResult : IFirebaseService;
/// <summary>
/// This is the entry point to the Firebase Admin SDK. It holds configuration and state common
/// to all APIs exposed from the SDK.
/// <para>Use one of the provided <c>Create()</c> methods to obtain a new instance.
/// See <a href="https://firebase.google.com/docs/admin/setup#initialize_the_sdk">
/// Initialize the SDK</a> for code samples and detailed documentation.</para>
/// </summary>
public sealed class FirebaseApp
{
internal static readonly IReadOnlyList<string> DefaultScopes = ImmutableList.Create(
"https://www.googleapis.com/auth/firebase", // RTDB.
"https://www.googleapis.com/auth/userinfo.email", // RTDB
"https://www.googleapis.com/auth/identitytoolkit", // User management
"https://www.googleapis.com/auth/devstorage.full_control", // Cloud Storage
"https://www.googleapis.com/auth/cloud-platform", // Cloud Firestore
"https://www.googleapis.com/auth/datastore");
private const string DefaultAppName = "[DEFAULT]";
private static readonly Dictionary<string, FirebaseApp> Apps = new Dictionary<string, FirebaseApp>();
private static readonly ILogger Logger = ApplicationContext.Logger.ForType<FirebaseApp>();
// Guards the mutable state local to an app instance.
private readonly object appLock = new object();
private readonly AppOptions options;
// A collection of stateful services initialized using this app instance (e.g.
// FirebaseAuth). Services are tracked here so they can be cleaned up when the app is
// deleted.
private readonly Dictionary<string, IFirebaseService> services = new Dictionary<string, IFirebaseService>();
private bool deleted = false;
private FirebaseApp(AppOptions options, string name)
{
this.options = new AppOptions(options);
if (this.options.Credential == null)
{
throw new ArgumentNullException("Credential must be set");
}
if (this.options.Credential.IsCreateScopedRequired)
{
this.options.Credential = this.options.Credential.CreateScoped(DefaultScopes);
}
if (this.options.HttpClientFactory == null)
{
throw new ArgumentNullException("HttpClientFactory must be set");
}
this.Name = name;
}
/// <summary>
/// Gets the default app instance. This property is <c>null</c> if the default app instance
/// doesn't yet exist.
/// </summary>
public static FirebaseApp DefaultInstance
{
get
{
return GetInstance(DefaultAppName);
}
}
/// <summary>
/// Gets a copy of the <see cref="AppOptions"/> this app was created with.
/// </summary>
public AppOptions Options
{
get
{
return new AppOptions(this.options);
}
}
/// <summary>
/// Gets the name of this app.
/// </summary>
public string Name { get; }
/// <summary>
/// Returns the app instance identified by the given name.
/// </summary>
/// <returns>The <see cref="FirebaseApp"/> instance with the specified name or null if it
/// doesn't exist.</returns>
/// <exception cref="System.ArgumentException">If the name argument is null or empty.</exception>
/// <param name="name">Name of the app to retrieve.</param>
public static FirebaseApp GetInstance(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("App name to lookup must not be null or empty");
}
lock (Apps)
{
FirebaseApp app;
if (Apps.TryGetValue(name, out app))
{
return app;
}
}
return null;
}
/// <summary>
/// Creates the default app instance with Google Application Default Credentials.
/// </summary>
/// <returns>The newly created <see cref="FirebaseApp"/> instance.</returns>
/// <exception cref="System.ArgumentException">If the default app instance already
/// exists.</exception>
public static FirebaseApp Create()
{
return Create(DefaultAppName);
}
/// <summary>
/// Creates an app with the specified name, and Google Application Default Credentials.
/// </summary>
/// <returns>The newly created <see cref="FirebaseApp"/> instance.</returns>
/// <exception cref="System.ArgumentException">If the default app instance already
/// exists.</exception>
/// <param name="name">Name of the app.</param>
public static FirebaseApp Create(string name)
{
return Create(null, name);
}
/// <summary>
/// Creates the default app instance with the specified options.
/// </summary>
/// <returns>The newly created <see cref="FirebaseApp"/> instance.</returns>
/// <exception cref="System.ArgumentException">If the default app instance already
/// exists.</exception>
/// <param name="options">Options to create the app with. Must at least contain the
/// <c>Credential</c>.</param>
public static FirebaseApp Create(AppOptions options)
{
return Create(options, DefaultAppName);
}
/// <summary>
/// Creates an app with the specified name and options.
/// </summary>
/// <returns>The newly created <see cref="FirebaseApp"/> instance.</returns>
/// <exception cref="System.ArgumentException">If the default app instance already
/// exists.</exception>
/// <param name="options">Options to create the app with. Must at least contain the
/// <c>Credential</c>.</param>
/// <param name="name">Name of the app.</param>
public static FirebaseApp Create(AppOptions options, string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("App name must not be null or empty");
}
options = options ?? GetOptionsFromEnvironment();
lock (Apps)
{
if (Apps.ContainsKey(name))
{
if (name == DefaultAppName)
{
throw new ArgumentException("The default FirebaseApp already exists.");
}
else
{
throw new ArgumentException($"FirebaseApp named {name} already exists.");
}
}
var app = new FirebaseApp(options, name);
Apps.Add(name, app);
return app;
}
}
/// <summary>
/// Deletes this app instance and cleans up any state associated with it. Once an app has
/// been deleted, accessing any services related to it will result in an exception.
/// If the app is already deleted, this method is a no-op.
/// </summary>
public void Delete()
{
// Clean up local state
lock (this.appLock)
{
this.deleted = true;
foreach (var entry in this.services)
{
try
{
entry.Value.Delete();
}
catch (Exception e)
{
Logger.Error(e, "Error while cleaning up service {0}", entry.Key);
}
}
this.services.Clear();
}
// Clean up global state
lock (Apps)
{
Apps.Remove(this.Name);
}
}
/// <summary>
/// Deleted all the apps created so far. Used for unit testing.
/// </summary>
internal static void DeleteAll()
{
lock (Apps)
{
var copy = new Dictionary<string, FirebaseApp>(Apps);
foreach (var entry in copy)
{
entry.Value.Delete();
}
if (Apps.Count > 0)
{
throw new InvalidOperationException("Failed to delete all apps");
}
}
}
/// <summary>
/// Returns the current version of the .NET assembly.
/// </summary>
/// <returns>A version string in major.minor.patch format.</returns>
internal static string GetSdkVersion()
{
const int majorMinorPatch = 3;
return typeof(FirebaseApp).GetTypeInfo().Assembly.GetName().Version.ToString(majorMinorPatch);
}
internal T GetOrInit<T>(string id, ServiceFactory<T> initializer)
where T : class, IFirebaseService
{
lock (this.appLock)
{
if (this.deleted)
{
throw new InvalidOperationException("Cannot use an app after it has been deleted");
}
IFirebaseService service;
if (!this.services.TryGetValue(id, out service))
{
service = initializer();
this.services.Add(id, service);
}
return (T)service;
}
}
/// <summary>
/// Returns the Google Cloud Platform project ID associated with this Firebase app. If a
/// project ID is specified in <see cref="AppOptions"/>, that value is returned. If not
/// attempts to determine a project ID from the <see cref="GoogleCredential"/> used to
/// initialize the app. Looks up the GOOGLE_CLOUD_PROJECT environment variable when all
/// else fails.
/// </summary>
/// <returns>A project ID string or null.</returns>
internal string GetProjectId()
{
if (!string.IsNullOrEmpty(this.Options.ProjectId))
{
return this.Options.ProjectId;
}
var projectId = this.Options.Credential.ToServiceAccountCredential()?.ProjectId;
if (!string.IsNullOrEmpty(projectId))
{
return projectId;
}
foreach (var variableName in new[] { "GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT" })
{
projectId = Environment.GetEnvironmentVariable(variableName);
if (!string.IsNullOrEmpty(projectId))
{
return projectId;
}
}
return null;
}
private static AppOptions GetOptionsFromEnvironment()
{
return new AppOptions()
{
Credential = GoogleCredential.GetApplicationDefault(),
};
}
}