commit ce9d626c0e1ce8ce00800a592d3a0dd16903d64e Author: Cristiano Hoshikawa Date: Sat Mar 15 21:27:57 2025 -0300 first commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..b96befe Binary files /dev/null and b/.DS_Store differ diff --git a/MIGRATE_TO_APIGW_01.00.0003.iar b/MIGRATE_TO_APIGW_01.00.0003.iar new file mode 100644 index 0000000..facfd68 Binary files /dev/null and b/MIGRATE_TO_APIGW_01.00.0003.iar differ diff --git a/MIGRATE_TO_APIGW_01.00.0007.iar b/MIGRATE_TO_APIGW_01.00.0007.iar new file mode 100644 index 0000000..600ef0e Binary files /dev/null and b/MIGRATE_TO_APIGW_01.00.0007.iar differ diff --git a/OIC_Version_Compare.iml b/OIC_Version_Compare.iml new file mode 100644 index 0000000..cbd2812 --- /dev/null +++ b/OIC_Version_Compare.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a8b2696 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Analyzing Differences in Oracle Integration Cloud Artifacts + +### Introduction +When developing integrations in Oracle Integration Cloud (OIC), artifacts are created with encapsulated metadata definitions. This script helps analyze differences between artifacts, making it easier to understand changes between versions. + +### Pre-Requisites + +Install these Python libraries: + + + pandas + tabulate + +### The code + +You can find the code here: [oic_python_compare.py](./source/oic_python_compare.py) + +You can execute the code changing the name of the artifacts: + +![img_2.png](images/img_2.png) + +You can change the code to accept parameters if you intend to use with any automation process. + +### How the Script Works +The script compares two `.iar` artifacts by extracting and analyzing their contents. The key functionalities include: + +1. **Creating Temporary Copies:** + - Copies the original artifacts into a temporary directory to avoid modifying the original files. + +2. **Extracting Files:** + - Converts `.iar` files into `.zip` format and extracts them into designated folders. + +3. **Renaming Versioned Folders:** + - Standardizes folder names by replacing version numbers with `_VERSION` to simplify comparisons. + +4. **Comparing Files:** + - Analyzes different file formats, including: + - **Text files**: Compares line-by-line differences. + - **Properties files**: Identifies key-value changes. + - **JSON files**: Recursively checks for modifications in structured data. + - **XML files**: Compares attributes, elements, and text content. + +5. **Comparing Directories:** + - Iterates through both artifact directories and reports missing or modified files. + +6. **Generating a Comparison Report:** + - Outputs a structured table with detected differences. + +7. **Cleaning Up Temporary Files:** + - Removes extracted and temporary files after execution. + +### Practical Use Case +Imagine two versions of an integration: +- `MIGRATE_TO_APIGW_01.00.0003.iar` +- `MIGRATE_TO_APIGW_01.00.0007.iar` + +By running the script, developers can identify exactly what changed between these versions, aiding in debugging and version control. + +## Disclaimer + +This code is not supported by Oracle and is provided solely as a snippet for the OIC community. It is intended for informational and educational purposes only. Use at your own discretion and risk. + +## Conclusion +This script provides an automated way to compare Oracle Integration artifacts, saving time and reducing errors when managing integration changes. By identifying modifications in metadata, configurations, and logic, teams can ensure seamless updates and deployments. + +![img.png](images/img.png) + +![img_1.png](images/img_1.png) + +## Acknowledgments + +- **Author** - Cristiano Hoshikawa (Oracle LAD A-Team Solution Engineer) diff --git a/images/img.png b/images/img.png new file mode 100644 index 0000000..52e2956 Binary files /dev/null and b/images/img.png differ diff --git a/images/img_1.png b/images/img_1.png new file mode 100644 index 0000000..e50789b Binary files /dev/null and b/images/img_1.png differ diff --git a/images/img_2.png b/images/img_2.png new file mode 100644 index 0000000..588c2fc Binary files /dev/null and b/images/img_2.png differ diff --git a/source/oic_python_compare.py b/source/oic_python_compare.py new file mode 100644 index 0000000..a059a6a --- /dev/null +++ b/source/oic_python_compare.py @@ -0,0 +1,310 @@ +import os +import zipfile +import shutil +import re +import tempfile +import json +import xml.etree.ElementTree as ET +import pandas as pd +from tabulate import tabulate + +# Set the path of the temporary directory +directory_temp = 'temp' + +def create_copy_temporary(file): + # Create a temporary file + fd, nome_file_temp = tempfile.mkstemp(dir=directory_temp, suffix='.zip') + os.close(fd) + + # Copy the contents of the original file to the temporary file + with open(file, 'rb') as f_orig, open(nome_file_temp, 'wb') as f_temp: + f_temp.write(f_orig.read()) + + return nome_file_temp + +def extract_file(file, directory_destino): + # Rename the file to .zip + nome_file_zip = file.replace('.iar', '.zip') + os.rename(file, nome_file_zip) + + # Extract the zip file to the destination directory + with zipfile.ZipFile(nome_file_zip, 'r') as zip_ref: + zip_ref.extractall(directory_destino) + + # Remove the temporary zip file + os.remove(nome_file_zip) + +def rename_folders_with_version(directory): + for root, dirs, files in os.walk(directory): + for dir in dirs: + if re.search(r'\d+\.\d+\.\d+', dir): + novo_nome = re.sub(r'\d+\.\d+\.\d+', '_VERSION', dir) + os.rename(os.path.join(root, dir), os.path.join(root, novo_nome)) + +def compare_files(file1, file2): + try: + with open(file1, 'r') as f1, open(file2, 'r') as f2: + lines1 = f1.readlines() + lines2 = f2.readlines() + + differences = [] + for i in range(max(len(lines1), len(lines2))): + if i >= len(lines1): + differences.append(f'+ {lines2[i].strip()}') + elif i >= len(lines2): + differences.append(f'- {lines1[i].strip()}') + elif lines1[i] != lines2[i]: + differences.append(f'- {lines1[i].strip()}') + differences.append(f'+ {lines2[i].strip()}') + + if differences: + return '\n'.join(differences) + else: + return None + except Exception as e: + print(f"Error in compare files: {e}") + return None + +def compare_properties(file1, file2): + try: + with open(file1, 'r') as f1, open(file2, 'r') as f2: + props1 = {} + props2 = {} + for line in f1.readlines(): + wkey, valor = line.strip().split('=') + props1[wkey] = valor + for line in f2.readlines(): + wkey, valor = line.strip().split('=') + props2[wkey] = valor + + differences = [] + for wkey in set(list(props1.keys()) + list(props2.keys())): + if wkey not in props1: + differences.append(f'+ {wkey}={props2[wkey]}') + elif wkey not in props2: + differences.append(f'- {wkey}={props1[wkey]}') + elif props1[wkey] != props2[wkey]: + differences.append(f'- {wkey}={props1[wkey]}') + differences.append(f'+ {wkey}={props2[wkey]}') + + if differences: + return '\n'.join(differences) + else: + return None + except Exception as e: + print(f"Error when comparing properties: {e}") + return None + +def compare_json(file1, file2): + try: + import json + with open(file1, 'r') as f1, open(file2, 'r') as f2: + json1 = json.load(f1) + json2 = json.load(f2) + + differences = [] + def compare_json_recursive(obj1, obj2, path=''): + if isinstance(obj1, dict) and isinstance(obj2, dict): + wkeys = set(list(obj1.keys()) + list(obj2.keys())) + for wkey in wkeys: + if wkey not in obj1: + differences.append(f'+ {path}{wkey}={obj2[wkey]}') + elif wkey not in obj2: + differences.append(f'- {path}{wkey}={obj1[wkey]}') + elif obj1[wkey] != obj2[wkey]: + differences.append(f'- {path}{wkey}={obj1[wkey]}') + differences.append(f'+ {path}{wkey}={obj2[wkey]}') + if isinstance(obj1[wkey], dict) and isinstance(obj2[wkey], dict): + compare_json_recursive(obj1[wkey], obj2[wkey], path+f'[{wkey}].') + elif isinstance(obj1, list) and isinstance(obj2, list): + for i in range(max(len(obj1), len(obj2))): + if i >= len(obj1): + differences.append(f'+ {path}[{i}]={obj2[i]}') + elif i >= len(obj2): + differences.append(f'- {path}[{i}]={obj1[i]}') + elif obj1[i] != obj2[i]: + differences.append(f'- {path}[{i}]={obj1[i]}') + differences.append(f'+ {path}[{i}]={obj2[i]}') + if isinstance(obj1[i], dict) and isinstance(obj2[i], dict): + compare_json_recursive(obj1[i], obj2[i], path+f'[{i}].') + compare_json_recursive(json1, json2) + if differences: + return '\n'.join(differences) + else: + return None + except Exception as e: + print(f"Error comparing JSON: {e}") + return None + +def compare_xml(file1, file2): + try: + import xml.etree.ElementTree as ET + tree1 = ET.parse(file1) + tree2 = ET.parse(file2) + + differences = [] + def compare_xml_recursive(elem1, elem2, path=''): + if elem1.tag != elem2.tag: + differences.append(f'- {path}{elem1.tag}') + differences.append(f'+ {path}{elem2.tag}') + elif elem1.attrib != elem2.attrib: + for wkey in set(list(elem1.attrib.keys()) + list(elem2.attrib.keys())): + if wkey not in elem1.attrib: + differences.append(f'+ {path}@{wkey}={elem2.attrib[wkey]}') + elif wkey not in elem2.attrib: + differences.append(f'- {path}@{wkey}={elem1.attrib[wkey]}') + elif elem1.attrib[wkey] != elem2.attrib[wkey]: + differences.append(f'- {path}@{wkey}={elem1.attrib[wkey]}') + differences.append(f'+ {path}@{wkey}={elem2.attrib[wkey]}') + if elem1.text != elem2.text: + differences.append(f'- {path}={elem1.text}') + differences.append(f'+ {path}={elem2.text}') + for child1, child2 in zip(elem1, elem2): + compare_xml_recursive(child1, child2, path+f'/{child1.tag}') + compare_xml_recursive(tree1.getroot(), tree2.getroot()) + if differences: + return '\n'.join(differences) + else: + return None + except Exception as e: + print(f"Error comparing XML: {e}") + return None + +def compare_directories(directory1, directory2): + print(f"Comparing {directory1} and {directory2}") + + result_lists = [] + files = [] + + # Get the list of files and folders in directory 1 + pasta1 = os.listdir(directory1) + + # Get the list of files and folders in directory 2 + pasta2 = os.listdir(directory2) + + # Compare files and folders + for item in pasta1: + if item not in pasta2: + result_lists.append(f"Item {item} not found in {directory2}") + files.append(directory1 + item) + else: + path_item1 = os.path.join(directory1, item) + path_item2 = os.path.join(directory2, item) + + if os.path.isfile(path_item1) and os.path.isfile(path_item2): + # Compare the files + if item.endswith('.properties'): + differences = compare_properties(path_item1, path_item2) + elif item.endswith('.json'): + differences = compare_json(path_item1, path_item2) + elif item.endswith('.xml'): + differences = compare_xml(path_item1, path_item2) + else: + differences = compare_files(path_item1, path_item2) + + if differences: + result_lists.append(f"Differences found in {path_item1} and {path_item2}:") + files.append(path_item1 + " - " + path_item2) + + x = differences.split('\n') + result_lists.extend(x) + for y in x: + files.append("") + + elif os.path.isdir(path_item1) and os.path.isdir(path_item2): + # Compare directories recursively + x, w = compare_directories(path_item1, path_item2) + result_lists.extend(x) + item_idx = 0 + for y in x: + files.append(w[item_idx]) + item_idx = item_idx + 1 + + # Create a table to store the result_lists + table_result_lists = { + "File/Folder": [], + "Differences": [] + } + + item_idx = 0 + # Add the result_lists to the table + for item in result_lists: + if item.startswith("Differences found"): + table_result_lists["File/Folder"].append(files[item_idx]) + table_result_lists["Differences"].append("\n".join(result_lists[result_lists.index(item)+1:result_lists.index(item)+10])) + else: + table_result_lists["File/Folder"].append(files[item_idx]) + table_result_lists["Differences"].append("") + item_idx = item_idx + 1 + + # Create a DataFrame from the table + df = pd.DataFrame(table_result_lists) + + # Save the result_list to a TXT file + with open('../result_list.txt', 'w') as f: + f.write(tabulate(df, headers="keys", tablefmt="psql")) + + return result_lists, files + +def main(): + # Defina os nomes dos artifacts + artifact1 = 'MIGRATE_TO_APIGW_01.00.0003.iar' + artifact2 = 'MIGRATE_TO_APIGW_01.00.0007.iar' + + # Create the temporary directory if it does not exist + if not os.path.exists(directory_temp): + os.makedirs(directory_temp) + + # Create temporary copies of artifacts + file_temp1 = create_copy_temporary(artifact1) + file_temp2 = create_copy_temporary(artifact2) + + # Create temporary directories for each artifact + directory_temp1 = os.path.join(directory_temp, artifact1.replace('.iar', '')) + directory_temp2 = os.path.join(directory_temp, artifact2.replace('.iar', '')) + + # Create temporary directories if they do not exist + if not os.path.exists(directory_temp1): + os.makedirs(directory_temp1) + if not os.path.exists(directory_temp2): + os.makedirs(directory_temp2) + + # Extract the temporary artifacts + extract_file(file_temp1, directory_temp1) + extract_file(file_temp2, directory_temp2) + + # Rename folders with version + rename_folders_with_version(directory_temp1) + rename_folders_with_version(directory_temp2) + + # Compare directories + compare_directories(directory_temp1, directory_temp2) + + # Remove temporary files + try: + os.remove(file_temp1) + except: + None + try: + os.remove(file_temp2) + except: + None + + # Remove temporary directories + try: + shutil.rmtree(directory_temp1) + except: + None + try: + shutil.rmtree(directory_temp2) + except: + None + + # Remove the temporary directory + try: + shutil.rmtree(directory_temp) + except: + None + +if __name__ == '__main__': + main() \ No newline at end of file