Skip to content

Commit 11f799f

Browse files
authored
feat: finalize param inputs + multiple fixes (#82)
1 parent bc41f56 commit 11f799f

File tree

13 files changed

+367
-92
lines changed

13 files changed

+367
-92
lines changed

samples/basic_params/.firebaserc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"projects": {
3+
"default": "python-functions-testing"
4+
}
5+
}

samples/basic_params/.gitignore

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
firebase-debug.log*
8+
firebase-debug.*.log*
9+
10+
# Firebase cache
11+
.firebase/
12+
13+
# Firebase config
14+
15+
# Uncomment this if you'd like others to create their own Firebase project.
16+
# For a team working on the same Firebase project(s), it is recommended to leave
17+
# it commented so all members can deploy to the same project(s) in .firebaserc.
18+
# .firebaserc
19+
20+
# Runtime data
21+
pids
22+
*.pid
23+
*.seed
24+
*.pid.lock
25+
26+
# Directory for instrumented libs generated by jscoverage/JSCover
27+
lib-cov
28+
29+
# Coverage directory used by tools like istanbul
30+
coverage
31+
32+
# nyc test coverage
33+
.nyc_output
34+
35+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36+
.grunt
37+
38+
# Bower dependency directory (https://bower.io/)
39+
bower_components
40+
41+
# node-waf configuration
42+
.lock-wscript
43+
44+
# Compiled binary addons (http://nodejs.org/api/addons.html)
45+
build/Release
46+
47+
# Dependency directories
48+
node_modules/
49+
50+
# Optional npm cache directory
51+
.npm
52+
53+
# Optional eslint cache
54+
.eslintcache
55+
56+
# Optional REPL history
57+
.node_repl_history
58+
59+
# Output of 'npm pack'
60+
*.tgz
61+
62+
# Yarn Integrity file
63+
.yarn-integrity
64+
65+
# dotenv environment variables file
66+
.env

samples/basic_params/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Required to avoid a 'duplicate modules' mypy error
2+
# in monorepos that have multiple main.py files.
3+
# https://github.com/python/mypy/issues/4008

samples/basic_params/firebase.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"functions": [
3+
{
4+
"source": "functions",
5+
"codebase": "default",
6+
"ignore": [
7+
"venv"
8+
]
9+
}
10+
]
11+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# pyenv
2+
.python-version
3+
4+
# Installer logs
5+
pip-log.txt
6+
pip-delete-this-directory.txt
7+
8+
# Environments
9+
.env
10+
.venv
11+
venv/
12+
venv.bak/
13+
__pycache__
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""
2+
Example Function params & inputs.
3+
"""
4+
from firebase_functions import storage_fn, params
5+
from firebase_admin import initialize_app
6+
7+
initialize_app()
8+
9+
bucket = params.StringParam(
10+
"BUCKET",
11+
label="storage bucket",
12+
description="The bucket to resize images from.",
13+
input=params.ResourceInput(type=params.ResourceType.STORAGE_BUCKET),
14+
default=params.STORAGE_BUCKET,
15+
)
16+
17+
output_path = params.StringParam(
18+
"OUTPUT_PATH",
19+
label="storage bucket output path",
20+
description=
21+
"The path of in the bucket where processed images will be stored.",
22+
input=params.TextInput(
23+
example="/images/processed",
24+
validation_regex=r"^\/.*$",
25+
validation_error_message=
26+
"Must be a valid path starting with a forward slash",
27+
),
28+
default="/images/processed",
29+
)
30+
31+
image_type = params.ListParam(
32+
"IMAGE_TYPE",
33+
label="convert image to preferred types",
34+
description="The image types you'd like your source image to convert to.",
35+
input=params.MultiSelectInput([
36+
params.SelectOption(value="jpeg", label="jpeg"),
37+
params.SelectOption(value="png", label="png"),
38+
params.SelectOption(value="webp", label="webp"),
39+
]),
40+
default=["jpeg", "png"],
41+
)
42+
43+
delete_original = params.BoolParam(
44+
"DELETE_ORIGINAL_FILE",
45+
label="delete the original file",
46+
description=
47+
"Do you want to automatically delete the original file from the Cloud Storage?",
48+
input=params.SelectInput([
49+
params.SelectOption(value=True, label="Delete on any resize attempt"),
50+
params.SelectOption(value=False, label="Don't delete"),
51+
],),
52+
default=True,
53+
)
54+
55+
image_resize_api_secret = params.SecretParam(
56+
"IMAGE_RESIZE_API_SECRET",
57+
label="image resize api secret",
58+
description="The fake secret key to use for the image resize API.",
59+
)
60+
61+
62+
@storage_fn.on_object_finalized(
63+
bucket=bucket,
64+
secrets=[image_resize_api_secret],
65+
)
66+
def resize_images(event: storage_fn.CloudEvent[storage_fn.StorageObjectData]):
67+
"""
68+
This function will be triggered when a new object is created in the bucket.
69+
"""
70+
print("Got a new image:", event)
71+
print("Selected image types:", image_type.value)
72+
print("Selected bucket resource:", bucket.value)
73+
print("Selected output location:", output_path.value)
74+
print("Testing a not so secret api key:", image_resize_api_secret.value)
75+
print("Should original images be deleted?:", delete_original.value)
76+
# TODO: Implement your image resize logic
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Not published yet,
2+
# firebase-functions-python >= 0.0.1
3+
# so we use a relative path during development:
4+
./../../../
5+
# Or switch to git ref for deployment testing:
6+
# git+https://github.com/firebase/firebase-functions-python.git@main#egg=firebase-functions
7+
8+
firebase-admin >= 6.0.1

src/firebase_functions/options.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,17 @@ def _asdict_with_global_options(self) -> dict:
313313
for option in resettable_options:
314314
if option not in merged_options:
315315
merged_options[option] = RESET_VALUE
316+
317+
if self.secrets and not self.secrets == _util.Sentinel:
318+
319+
def convert_secret(secret) -> str:
320+
secret_value = secret
321+
if isinstance(secret, SecretParam):
322+
secret_value = secret.name
323+
return secret_value
324+
325+
merged_options["secrets"] = list(
326+
map(convert_secret, _typing.cast(list, self.secrets)))
316327
# _util.Sentinel values are converted to `None` in ManifestEndpoint generation
317328
# after other None values are removed - so as to keep them in the generated
318329
# YAML output as 'null' values.
@@ -322,18 +333,14 @@ def _endpoint(self, **kwargs) -> _manifest.ManifestEndpoint:
322333
assert kwargs["func_name"] is not None
323334
options_dict = self._asdict_with_global_options()
324335
options = self.__class__(**options_dict)
325-
326336
secret_envs: list[
327337
_manifest.SecretEnvironmentVariable] | _util.Sentinel = []
328338
if options.secrets is not None:
329339
if isinstance(options.secrets, list):
330340

331341
def convert_secret(
332342
secret) -> _manifest.SecretEnvironmentVariable:
333-
secret_value = secret
334-
if isinstance(secret, SecretParam):
335-
secret_value = secret.name
336-
return {"key": secret_value}
343+
return {"key": secret}
337344

338345
secret_envs = list(
339346
map(convert_secret, _typing.cast(list, options.secrets)))
@@ -834,7 +841,7 @@ class StorageOptions(RuntimeOptions):
834841
Internal use only.
835842
"""
836843

837-
bucket: str | None = None
844+
bucket: str | Expression[str] | None = None
838845
"""
839846
The name of the bucket to watch for Storage events.
840847
"""

0 commit comments

Comments
 (0)