|
| 1 | +import json |
| 2 | +from pathlib import Path |
| 3 | +import requests |
| 4 | +import os |
| 5 | +import time |
| 6 | +from colorama import init, Fore, Style |
| 7 | +from prompt_toolkit import prompt |
| 8 | +from prompt_toolkit.completion import WordCompleter |
| 9 | + |
| 10 | +# Initialize colorama |
| 11 | +init(autoreset=True) |
| 12 | + |
| 13 | +# Load configuration |
| 14 | + |
| 15 | +script_dir = Path(__file__).parent.parent |
| 16 | + |
| 17 | +with open(f'{script_dir}/infra/integrations/integration_outputs.json', 'r') as f: |
| 18 | + config = json.load(f) |
| 19 | + |
| 20 | +# JupiterOne API details |
| 21 | +API_URL = 'https://api.us.jupiterone.io/graphql' |
| 22 | +HEADERS = { |
| 23 | + 'Content-Type': 'application/json', |
| 24 | + 'Authorization': f'Bearer {os.environ.get("JUPITERONE_API_KEY")}', |
| 25 | + 'JupiterOne-Account': os.environ.get("JUPITERONE_ACCOUNT") |
| 26 | +} |
| 27 | + |
| 28 | +def get_upload_url(integration_instance_id, filename, dataset_id): |
| 29 | + query = """ |
| 30 | + mutation integrationFileTransferUploadUrl( |
| 31 | + $integrationInstanceId: String! |
| 32 | + $filename: String! |
| 33 | + $datasetId: String! |
| 34 | + ) { |
| 35 | + integrationFileTransferUploadUrl( |
| 36 | + integrationInstanceId: $integrationInstanceId |
| 37 | + filename: $filename |
| 38 | + datasetId: $datasetId |
| 39 | + ) { |
| 40 | + uploadUrl |
| 41 | + expiresIn |
| 42 | + } |
| 43 | + } |
| 44 | + """ |
| 45 | + variables = { |
| 46 | + "integrationInstanceId": integration_instance_id, |
| 47 | + "filename": filename, |
| 48 | + "datasetId": dataset_id |
| 49 | + } |
| 50 | + response = requests.post(API_URL, json={"query": query, "variables": variables}, headers=HEADERS) |
| 51 | + return response.json()['data']['integrationFileTransferUploadUrl']['uploadUrl'] |
| 52 | + |
| 53 | +def upload_file(upload_url, file_path): |
| 54 | + with open(file_path, 'rb') as f: |
| 55 | + response = requests.put(upload_url, data=f, headers={'Content-Type': 'text/csv'}) |
| 56 | + return response.status_code |
| 57 | + |
| 58 | +def invoke_integration(integration_instance_id): |
| 59 | + query = """ |
| 60 | + mutation InvokeIntegrationInstance( |
| 61 | + $id: String! |
| 62 | + ) { |
| 63 | + invokeIntegrationInstance( |
| 64 | + id: $id |
| 65 | + ) { |
| 66 | + success |
| 67 | + integrationJobId |
| 68 | + } |
| 69 | + } |
| 70 | + """ |
| 71 | + variables = {"id": integration_instance_id} |
| 72 | + response = requests.post(API_URL, json={"query": query, "variables": variables}, headers=HEADERS) |
| 73 | + response_json = response.json() |
| 74 | + |
| 75 | + if 'errors' in response_json: |
| 76 | + error = response_json['errors'][0] |
| 77 | + if error.get('extensions', {}).get('code') == 'ALREADY_EXECUTING_ERROR': |
| 78 | + return 'ALREADY_RUNNING' |
| 79 | + else: |
| 80 | + print(f"GraphQL error: {error['message']}") |
| 81 | + return False |
| 82 | + elif 'data' in response_json and response_json['data'] is not None: |
| 83 | + if 'invokeIntegrationInstance' in response_json['data']: |
| 84 | + return response_json['data']['invokeIntegrationInstance']['success'] |
| 85 | + else: |
| 86 | + print(f"Unexpected response format: 'invokeIntegrationInstance' not found in data") |
| 87 | + return False |
| 88 | + else: |
| 89 | + print(f"Unexpected response format: {response_json}") |
| 90 | + return False |
| 91 | + |
| 92 | +def print_colored(message, color=Fore.WHITE, style=Style.NORMAL): |
| 93 | + print(f"{style}{color}{message}") |
| 94 | + |
| 95 | +def select_integrations(): |
| 96 | + integration_names = list(config.keys()) |
| 97 | + integration_names_lower = [name.lower() for name in integration_names] |
| 98 | + completer = WordCompleter(integration_names + ['all']) |
| 99 | + |
| 100 | + print_colored("Available integrations:", Fore.CYAN, Style.BRIGHT) |
| 101 | + for name in integration_names: |
| 102 | + print_colored(f" - {name}", Fore.CYAN) |
| 103 | + |
| 104 | + while True: |
| 105 | + selection = prompt( |
| 106 | + "Enter integration names to run (comma-separated) or 'all': ", |
| 107 | + completer=completer |
| 108 | + ).strip().lower() |
| 109 | + |
| 110 | + if selection == 'all': |
| 111 | + return integration_names |
| 112 | + |
| 113 | + selected = [name.strip() for name in selection.split(',')] |
| 114 | + valid_selections = [] |
| 115 | + invalid = [] |
| 116 | + |
| 117 | + for name in selected: |
| 118 | + if name in integration_names_lower: |
| 119 | + valid_selections.append(integration_names[integration_names_lower.index(name)]) |
| 120 | + else: |
| 121 | + invalid.append(name) |
| 122 | + |
| 123 | + if invalid: |
| 124 | + print_colored(f"Invalid integrations: {', '.join(invalid)}. Please try again.", Fore.RED) |
| 125 | + else: |
| 126 | + return valid_selections |
| 127 | + |
| 128 | +def main(): |
| 129 | + # Check if environment variables are set |
| 130 | + if not os.environ.get("JUPITERONE_API_KEY") or not os.environ.get("JUPITERONE_ACCOUNT"): |
| 131 | + print_colored("Error: JUPITERONE_API_KEY and JUPITERONE_ACCOUNT environment variables must be set.", Fore.RED, Style.BRIGHT) |
| 132 | + return |
| 133 | + |
| 134 | + selected_integrations = select_integrations() |
| 135 | + |
| 136 | + for integration_name in selected_integrations: |
| 137 | + integration_data = config[integration_name] |
| 138 | + integration_instance_id = integration_data['integrationInstanceId'] |
| 139 | + source_files = integration_data['sourceFiles'].split(',') |
| 140 | + dataset_ids = integration_data['dataSetIds'].split(',') |
| 141 | + |
| 142 | + print_colored(f"\nProcessing integration: {integration_name}", Fore.GREEN, Style.BRIGHT) |
| 143 | + |
| 144 | + for filename, dataset_id in zip(source_files, dataset_ids): |
| 145 | + print_colored(f" Uploading {filename} for dataset {dataset_id}", Fore.YELLOW) |
| 146 | + upload_url = get_upload_url(integration_instance_id, filename, dataset_id) |
| 147 | + |
| 148 | + file_path = os.path.join(script_dir, 'data', filename) |
| 149 | + |
| 150 | + status_code = upload_file(upload_url, file_path) |
| 151 | + if status_code == 200: |
| 152 | + print_colored(f" ✔ Successfully uploaded {filename}", Fore.GREEN) |
| 153 | + else: |
| 154 | + print_colored(f" ✘ Failed to upload {filename}. Status code: {status_code}", Fore.RED) |
| 155 | + |
| 156 | + print_colored(f" Invoking integration: {integration_name}", Fore.YELLOW) |
| 157 | + try: |
| 158 | + result = invoke_integration(integration_instance_id) |
| 159 | + if result == True: |
| 160 | + print_colored(f" ✔ Successfully invoked integration: {integration_name}", Fore.GREEN) |
| 161 | + elif result == 'ALREADY_RUNNING': |
| 162 | + print_colored(f" ⚠ Integration {integration_name} is already running. Skipping.", Fore.YELLOW) |
| 163 | + else: |
| 164 | + print_colored(f" ✘ Failed to invoke integration: {integration_name}", Fore.RED) |
| 165 | + except Exception as e: |
| 166 | + print_colored(f" ✘ Error invoking integration {integration_name}: {str(e)}", Fore.RED) |
| 167 | + |
| 168 | + print() # Empty line for readability |
| 169 | + time.sleep(5) # Wait for 5 seconds before the next integration |
| 170 | + |
| 171 | +if __name__ == "__main__": |
| 172 | + main() |
0 commit comments