From 6acb6516f1b59064763020c3484856e9718ff114 Mon Sep 17 00:00:00 2001 From: Ace Winters Date: Thu, 1 Feb 2018 13:23:46 -0600 Subject: [PATCH] Adding JSON Builder Playbook App. --- apps/TCPB_-_JSON_Builder/.gitignore | 47 ++++ apps/TCPB_-_JSON_Builder/README.md | 35 +++ apps/TCPB_-_JSON_Builder/__main__.py | 73 ++++++ .../TCPB_-_JSON_Builder/docs/screenshot_1.jpg | Bin 0 -> 24254 bytes apps/TCPB_-_JSON_Builder/install.json | 31 +++ apps/TCPB_-_JSON_Builder/json_builder.py | 49 ++++ apps/TCPB_-_JSON_Builder/requirements.txt | 1 + apps/TCPB_-_JSON_Builder/setup.cfg | 7 + .../tcex.d/data/json_builder.json | 29 +++ .../tcex.d/profiles/json_builder.json | 50 ++++ apps/TCPB_-_JSON_Builder/tcex.json | 15 ++ .../TCPB_-_JSON_Builder/tcex_json_schema.json | 240 ++++++++++++++++++ 12 files changed, 577 insertions(+) create mode 100644 apps/TCPB_-_JSON_Builder/.gitignore create mode 100644 apps/TCPB_-_JSON_Builder/README.md create mode 100644 apps/TCPB_-_JSON_Builder/__main__.py create mode 100644 apps/TCPB_-_JSON_Builder/docs/screenshot_1.jpg create mode 100644 apps/TCPB_-_JSON_Builder/install.json create mode 100644 apps/TCPB_-_JSON_Builder/json_builder.py create mode 100644 apps/TCPB_-_JSON_Builder/requirements.txt create mode 100644 apps/TCPB_-_JSON_Builder/setup.cfg create mode 100644 apps/TCPB_-_JSON_Builder/tcex.d/data/json_builder.json create mode 100644 apps/TCPB_-_JSON_Builder/tcex.d/profiles/json_builder.json create mode 100644 apps/TCPB_-_JSON_Builder/tcex.json create mode 100644 apps/TCPB_-_JSON_Builder/tcex_json_schema.json diff --git a/apps/TCPB_-_JSON_Builder/.gitignore b/apps/TCPB_-_JSON_Builder/.gitignore new file mode 100644 index 0000000..f917d54 --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/.gitignore @@ -0,0 +1,47 @@ +#================================================= +# Python App Template +# Version: 1.0.0 +# Last Updated: 2017/10/04 +#================================================= + +#------------------------------------------------- +# Language Exclusions +#------------------------------------------------- +__pycache__ +*.pyc + +#------------------------------------------------- +# App Specific Exclusions +#------------------------------------------------- +*.log +log +sample-*.json + +#------------------------------------------------- +# Build Exclusions +#------------------------------------------------- +build +lib_* +target +temp + +#------------------------------------------------- +# IDE Exclusions +#------------------------------------------------- +.c9 +.idea +.project +.python-version +.settings +.vscode +nbproject + +#------------------------------------------------- +# Other Nonsense +#------------------------------------------------- +bcs* + +#************************************************* +# Custom Exclusion (per App exclusions) +#************************************************* +sample.json \ No newline at end of file diff --git a/apps/TCPB_-_JSON_Builder/README.md b/apps/TCPB_-_JSON_Builder/README.md new file mode 100644 index 0000000..f8f9ace --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/README.md @@ -0,0 +1,35 @@ +# Release Notes +## 1.0.0 +* Initial Release + +--- + +# Summary +This playbook App will take a JSON formatted String with embedded variables and output a JSON String. + +> Note: String value must be wrapped in double quotes. All other values should **not** have double quotes. + +# Dependencies +* tcex>=0.7,<0.8 + +# Input Definitions +* JSON Data - The JSON string to resolve embedded variables. + +# Output Definitions +* json.data - The JSON String with all embedded variables resolved. + +# Building + +``` +pip install tcex +tclib +tcpackage +``` + +# Local Testing + +All the environment variables in `tcex.d/profiles/json_builder.json` file must be set on the local system. + +``` +tcrun --group qa-build +``` \ No newline at end of file diff --git a/apps/TCPB_-_JSON_Builder/__main__.py b/apps/TCPB_-_JSON_Builder/__main__.py new file mode 100644 index 0000000..07dc156 --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/__main__.py @@ -0,0 +1,73 @@ +"""Set lib directory for current version of Python""" +import os +import subprocess +import sys + +__version__ = '1.0.1' + + +def main(): + """Main""" + lib_directory = None + + # All Python Version that will be searched + lib_major_version = 'lib_{}'.format(sys.version_info.major) + lib_minor_version = '{}.{}'.format(lib_major_version, sys.version_info.minor) + lib_micro_version = '{}.{}'.format(lib_minor_version, sys.version_info.micro) + + # Get all "lib" directories + app_path = os.getcwd() + contents = os.listdir(app_path) + lib_directories = [] + for c in contents: + # ensure content starts with lib, is directory, and is readable + if c.startswith('lib') and os.path.isdir(c) and (os.access(c, os.R_OK)): + lib_directories.append(c) + # reverse sort directories + lib_directories.sort(reverse=True) + + # Find most appropriate FULL version + lib_directory = None + if lib_micro_version in lib_directories: + lib_directory = lib_micro_version + elif lib_minor_version in lib_directories: + lib_directory = lib_minor_version + elif lib_major_version in lib_directories: + lib_directory = lib_major_version + else: + for lv in [lib_micro_version, lib_minor_version, lib_major_version]: + for ld in lib_directories: + if lv in ld: + lib_directory = ld + break + else: + continue + break + + # No reason to continue if no valid lib directory found + if lib_directory is None: + print('Failed to find lib directory ({}).'.format(lib_directories)) + sys.exit(1) + + # Use this if you want to include modules from a subfolder + # lib_path = os.path.realpath( + # os.path.abspath( + # os.path.join( + # os.path.split(inspect.getfile(inspect.currentframe()))[0], lib_directory))) + lib_path = os.path.join(app_path, lib_directory) + if 'PYTHONPATH' in os.environ: + os.environ['PYTHONPATH'] = '{}{}{}'.format(lib_path, os.pathsep, os.environ['PYTHONPATH']) + else: + os.environ['PYTHONPATH'] = '{}'.format(lib_path) + + # Update system arguments + sys.argv[0] = sys.executable + sys.argv[1] = '{}.py'.format(sys.argv[1]) + + # Make sure to exit with the return value from the subprocess call + ret = subprocess.call(sys.argv) + sys.exit(ret) + + +if __name__ == '__main__': + main() diff --git a/apps/TCPB_-_JSON_Builder/docs/screenshot_1.jpg b/apps/TCPB_-_JSON_Builder/docs/screenshot_1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..02c217c1596ccb81f36f2345a8508beec830b074 GIT binary patch literal 24254 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 literal 0 HcmV?d00001 diff --git a/apps/TCPB_-_JSON_Builder/install.json b/apps/TCPB_-_JSON_Builder/install.json new file mode 100644 index 0000000..9b63a09 --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/install.json @@ -0,0 +1,31 @@ +{ + "allowOnDemand": true, + "displayName": "JSON Builder", + "languageVersion": "3.6", + "listDelimiter": "|", + "note": "Take data of multiple types and output a JSON String.", + "params": [{ + "label": "JSON Data", + "name": "json_data", + "note": "The JSON data including variables types. String variables must be manually wrapped in double quotes. All other Types should not have double quotes. Binary/BinaryArray values are not supported.", + "playbookDataType": [ + "Any" + ], + "required": true, + "sequence": 1, + "type": "String", + "validValues": ["${TEXT}"], + "viewRows": 20 + }], + "playbook": { + "outputVariables": [{ + "name": "json.data", + "type": "String" + }], + "type": "Utility" + }, + "programLanguage": "PYTHON", + "programMain": "json_builder", + "programVersion": "1.0.0", + "runtimeLevel": "Playbook" +} \ No newline at end of file diff --git a/apps/TCPB_-_JSON_Builder/json_builder.py b/apps/TCPB_-_JSON_Builder/json_builder.py new file mode 100644 index 0000000..e409bfe --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/json_builder.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" JSON Pretty Playbook App """ +import codecs +import json +import traceback +import sys + +from tcex import TcEx + +# Python 2 unicode +if sys.version_info[0] == 2: + reload(sys) + sys.setdefaultencoding('utf-8') + +tcex = TcEx() + +# App args +tcex.parser.add_argument('--json_data', required=True) +args = tcex.args + + +def main(): + """Main App Logic""" + json_data = tcex.playbook.read(args.json_data) + + try: + json.loads(json_data) + except Exception as e: + err = 'JSON data was not properly formatted ({}).'.format(e) + tcex.log.error(err) + tcex.message_tc(err) + tcex.playbook.exit(1) + + # create output + tcex.log.info('JSON data: {}'.format(json_data)) + tcex.playbook.create_output('json.data', json_data) + + tcex.message_tc('JSON data has been created.') + tcex.exit() + + +if __name__ == '__main__': + try: + main() + except Exception as e: + main_err = 'Generic Error. See logs for more details ({}).'.format(e) + tcex.log.error(traceback.format_exc()) + tcex.message_tc(main_err) + tcex.playbook.exit(1) diff --git a/apps/TCPB_-_JSON_Builder/requirements.txt b/apps/TCPB_-_JSON_Builder/requirements.txt new file mode 100644 index 0000000..0341530 --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/requirements.txt @@ -0,0 +1 @@ +tcex>=0.7,<0.8 diff --git a/apps/TCPB_-_JSON_Builder/setup.cfg b/apps/TCPB_-_JSON_Builder/setup.cfg new file mode 100644 index 0000000..abca849 --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/setup.cfg @@ -0,0 +1,7 @@ +[flake8] +max-line-length = 100 +ignore = E402 + +[pep8] +max-line-length = 100 +ignore = E402 \ No newline at end of file diff --git a/apps/TCPB_-_JSON_Builder/tcex.d/data/json_builder.json b/apps/TCPB_-_JSON_Builder/tcex.d/data/json_builder.json new file mode 100644 index 0000000..bbfe9c7 --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/tcex.d/data/json_builder.json @@ -0,0 +1,29 @@ +[{ + "data": "\"one\"", + "variable": "#App:0001:name!String" + }, + { + "data": ["#App:0001:name!String", "two", "three"], + "variable": "#App:0001:counts!StringArray" + }, + { + "data": { + "key": "numbers", + "value": "#App:0001:counts!StringArray" + }, + "variable": "#App:0002:numbers!KeyValue" + }, + { + "data": [{ + "key": "alias", + "value": "ace" + }, { + "key": "alias", + "value": "#App:0001:name!String" + }, { + "key": "counts", + "value": "#App:0001:counts!StringArray" + }], + "variable": "#App:0002:aliases!KeyValueArray" + } +] \ No newline at end of file diff --git a/apps/TCPB_-_JSON_Builder/tcex.d/profiles/json_builder.json b/apps/TCPB_-_JSON_Builder/tcex.d/profiles/json_builder.json new file mode 100644 index 0000000..806b7fe --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/tcex.d/profiles/json_builder.json @@ -0,0 +1,50 @@ +[ + { + "args": { + "api_default_org": "$env.API_DEFAULT_ORG", + "api_access_id": "$env.API_ACCESS_ID", + "api_secret_key": "$envs.API_SECRET_KEY", + "tc_api_path": "$env.TC_API_PATH", + "tc_log_level": "debug", + "tc_log_path": "log", + "tc_log_to_api": false, + "tc_out_path": "log", + "tc_proxy_external": false, + "tc_proxy_tc": false, + "tc_temp_path": "log", + "tc_playbook_db_type": "Redis", + "tc_playbook_db_context": "686be118-ff9a-4d05-bb14-4c1c6a0c9b87", + "tc_playbook_db_path": "$env.DB_PATH", + "tc_playbook_db_port": "$env.DB_PORT", + "tc_playbook_out_variables": "#App:3681:json.data!String", + "json_data": "{\"name\": \"#App:0001:name!String\", \"counts\": #App:0001:counts!StringArray, \"numbers\": #App:0002:numbers!KeyValue, \"aliases\": #App:0002:aliases!KeyValueArray}" + }, + "data_files": [ + "tcex.d/data/json_builder.json" + ], + "description": "Pass test of JSON Builder playbook App.", + "exit_codes": [ + 0 + ], + "groups": [ + "local" + ], + "install_json": "install.json", + "profile_name": "json-builder", + "quiet": false, + "validations": [ + { + "data": null, + "data_type": "redis", + "operator": "ne", + "variable": "#App:3681:json.data!String" + }, + { + "data": "string", + "data_type": "redis", + "operator": "it", + "variable": "#App:3681:json.data!String" + } + ] + } +] \ No newline at end of file diff --git a/apps/TCPB_-_JSON_Builder/tcex.json b/apps/TCPB_-_JSON_Builder/tcex.json new file mode 100644 index 0000000..add7811 --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/tcex.json @@ -0,0 +1,15 @@ +{ + "package": { + "app_name": "TCPB_-_JSON_Create", + "bundle": false, + "excludes": [ + "requirements.txt", + "tcex.d" + ], + "outdir": "target" + }, + "profiles": [], + "profile_include_dirs": [ + "tcex.d/profiles" + ] +} \ No newline at end of file diff --git a/apps/TCPB_-_JSON_Builder/tcex_json_schema.json b/apps/TCPB_-_JSON_Builder/tcex_json_schema.json new file mode 100644 index 0000000..1464161 --- /dev/null +++ b/apps/TCPB_-_JSON_Builder/tcex_json_schema.json @@ -0,0 +1,240 @@ +{ + "additionalProperties": false, + "properties": { + "allowOnDemand": { + "type": "boolean" + }, + "allowRunAsUser": { + "type": "boolean" + }, + "apiUserTokenParam": { + "type": "boolean" + }, + "displayName": { + "type": "string" + }, + "feeds": { + "items": { + "additionalProperties": false, + "properties": { + "attributesFile": { + "type": "string" + }, + "enableBulkJson": { + "type": "boolean" + }, + "documentStorageLimitMb": { + "type": "integer" + }, + "indicatorLimit": { + "type": "integer" + }, + "jobFile": { + "type": "string" + }, + "sourceCategory": { + "type": "string" + }, + "sourceDescription": { + "type": "string" + }, + "sourceName": { + "type": "string" + } + } + } + }, + "languageVersion": { + "type": "string" + }, + "listDelimiter": { + "type": "string" + }, + "minServerVersion": { + "type": "string" + }, + "note": { + "type": "string" + }, + "params": { + "items": { + "additionalProperties": false, + "properties": { + "allowMultiple": { + "type": "boolean" + }, + "default": { + "type": [ + "boolean", + "integer", + "string" + ] + }, + "encrypt": { + "type": "boolean" + }, + "exposePlaybookKeyAs": { + "items": { + "enum": [ + "Binary", + "BinaryArray", + "KeyValue", + "KeyValueArray", + "String", + "StringArray", + "TCEntity", + "TCEntityArray" + ] + } + }, + "hidden": { + "type": "boolean" + }, + "label": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "type": "string" + }, + "playbookDataType": { + "items": { + "enum": [ + "Any", + "Binary", + "BinaryArray", + "KeyValue", + "KeyValueArray", + "String", + "StringArray", + "TCEntity", + "TCEntityArray" + ] + }, + "type": "array" + }, + "required": { + "type": "boolean" + }, + "sequence": { + "type": "integer" + }, + "type": { + "enum": [ + "Boolean", + "Choice", + "KeyValueList", + "MultiChoice", + "String", + "StringMixed" + ], + "type": "string" + }, + "validValues": { + "type": "array" + }, + "viewRows": { + "type": "integer" + } + }, + "required": [ + "label", + "name", + "type" + ], + "type": "object" + }, + "type": "array", + "uniqueItems": true + }, + "playbook": { + "properties": { + "outputVariables": { + "items": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "type": { + "enum": [ + "Binary", + "BinaryArray", + "KeyValue", + "KeyValueArray", + "String", + "StringArray", + "TCEntity", + "TCEntityArray" + ], + "type": "string" + } + }, + "required": [ + "name", + "type" + ], + "type": "object" + }, + "type": "array", + "uniqueItems": false + }, + "retry": { + "additionalProperties": false, + "properties": { + "allowed": { + "type": "boolean" + }, + "defaultDelayMinutes": { + "type": "integer" + }, + "defaultMaxRetries": { + "type": "integer" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "programIcon": { + "type": "string" + }, + "programLanguage": { + "type": "string" + }, + "programMain": { + "type": "string" + }, + "programVersion": { + "type": "string" + }, + "publishOutFiles": { + "type": "array" + }, + "repeatingMinutes": { + "type": "array" + }, + "runtimeContext": { + "type": "array" + }, + "runtimeLevel": { + "type": [ + "array", + "string" + ] + } + }, + "required": [ + "allowOnDemand", + "displayName", + "params", + "programLanguage", + "programMain", + "programVersion", + "runtimeLevel" + ], + "type": "object" +} \ No newline at end of file