diff --git a/src/firebase_functions/firestore_fn.py b/src/firebase_functions/firestore_fn.py index b8ae498..122a82c 100644 --- a/src/firebase_functions/firestore_fn.py +++ b/src/firebase_functions/firestore_fn.py @@ -201,7 +201,8 @@ def example(event: Event[Change[DocumentSnapshot]]) -> None: options = FirestoreOptions(**kwargs) def on_document_written_inner_decorator(func: _C1): - document_pattern = _path_pattern.PathPattern(options.document) + document_pattern = _path_pattern.PathPattern( + _util.normalize_path(options.document)) @_functools.wraps(func) def on_document_written_wrapped(raw: _ce.CloudEvent): @@ -248,7 +249,8 @@ def example(event: Event[Change[DocumentSnapshot]]) -> None: options = FirestoreOptions(**kwargs) def on_document_updated_inner_decorator(func: _C1): - document_pattern = _path_pattern.PathPattern(options.document) + document_pattern = _path_pattern.PathPattern( + _util.normalize_path(options.document)) @_functools.wraps(func) def on_document_updated_wrapped(raw: _ce.CloudEvent): @@ -295,7 +297,8 @@ def example(event: Event[DocumentSnapshot]): options = FirestoreOptions(**kwargs) def on_document_created_inner_decorator(func: _C2): - document_pattern = _path_pattern.PathPattern(options.document) + document_pattern = _path_pattern.PathPattern( + _util.normalize_path(options.document)) @_functools.wraps(func) def on_document_created_wrapped(raw: _ce.CloudEvent): @@ -342,7 +345,8 @@ def example(event: Event[DocumentSnapshot]) -> None: options = FirestoreOptions(**kwargs) def on_document_deleted_inner_decorator(func: _C2): - document_pattern = _path_pattern.PathPattern(options.document) + document_pattern = _path_pattern.PathPattern( + _util.normalize_path(options.document)) @_functools.wraps(func) def on_document_deleted_wrapped(raw: _ce.CloudEvent): diff --git a/src/firebase_functions/private/util.py b/src/firebase_functions/private/util.py index 49bf264..5838fcf 100644 --- a/src/firebase_functions/private/util.py +++ b/src/firebase_functions/private/util.py @@ -308,3 +308,10 @@ def firebase_config() -> None | FirebaseConfig: f'FIREBASE_CONFIG JSON string "{json_str}" is not valid json. {err}' ) from err return FirebaseConfig(storage_bucket=json_data.get("storageBucket")) + + +def normalize_path(path: str) -> str: + """ + Normalize a path string to a consistent format. + """ + return path.strip("/") diff --git a/tests/test_util.py b/tests/test_util.py index e13fe1c..8a40f99 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -15,7 +15,7 @@ Internal utils tests. """ from os import environ, path -from firebase_functions.private.util import firebase_config +from firebase_functions.private.util import firebase_config, normalize_path test_bucket = "python-functions-testing.appspot.com" test_config_file = path.join(path.dirname(path.realpath(__file__)), @@ -40,3 +40,21 @@ def test_firebase_config_loads_from_env_file(): environ["FIREBASE_CONFIG"] = test_config_file assert firebase_config().storage_bucket == test_bucket, ( "Failure, firebase_config did not load from env variable.") + + +def test_normalize_document_path(): + """ + Testing "document" path passed to Firestore event listener + is normalized. + """ + test_path = "/test/document/" + assert normalize_path(test_path) == "test/document", ( + "Failure, path was not normalized.") + + test_path1 = "//////test/document//////////" + assert normalize_path(test_path1) == "test/document", ( + "Failure, path was not normalized.") + + test_path2 = "test/document" + assert normalize_path(test_path2) == "test/document", ( + "Failure, path should not be changed if it is already normalized.")