diff --git a/cumulusci.yml b/cumulusci.yml index 64e8a897..ddab7b81 100644 --- a/cumulusci.yml +++ b/cumulusci.yml @@ -3,6 +3,7 @@ project: name: Snowfakery dependencies: - github: https://github.com/SalesforceFoundation/NPSP + source_format: sfdx sources: npsp: diff --git a/examples/multiselect.yml b/examples/multiselect.yml new file mode 100644 index 00000000..9b2cf17a --- /dev/null +++ b/examples/multiselect.yml @@ -0,0 +1,18 @@ +- plugin: snowfakery.standard_plugins.experimental.multiselect.Multiselect +- object: DNA + fields: + with_replacement: + Multiselect.random_choices: + min: 0 + max: 10 + with_replacement: True + choices: A,C,G,T + no_replacement: + Multiselect.random_choices: + min: 0 + max: 10 + with_replacement: False + choices: A,C,G,T + defaults: # no replacement, min=1, max=len(choices) + Multiselect.random_choices: + choices: A,C,G,T diff --git a/examples/salesforce/custom_field_multiselect.yml b/examples/salesforce/custom_field_multiselect.yml new file mode 100644 index 00000000..15948b9d --- /dev/null +++ b/examples/salesforce/custom_field_multiselect.yml @@ -0,0 +1,12 @@ +# use `cci task run dx_push` to push the appropriate metadata + +- plugin: snowfakery.standard_plugins.experimental.multiselect.Multiselect +- object: Contact + fields: + FirstName: + fake: FirstName + LastName: + fake: LastName + Types__c: + Multiselect.random_choices: + choices: Donor;Volunteer;Staff Member diff --git a/force-app/main/default/objects/Contact/fields/Types__c.field-meta.xml b/force-app/main/default/objects/Contact/fields/Types__c.field-meta.xml new file mode 100644 index 00000000..57597b2a --- /dev/null +++ b/force-app/main/default/objects/Contact/fields/Types__c.field-meta.xml @@ -0,0 +1,31 @@ + + + Types__c + false + + false + false + MultiselectPicklist + + true + + false + + Donor + false + + + + Staff Member + false + + + + Volunteer + false + + + + + 3 + diff --git a/force-app/main/default/profiles/Admin.profile-meta.xml b/force-app/main/default/profiles/Admin.profile-meta.xml new file mode 100644 index 00000000..521262de --- /dev/null +++ b/force-app/main/default/profiles/Admin.profile-meta.xml @@ -0,0 +1,9 @@ + + + false + + true + Contact.Types__c + true + + diff --git a/sfdx-project.json b/sfdx-project.json new file mode 100644 index 00000000..60fec5ce --- /dev/null +++ b/sfdx-project.json @@ -0,0 +1,12 @@ +{ + "packageDirectories": [ + { + "path": "force-app", + "default": true + } + ], + "name": "snowfakery-demo", + "namespace": "", + "sfdcLoginUrl": "https://login.salesforce.com", + "sourceApiVersion": "51.0" +} diff --git a/snowfakery/standard_plugins/experimental/multiselect.py b/snowfakery/standard_plugins/experimental/multiselect.py new file mode 100644 index 00000000..b219fca7 --- /dev/null +++ b/snowfakery/standard_plugins/experimental/multiselect.py @@ -0,0 +1,29 @@ +import random +import builtins + +from snowfakery import SnowfakeryPlugin + + +# This is experimental primarily because it hard-codes the separator. +# It would be superior for the output stream to select the separator. + +# Also not thrilled that it cannot accept a list as input. + + +class Multiselect(SnowfakeryPlugin): + class Functions: + def random_choices( + self, + choices: str, + min: int = 1, + max: int = None, + with_replacement: bool = False, + separator=";", + ): + choices = choices.split(",") + max = max or len(choices) + num_choices = random.randint(min, builtins.min(max, len(choices))) + if with_replacement: + return ";".join(random.choices(choices, k=num_choices)) + else: + return ";".join(random.sample(choices, k=num_choices)) diff --git a/tests/test_custom_plugins_and_providers.py b/tests/test_custom_plugins_and_providers.py index ace34444..ec743768 100644 --- a/tests/test_custom_plugins_and_providers.py +++ b/tests/test_custom_plugins_and_providers.py @@ -2,6 +2,7 @@ import math import operator from base64 import b64decode +from pathlib import Path from snowfakery import SnowfakeryPlugin, lazy from snowfakery.plugins import PluginResult @@ -307,3 +308,9 @@ def test_null_attributes(self): with pytest.raises(DataGenError) as e: generate_data(StringIO(yaml)) assert 6 > e.value.line_num >= 3 + + def test_experimental_multiselect(self, generated_rows): + example = Path(__file__).parent.parent / "examples/multiselect.yml" + with open(example) as f: + generate_data(f) + assert all(ch in "ACGT;" for ch in generated_rows.row_values(0, "defaults"))