Skip to content

Commit 4e753cf

Browse files
Add Firestore named database bindings (#127)
1 parent 3615bd1 commit 4e753cf

3 files changed

Lines changed: 140 additions & 0 deletions

File tree

source/Firebase/CloudFirestore/ApiDefinition.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,16 @@ interface Firestore
647647
[Export ("firestoreForApp:")]
648648
Firestore Create (Firebase.Core.App app);
649649

650+
// +(instancetype _Nonnull)firestoreForApp:(FIRApp * _Nonnull)app database:(NSString * _Nonnull)database;
651+
[Static]
652+
[Export ("firestoreForApp:database:")]
653+
Firestore Create (Firebase.Core.App app, string database);
654+
655+
// +(instancetype _Nonnull)firestoreForDatabase:(NSString * _Nonnull)database;
656+
[Static]
657+
[Export ("firestoreForDatabase:")]
658+
Firestore Create (string database);
659+
650660
// @property (copy, nonatomic) FIRFirestoreSettings * _Nonnull settings;
651661
[Export ("settings", ArgumentSemantic.Copy)]
652662
FirestoreSettings Settings { get; set; }

tests/E2E/Firebase.Foundation/FirebaseFoundationE2E/FirebaseRuntimeDriftCases.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@
7272
using ObjCRuntime;
7373
#endif
7474

75+
#if ENABLE_RUNTIME_DRIFT_CASE_CLOUDFIRESTORE_NAMED_DATABASE
76+
using Firebase.CloudFirestore;
77+
using Foundation;
78+
using ObjCRuntime;
79+
#endif
80+
7581
#if ENABLE_RUNTIME_DRIFT_CASE_CLOUDFUNCTIONS_USEFUNCTIONSEMULATORORIGIN
7682
using Firebase.CloudFunctions;
7783
using Foundation;
@@ -1875,6 +1881,119 @@ void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEven
18751881

18761882
#endif
18771883

1884+
#if ENABLE_RUNTIME_DRIFT_CASE_CLOUDFIRESTORE_NAMED_DATABASE
1885+
static Task<string> VerifyCloudFirestoreNamedDatabaseAsync()
1886+
{
1887+
const string firestoreForDatabaseSelector = "firestoreForDatabase:";
1888+
const string firestoreForAppDatabaseSelector = "firestoreForApp:database:";
1889+
const string databaseId = "(default)";
1890+
1891+
var databaseSignature = typeof(Firestore).GetMethod(
1892+
nameof(Firestore.Create),
1893+
BindingFlags.Static | BindingFlags.Public,
1894+
binder: null,
1895+
types: new[] { typeof(string) },
1896+
modifiers: null);
1897+
if (databaseSignature?.ReturnType != typeof(Firestore))
1898+
{
1899+
throw new InvalidOperationException(
1900+
$"Expected managed API '{typeof(Firestore).FullName}.{nameof(Firestore.Create)}({typeof(string).FullName})' " +
1901+
$"to return '{typeof(Firestore).FullName}' for selector '{firestoreForDatabaseSelector}', " +
1902+
$"observed '{databaseSignature?.ReturnType.FullName ?? "<missing>"}'.");
1903+
}
1904+
1905+
var appDatabaseSignature = typeof(Firestore).GetMethod(
1906+
nameof(Firestore.Create),
1907+
BindingFlags.Static | BindingFlags.Public,
1908+
binder: null,
1909+
types: new[] { typeof(Firebase.Core.App), typeof(string) },
1910+
modifiers: null);
1911+
if (appDatabaseSignature?.ReturnType != typeof(Firestore))
1912+
{
1913+
throw new InvalidOperationException(
1914+
$"Expected managed API '{typeof(Firestore).FullName}.{nameof(Firestore.Create)}({typeof(Firebase.Core.App).FullName}, {typeof(string).FullName})' " +
1915+
$"to return '{typeof(Firestore).FullName}' for selector '{firestoreForAppDatabaseSelector}', " +
1916+
$"observed '{appDatabaseSignature?.ReturnType.FullName ?? "<missing>"}'.");
1917+
}
1918+
1919+
var defaultApp = Firebase.Core.App.DefaultInstance
1920+
?? throw new InvalidOperationException("Firebase.Core.App.DefaultInstance returned null after App.Configure().");
1921+
NSException? marshaledException = null;
1922+
MarshalObjectiveCExceptionMode? marshaledExceptionMode = null;
1923+
1924+
void OnMarshalObjectiveCException(object? sender, MarshalObjectiveCExceptionEventArgs args)
1925+
{
1926+
marshaledException ??= args.Exception;
1927+
marshaledExceptionMode ??= args.ExceptionMode;
1928+
}
1929+
1930+
Runtime.MarshalObjectiveCException += OnMarshalObjectiveCException;
1931+
try
1932+
{
1933+
Firestore? defaultNamedDatabase = null;
1934+
Firestore? appNamedDatabase = null;
1935+
try
1936+
{
1937+
defaultNamedDatabase = Firestore.Create(databaseId);
1938+
appNamedDatabase = Firestore.Create(defaultApp, databaseId);
1939+
}
1940+
catch (ObjCException ex)
1941+
{
1942+
throw new InvalidOperationException(
1943+
$"Firestore named-database selectors should not throw after the missing bindings are added, but observed {ex.GetType().FullName}. " +
1944+
$"Selectors exercised: '{firestoreForDatabaseSelector}', '{firestoreForAppDatabaseSelector}'. " +
1945+
$"Database id: {databaseId}. " +
1946+
$"NSException.Name: {FormatDetail(marshaledException?.Name?.ToString())}. " +
1947+
$"NSException.Reason: {FormatDetail(marshaledException?.Reason)}. " +
1948+
$"Marshal mode: {FormatDetail(marshaledExceptionMode?.ToString())}.",
1949+
ex);
1950+
}
1951+
1952+
if (defaultNamedDatabase is null)
1953+
{
1954+
throw new InvalidOperationException($"Selector '{firestoreForDatabaseSelector}' returned null.");
1955+
}
1956+
1957+
if (appNamedDatabase is null)
1958+
{
1959+
throw new InvalidOperationException($"Selector '{firestoreForAppDatabaseSelector}' returned null.");
1960+
}
1961+
1962+
var defaultCollection = defaultNamedDatabase.GetCollection($"codex-named-database-default-{Guid.NewGuid():N}");
1963+
if (defaultCollection is null)
1964+
{
1965+
throw new InvalidOperationException(
1966+
$"Firestore instance from selector '{firestoreForDatabaseSelector}' could not create a collection reference.");
1967+
}
1968+
1969+
var appCollection = appNamedDatabase.GetCollection($"codex-named-database-app-{Guid.NewGuid():N}");
1970+
if (appCollection is null)
1971+
{
1972+
throw new InvalidOperationException(
1973+
$"Firestore instance from selector '{firestoreForAppDatabaseSelector}' could not create a collection reference.");
1974+
}
1975+
1976+
if (marshaledException is not null)
1977+
{
1978+
throw new InvalidOperationException(
1979+
$"Firestore named-database selectors completed, but Runtime.MarshalObjectiveCException captured unexpected NSException.Name '{marshaledException.Name}'. " +
1980+
$"Reason: {FormatDetail(marshaledException.Reason)}. Marshal mode: {FormatDetail(marshaledExceptionMode?.ToString())}.");
1981+
}
1982+
1983+
return Task.FromResult(
1984+
$"Firestore named-database factory APIs crossed the native selector boundary. " +
1985+
$"Database id: {databaseId}. " +
1986+
$"Default-app selector returned: {defaultNamedDatabase.GetType().FullName}. " +
1987+
$"Explicit-app selector returned: {appNamedDatabase.GetType().FullName}. " +
1988+
$"Collection references: {defaultCollection.Path}, {appCollection.Path}.");
1989+
}
1990+
finally
1991+
{
1992+
Runtime.MarshalObjectiveCException -= OnMarshalObjectiveCException;
1993+
}
1994+
}
1995+
#endif
1996+
18781997
#if ENABLE_RUNTIME_DRIFT_CASE_CLOUDFUNCTIONS_USEFUNCTIONSEMULATORORIGIN
18791998
static Task<string> VerifyCloudFunctionsUseFunctionsEmulatorOriginAsync()
18801999
{

tests/E2E/Firebase.Foundation/runtime-drift-cases.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@
126126
}
127127
]
128128
},
129+
{
130+
"id": "cloudfirestore-named-database",
131+
"method": "VerifyCloudFirestoreNamedDatabaseAsync",
132+
"bindingPackage": "AdamE.Firebase.iOS.CloudFirestore",
133+
"packages": [
134+
{
135+
"id": "AdamE.Firebase.iOS.CloudFirestore",
136+
"version": "12.6.0"
137+
}
138+
]
139+
},
129140
{
130141
"id": "cloudfunctions-usefunctionsemulatororigin",
131142
"method": "VerifyCloudFunctionsUseFunctionsEmulatorOriginAsync",

0 commit comments

Comments
 (0)