Skip to content

feat: finalize param inputs + multiple fixes #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions samples/basic_params/.firebaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projects": {
"default": "python-functions-testing"
}
}
66 changes: 66 additions & 0 deletions samples/basic_params/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*

# Firebase cache
.firebase/

# Firebase config

# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
3 changes: 3 additions & 0 deletions samples/basic_params/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Required to avoid a 'duplicate modules' mypy error
# in monorepos that have multiple main.py files.
# https://github.com/python/mypy/issues/4008
11 changes: 11 additions & 0 deletions samples/basic_params/firebase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"functions": [
{
"source": "functions",
"codebase": "default",
"ignore": [
"venv"
]
}
]
}
13 changes: 13 additions & 0 deletions samples/basic_params/functions/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# pyenv
.python-version

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Environments
.env
.venv
venv/
venv.bak/
__pycache__
76 changes: 76 additions & 0 deletions samples/basic_params/functions/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
Example Function params & inputs.
"""
from firebase_functions import storage_fn, params
from firebase_admin import initialize_app

initialize_app()

bucket = params.StringParam(
"BUCKET",
label="storage bucket",
description="The bucket to resize images from.",
input=params.ResourceInput(type=params.ResourceType.STORAGE_BUCKET),
default=params.STORAGE_BUCKET,
)

output_path = params.StringParam(
"OUTPUT_PATH",
label="storage bucket output path",
description=
"The path of in the bucket where processed images will be stored.",
input=params.TextInput(
example="/images/processed",
validation_regex=r"^\/.*$",
validation_error_message=
"Must be a valid path starting with a forward slash",
),
default="/images/processed",
)

image_type = params.ListParam(
"IMAGE_TYPE",
label="convert image to preferred types",
description="The image types you'd like your source image to convert to.",
input=params.MultiSelectInput([
params.SelectOption(value="jpeg", label="jpeg"),
params.SelectOption(value="png", label="png"),
params.SelectOption(value="webp", label="webp"),
]),
default=["jpeg", "png"],
)

delete_original = params.BoolParam(
"DELETE_ORIGINAL_FILE",
label="delete the original file",
description=
"Do you want to automatically delete the original file from the Cloud Storage?",
input=params.SelectInput([
params.SelectOption(value=True, label="Delete on any resize attempt"),
params.SelectOption(value=False, label="Don't delete"),
],),
default=True,
)

image_resize_api_secret = params.SecretParam(
"IMAGE_RESIZE_API_SECRET",
label="image resize api secret",
description="The fake secret key to use for the image resize API.",
)


@storage_fn.on_object_finalized(
bucket=bucket,
secrets=[image_resize_api_secret],
)
def resize_images(event: storage_fn.CloudEvent[storage_fn.StorageObjectData]):
"""
This function will be triggered when a new object is created in the bucket.
"""
print("Got a new image:", event)
print("Selected image types:", image_type.value)
print("Selected bucket resource:", bucket.value)
print("Selected output location:", output_path.value)
print("Testing a not so secret api key:", image_resize_api_secret.value)
print("Should original images be deleted?:", delete_original.value)
# TODO: Implement your image resize logic
8 changes: 8 additions & 0 deletions samples/basic_params/functions/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Not published yet,
# firebase-functions-python >= 0.0.1
# so we use a relative path during development:
./../../../
# Or switch to git ref for deployment testing:
# git+https://github.com/firebase/firebase-functions-python.git@main#egg=firebase-functions

firebase-admin >= 6.0.1
19 changes: 13 additions & 6 deletions src/firebase_functions/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,17 @@ def _asdict_with_global_options(self) -> dict:
for option in resettable_options:
if option not in merged_options:
merged_options[option] = RESET_VALUE

if self.secrets and not self.secrets == _util.Sentinel:

def convert_secret(secret) -> str:
secret_value = secret
if isinstance(secret, SecretParam):
secret_value = secret.name
return secret_value

merged_options["secrets"] = list(
map(convert_secret, _typing.cast(list, self.secrets)))
# _util.Sentinel values are converted to `None` in ManifestEndpoint generation
# after other None values are removed - so as to keep them in the generated
# YAML output as 'null' values.
Expand All @@ -322,18 +333,14 @@ def _endpoint(self, **kwargs) -> _manifest.ManifestEndpoint:
assert kwargs["func_name"] is not None
options_dict = self._asdict_with_global_options()
options = self.__class__(**options_dict)

secret_envs: list[
_manifest.SecretEnvironmentVariable] | _util.Sentinel = []
if options.secrets is not None:
if isinstance(options.secrets, list):

def convert_secret(
secret) -> _manifest.SecretEnvironmentVariable:
secret_value = secret
if isinstance(secret, SecretParam):
secret_value = secret.name
return {"key": secret_value}
return {"key": secret}

secret_envs = list(
map(convert_secret, _typing.cast(list, options.secrets)))
Expand Down Expand Up @@ -834,7 +841,7 @@ class StorageOptions(RuntimeOptions):
Internal use only.
"""

bucket: str | None = None
bucket: str | Expression[str] | None = None
"""
The name of the bucket to watch for Storage events.
"""
Expand Down
Loading