Skip to content

Commit 88709e8

Browse files
authored
Revert "refactor: remove ListParam & MultiSelectInput (#85)"
This reverts commit bbf183c.
1 parent 7d3ef9d commit 88709e8

File tree

5 files changed

+123
-31
lines changed

5 files changed

+123
-31
lines changed

Diff for: samples/basic_params/functions/main.py

+13
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@
2828
default="/images/processed",
2929
)
3030

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+
3143
delete_original = params.BoolParam(
3244
"DELETE_ORIGINAL_FILE",
3345
label="delete the original file",
@@ -56,6 +68,7 @@ def resize_images(event: storage_fn.CloudEvent[storage_fn.StorageObjectData]):
5668
This function will be triggered when a new object is created in the bucket.
5769
"""
5870
print("Got a new image:", event)
71+
print("Selected image types:", image_type.value)
5972
print("Selected bucket resource:", bucket.value)
6073
print("Selected output location:", output_path.value)
6174
print("Testing a not so secret api key:", image_resize_api_secret.value)

Diff for: src/firebase_functions/params.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""Module for params that can make Cloud Functions codebases generic."""
1515

1616
import abc as _abc
17+
import json as _json
1718
import dataclasses as _dataclasses
1819
import os as _os
1920
import re as _re
@@ -139,6 +140,18 @@ class SelectInput(_typing.Generic[_T]):
139140
"""A list of user selectable options."""
140141

141142

143+
@_dataclasses.dataclass(frozen=True)
144+
class MultiSelectInput():
145+
"""
146+
Specifies that a Param's value should be determined by having the user select
147+
a subset from a list of pre-canned options interactively at deploy-time.
148+
Will result in errors if used on Params of type other than string[].
149+
"""
150+
151+
options: list[SelectOption[str]]
152+
"""A list of user selectable options."""
153+
154+
142155
@_dataclasses.dataclass(frozen=True)
143156
class TextInput:
144157
"""
@@ -215,7 +228,8 @@ class Param(Expression[_T]):
215228
deployments.
216229
"""
217230

218-
input: TextInput | ResourceInput | SelectInput[_T] | None = None
231+
input: TextInput | ResourceInput | SelectInput[
232+
_T] | MultiSelectInput | None = None
219233
"""
220234
The type of input that is required for this param, e.g. TextInput.
221235
"""
@@ -355,6 +369,32 @@ def value(self) -> bool:
355369
return False
356370

357371

372+
@_dataclasses.dataclass(frozen=True)
373+
class ListParam(Param[list]):
374+
"""A parameter as a list of strings."""
375+
376+
@property
377+
def value(self) -> list[str]:
378+
if _os.environ.get(self.name) is not None:
379+
# If the environment variable starts with "[" and ends with "]",
380+
# then assume it is a JSON array and try to parse it.
381+
# (This is for Cloud Run (v2 Functions), the environment variable is a JSON array.)
382+
if _os.environ[self.name].startswith("[") and _os.environ[
383+
self.name].endswith("]"):
384+
try:
385+
return _json.loads(_os.environ[self.name])
386+
except _json.JSONDecodeError:
387+
return []
388+
# Otherwise, split the string by commas.
389+
# (This is for emulator & the Firebase CLI generated .env file, the environment
390+
# variable is a comma-separated list.)
391+
return list(filter(len, _os.environ[self.name].split(",")))
392+
if self.default is not None:
393+
return self.default.value if isinstance(
394+
self.default, Expression) else self.default
395+
return []
396+
397+
358398
@_dataclasses.dataclass(frozen=True)
359399
class _DefaultStringParam(StringParam):
360400
"""

Diff for: src/firebase_functions/private/manifest.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ class ManifestStack:
181181

182182

183183
def _param_input_to_spec(
184-
param_input: _params.TextInput | _params.ResourceInput | _params.SelectInput
184+
param_input: _params.TextInput | _params.ResourceInput |
185+
_params.SelectInput | _params.MultiSelectInput
185186
) -> dict[str, _typing.Any]:
186187
if isinstance(param_input, _params.TextInput):
187188
return {
@@ -204,9 +205,11 @@ def _param_input_to_spec(
204205
},
205206
}
206207

207-
if isinstance(param_input, _params.SelectInput):
208+
if isinstance(param_input, (_params.MultiSelectInput, _params.SelectInput)):
209+
key = "select" if isinstance(param_input,
210+
_params.SelectInput) else "multiSelect"
208211
return {
209-
"select": {
212+
key: {
210213
"options": [{
211214
key: value for key, value in {
212215
"value": option.value,
@@ -242,6 +245,8 @@ def _param_to_spec(
242245
spec_dict["type"] = "float"
243246
elif isinstance(param, _params.SecretParam):
244247
spec_dict["type"] = "secret"
248+
elif isinstance(param, _params.ListParam):
249+
spec_dict["type"] = "list"
245250
elif isinstance(param, _params.StringParam):
246251
spec_dict["type"] = "string"
247252
else:

Diff for: tests/test_manifest.py

+25-27
Original file line numberDiff line numberDiff line change
@@ -68,43 +68,41 @@
6868
_params.FloatParam("FLOAT_TEST", immutable=True),
6969
_params.SecretParam("SECRET_TEST"),
7070
_params.StringParam("STRING_TEST"),
71+
_params.ListParam("LIST_TEST", default=["1", "2", "3"]),
7172
],
7273
requiredAPIs=[{
7374
"api": "test_api",
7475
"reason": "testing"
75-
}],
76-
)
76+
}])
7777

7878
full_stack_dict = {
7979
"specVersion": "v1alpha1",
8080
"endpoints": {
8181
"test": full_endpoint_dict
8282
},
83-
"params": [
84-
{
85-
"name": "BOOL_TEST",
86-
"type": "boolean",
87-
"default": False,
88-
},
89-
{
90-
"name": "INT_TEST",
91-
"type": "int",
92-
"description": "int_description"
93-
},
94-
{
95-
"name": "FLOAT_TEST",
96-
"type": "float",
97-
"immutable": True,
98-
},
99-
{
100-
"name": "SECRET_TEST",
101-
"type": "secret"
102-
},
103-
{
104-
"name": "STRING_TEST",
105-
"type": "string"
106-
},
107-
],
83+
"params": [{
84+
"name": "BOOL_TEST",
85+
"type": "boolean",
86+
"default": False,
87+
}, {
88+
"name": "INT_TEST",
89+
"type": "int",
90+
"description": "int_description"
91+
}, {
92+
"name": "FLOAT_TEST",
93+
"type": "float",
94+
"immutable": True,
95+
}, {
96+
"name": "SECRET_TEST",
97+
"type": "secret"
98+
}, {
99+
"name": "STRING_TEST",
100+
"type": "string"
101+
}, {
102+
"default": ["1", "2", "3"],
103+
"name": "LIST_TEST",
104+
"type": "list"
105+
}],
108106
"requiredAPIs": [{
109107
"api": "test_api",
110108
"reason": "testing"

Diff for: tests/test_params.py

+36
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,42 @@ def test_string_param_equality(self):
146146
is False), "Failure, equality check returned False"
147147

148148

149+
class TestListParams:
150+
"""ListParam unit tests."""
151+
152+
def test_list_param_value(self):
153+
"""Testing if list param correctly returns list values."""
154+
environ["LIST_VALUE_TEST1"] = "item1,item2"
155+
assert params.ListParam("LIST_VALUE_TEST1").value == ["item1","item2"], \
156+
'Failure, params value != ["item1","item2"]'
157+
158+
def test_list_param_filter_empty_strings(self):
159+
"""Testing if list param correctly returns list values wth empty strings excluded."""
160+
environ["LIST_VALUE_TEST2"] = ",,item1,item2,,,item3,"
161+
assert params.ListParam("LIST_VALUE_TEST2").value == ["item1","item2", "item3"], \
162+
'Failure, params value != ["item1","item2", "item3"]'
163+
164+
def test_list_param_empty_default(self):
165+
"""Testing if list param defaults to an empty list if no value and no default."""
166+
assert params.ListParam("LIST_DEFAULT_TEST1").value == [], \
167+
"Failure, params value is not an empty list"
168+
169+
def test_list_param_default(self):
170+
"""Testing if list param defaults to the provided default value."""
171+
assert (params.ListParam("LIST_DEFAULT_TEST2", default=["1", "2"]).value
172+
== ["1", "2"]), \
173+
'Failure, params default value != ["1", "2"]'
174+
175+
def test_list_param_equality(self):
176+
"""Test list equality."""
177+
assert (params.ListParam("LIST_TEST1",
178+
default=["123"]).equals(["123"]).value
179+
is True), "Failure, equality check returned False"
180+
assert (params.ListParam("LIST_TEST2",
181+
default=["456"]).equals(["123"]).value
182+
is False), "Failure, equality check returned False"
183+
184+
149185
class TestParamsManifest:
150186
"""
151187
Tests any created params are tracked for the purposes

0 commit comments

Comments
 (0)