diff --git a/oscontrib/README.md b/oscontrib/README.md new file mode 100644 index 000000000..6de403c6d --- /dev/null +++ b/oscontrib/README.md @@ -0,0 +1,49 @@ +# Contributing to RudderStack + +Thanks for taking the time for contributing to this project! + +## How you can contribute a destination to this project + +To contribute a destination, you need to provide all the required data for all the fields you want as the settings to configure the destination. + +## How you can provide your destination connection setting details + +You can checkout the sample input file [**here**](https://github.com/rudderlabs/rudder-integrations-config/blob/config-generator-script/test/configData/inputData.json): + +For the above input data, the UI will look like as shown below: + +connectionSettings +configurationSettings + + +In the input file, you need to provide the destination name you want to display in the UI in the displayName field. + +Each json object inside the formFields represents a field you want to add as a connection/configuration settings to the destination. + +Description of Keys inside each JSON object: + +| Property | Description | +| ------------- | -------------------------------------------------------------------------------------- | +| type | This is the input field type you want to add | +| label | This is the display name for your field | +| configKey | This is the key to which the value for this field will be stored in the db | +| required | You can provide true as a value to make this field as required or false | +| placeholder | This is the the placeholder for this field | +| secret | You can provide true as a value to make this field as secret or false | +| preRequisites | You can add the configKey and value if you there is a preRequiste field for this field | + +The input field types that you can add are: + +| type | Description | +| ------------ | ------------------------------------------------------------------------- | +| textInput | This is a simple field for adding text inputs | +| checkbox | This is used for boolean values (true or false) | +| singleSelect | This is used for choosing one of the option from a set of defined options | +| multiSelect | This is used for choosing multiple options from a set of defined options | +| tagInput | This can be used to add multiple text inputs for a field | + +You can also contribute to any open-source RudderStack project. View our [**GitHub page**](https://github.com/rudderlabs) to see all the different projects. + +## Getting help + +For any questions, concerns, or queries, you can start by asking a question in our [**Slack**](https://rudderstack.com/join-rudderstack-slack-community/) community. diff --git a/scripts/configGenerator.py b/scripts/configGenerator.py new file mode 100644 index 000000000..1a1fdaf4d --- /dev/null +++ b/scripts/configGenerator.py @@ -0,0 +1,94 @@ +from typing import TypedDict + +import json +import os +import sys + +ConfigData = TypedDict('ConfigData', {'db_config': str, 'ui_config': str}) + +def generateConfigs(data) -> ConfigData: + # Read the content of template-db-config.json + with open('scripts/template-db-config.json', 'r') as file: + template_db_config = json.load(file) + + # Read the content of template-ui-config.json + with open('scripts/template-ui-config.json', 'r') as file: + template_ui_config = json.load(file) + + # Create db-config object with the same content + db_config = template_db_config + + db_config['displayName'] = data['displayName'] + db_config['name'] = data['displayName'] + formFields = data['formFields'] + + # Create db-config object with the same content + ui_config = template_ui_config + + # Access the necessary location and update the fields array + + def appendFieldsInGroups(settings, field): + if "sections" in settings and settings["sections"]: + groups = settings["sections"][0]["groups"] + if groups: + first_group = groups[0] + if "fields" in first_group: + del field['required'] + first_group["fields"].append(field) + + + def updateUiConfig(field): + if "uiConfig" in ui_config and "baseTemplate" in ui_config["uiConfig"]: + base_template = ui_config["uiConfig"]["baseTemplate"] + if base_template and field['required'] == True: + connection_settings = base_template[0] + appendFieldsInGroups(connection_settings, field) + elif base_template: + configuration_settings = base_template[1] + appendFieldsInGroups(configuration_settings, field) + return ui_config + + + # Iterate over JSON objects in the array + for obj in formFields: + # update field in ui-config + ui_config = updateUiConfig(obj) + # update db-config + for key, value in obj.items(): + if key == 'configKey': + db_config['config']['destConfig']['defaultConfig'].append( + value) + if key == 'secret' and value == True: + db_config['config']['secretKeys'].append( + db_config['config']['destConfig']['defaultConfig'][-1]) + + db_config = json.dumps(db_config) + ui_config = json.dumps(ui_config) + return {'db_config':db_config, 'ui_config': ui_config} + + +if __name__ == '__main__': + file_path = sys.argv[1] if len(sys.argv) > 1 else 'test/configData/inputData.json' + with open(file_path, 'r') as file: + # Load the JSON data + data = json.load(file) + + configData = generateConfigs(data) + file_path = f'src/configurations/destinations/{data["displayName"]}/db-config.json' + directory = os.path.dirname(file_path) + if not os.path.exists(directory): + os.makedirs(directory) + + with open(file_path, 'w') as file: + # Write the new content + file.write(configData['db_config']) + + + file_path = f'src/configurations/destinations/{data["displayName"]}/ui-config.json' + directory = os.path.dirname(file_path) + if not os.path.exists(directory): + os.makedirs(directory) + + with open(file_path, 'w') as file: + # Write the new content + file.write(configData['ui_config']) diff --git a/scripts/template-db-config.json b/scripts/template-db-config.json new file mode 100644 index 000000000..0e8f50817 --- /dev/null +++ b/scripts/template-db-config.json @@ -0,0 +1,31 @@ +{ + "name": "", + "displayName": "", + "config": { + "transformAt": "processor", + "transformAtV1": "processor", + "saveDestinationResponse": false, + "includeKeys": [], + "excludeKeys": [], + "supportedSourceTypes": [ + "android", + "ios", + "web", + "unity", + "amp", + "cloud", + "reactnative", + "flutter", + "cordova", + "warehouse" + ], + "supportedConnectionModes": [], + "destConfig": { + "defaultConfig": [] + }, + "secretKeys": [] + }, + "options": { + "isBeta": false + } +} diff --git a/scripts/template-ui-config.json b/scripts/template-ui-config.json new file mode 100644 index 000000000..33539a767 --- /dev/null +++ b/scripts/template-ui-config.json @@ -0,0 +1,60 @@ +{ + "uiConfig": { + "baseTemplate": [ + { + "title": "Initial setup", + "note": "Review how this destination is set up", + "sections": [ + { + "groups": [ + { + "title": "Connection settings", + "note": "Update your connection settings here", + "icon": "settings", + "fields": [] + } + ] + }, + { + "groups": [ + { + "title": "Connection mode", + "note": [ + "Update how you want to route events from your source to destination. ", + { + "text": "Get help deciding", + "link": "https://www.rudderstack.com/docs/destinations/rudderstack-connection-modes/" + } + ], + "icon": "sliders", + "fields": [] + } + ] + } + ] + }, + { + "title": "Configuration settings", + "note": "Manage the settings for your destination", + "sections": [ + { + "title": "Destination settings", + "note": "Configure advanced destination-specific settings here", + "icon": "settings", + "groups": [ + { + "title": "Configure settings", + "fields": [] + } + ] + } + ] + } + ], + "sdkTemplate": { + "title": "Web SDK settings", + "note": "not visible in the ui", + "fields": [] + } + } +} diff --git a/test/configData/db-config.json b/test/configData/db-config.json new file mode 100644 index 000000000..6bec2b986 --- /dev/null +++ b/test/configData/db-config.json @@ -0,0 +1,27 @@ +{ + "name": "aNewDest", + "displayName": "aNewDest", + "config": { + "transformAt": "processor", + "transformAtV1": "processor", + "saveDestinationResponse": false, + "includeKeys": [], + "excludeKeys": [], + "supportedSourceTypes": [ + "android", + "ios", + "web", + "unity", + "amp", + "cloud", + "reactnative", + "flutter", + "cordova", + "warehouse" + ], + "supportedConnectionModes": [], + "destConfig": { "defaultConfig": ["key1", "key2", "key3", "key4", "key5", "key6", "key7"] }, + "secretKeys": [] + }, + "options": { "isBeta": false } +} diff --git a/test/configData/inputData.json b/test/configData/inputData.json new file mode 100644 index 000000000..1c29954cf --- /dev/null +++ b/test/configData/inputData.json @@ -0,0 +1,97 @@ +{ + "displayName": "aNewDest", + "formFields": [ + { + "type": "textInput", + "label": "Label1", + "configKey": "key1", + "required": true, + "regex": "^(.{1,100})$", + "placeholder": "value1", + "secret": false + }, + { + "type": "textInput", + "label": "Label2", + "configKey": "key2", + "required": true, + "regex": "^(.{1,100})$", + "placeholder": "value2", + "secret": false + }, + { + "type": "checkbox", + "label": "Label3", + "required": false, + "note": "Any additional note for this field", + "configKey": "key3", + "default": true + }, + { + "type": "textInput", + "label": "Label4", + "configKey": "key4", + "required": false, + "regex": "^(.{0,100})$", + "placeholder": "value4", + "secret": false, + "preRequisites": { + "fields": [ + { + "configKey": "key3", + "value": true + } + ] + } + }, + { + "type": "singleSelect", + "configKey": "key5", + "label": "Label5", + "note": "Any additional note for this field", + "required": false, + "options": [ + { + "label": "optionLabel1", + "value": "optionKey1" + }, + { + "label": "optionLabel2", + "value": "optionKey2" + }, + { + "label": "optionLabel3", + "value": "optionKey3" + } + ], + "default": "optionKey2" + }, + { + "type": "multiSelect", + "label": "Label6", + "configKey": "key6", + "required": false, + "note": "Any additional note for this field", + "options": [ + { + "label": "optionLabel1", + "value": "optionKey1" + }, + { + "label": "optionLabel2", + "value": "optionKey2" + } + ], + "default": ["optionKey1"] + }, + { + "type": "tagInput", + "label": "Label7", + "configKey": "key7", + "required": false, + "note": "Any additional note for this field", + "tagKey": "tagKey1", + "placeholder": "e.g: Credit card visit" + } + ] +} diff --git a/test/configData/ui-config.json b/test/configData/ui-config.json new file mode 100644 index 000000000..e0cd5415e --- /dev/null +++ b/test/configData/ui-config.json @@ -0,0 +1,121 @@ +{ + "uiConfig": { + "baseTemplate": [ + { + "title": "Initial setup", + "note": "Review how this destination is set up", + "sections": [ + { + "groups": [ + { + "title": "Connection settings", + "note": "Update your connection settings here", + "icon": "settings", + "fields": [ + { + "type": "textInput", + "label": "Label1", + "configKey": "key1", + "regex": "^(.{1,100})$", + "placeholder": "value1", + "secret": false + }, + { + "type": "textInput", + "label": "Label2", + "configKey": "key2", + "regex": "^(.{1,100})$", + "placeholder": "value2", + "secret": false + } + ] + } + ] + }, + { + "groups": [ + { + "title": "Connection mode", + "note": [ + "Update how you want to route events from your source to destination. ", + { + "text": "Get help deciding", + "link": "https://www.rudderstack.com/docs/destinations/rudderstack-connection-modes/" + } + ], + "icon": "sliders", + "fields": [] + } + ] + } + ] + }, + { + "title": "Configuration settings", + "note": "Manage the settings for your destination", + "sections": [ + { + "title": "Destination settings", + "note": "Configure advanced destination-specific settings here", + "icon": "settings", + "groups": [ + { + "title": "Configure settings", + "fields": [ + { + "type": "checkbox", + "label": "Label3", + "note": "Any additional note for this field", + "configKey": "key3", + "default": true + }, + { + "type": "textInput", + "label": "Label4", + "configKey": "key4", + "regex": "^(.{0,100})$", + "placeholder": "value4", + "secret": false, + "preRequisites": { "fields": [{ "configKey": "key3", "value": true }] } + }, + { + "type": "singleSelect", + "configKey": "key5", + "label": "Label5", + "note": "Any additional note for this field", + "options": [ + { "label": "optionLabel1", "value": "optionKey1" }, + { "label": "optionLabel2", "value": "optionKey2" }, + { "label": "optionLabel3", "value": "optionKey3" } + ], + "default": "optionKey2" + }, + { + "type": "multiSelect", + "label": "Label6", + "configKey": "key6", + "note": "Any additional note for this field", + "options": [ + { "label": "optionLabel1", "value": "optionKey1" }, + { "label": "optionLabel2", "value": "optionKey2" } + ], + "default": ["optionKey1"] + }, + { + "type": "tagInput", + "label": "Label7", + "configKey": "key7", + "note": "Any additional note for this field", + "tagKey": "tagKey1", + "placeholder": "e.g: Credit card visit" + } + ] + } + ] + } + ] + } + ], + "sdkTemplate": { "title": "Web SDK settings", "note": "not visible in the ui", "fields": [] } + } +} diff --git a/test/test_configGenerator.py b/test/test_configGenerator.py new file mode 100644 index 000000000..1964e7f77 --- /dev/null +++ b/test/test_configGenerator.py @@ -0,0 +1,29 @@ +import sys +import os +import unittest +import json + +# Add the parent directory to the Python path +current_dir = os.path.dirname(os.path.abspath(__file__)) +parent_dir = os.path.dirname(current_dir) +sys.path.insert(0, parent_dir) + +from scripts.configGenerator import generateConfigs + +with open('test/configData/inputData.json', 'r') as file: + input_data = json.load(file) + +with open('test/configData/db-config.json', 'r') as file: + db_config = json.load(file) +with open('test/configData/ui-config.json', 'r') as file: + ui_config = json.load(file) + +class TestConfigGenerator(unittest.TestCase): + + def test_config_generator(self): + result = generateConfigs(input_data) + self.assertEqual(result['db_config'], json.dumps(db_config)) + self.assertEqual(result['ui_config'], json.dumps(ui_config)) + +if __name__ == '__main__': + unittest.main()