diff --git a/src/json_parser.py b/src/json_parser.py new file mode 100644 index 0000000..eec02be --- /dev/null +++ b/src/json_parser.py @@ -0,0 +1,37 @@ +import json +from pathlib import Path +import click + +def _get_fields(json_data: dict) -> list[str]: + """ + Extract field names from a JSON data structure. + + Args: + json_data (dict): The JSON data from which to extract field names. + + Returns: + list[str]: A list of field names extracted from the JSON data. + """ + attributes = json_data["conceptCodes"][0]["attributes"] + namelist = [attribute["attributeName"] for attribute in attributes] + return namelist + +def parse_json(json_file: Path) -> list[str]: + """ + Parse a JSON file and extract field names. + + Args: + json_file (Path): The path to the JSON file to parse. + + Returns: + list[str]: A list of field names extracted from the JSON file. + + Raises: + click.FileError: If the file is not a valid JSON file. + """ + try: + with open(json_file, "r") as f: + json_data = json.load(f) + except json.JSONDecodeError as e: + raise click.FileError(json_file, "Not a valid JSON file.") + return _get_fields(json_data) \ No newline at end of file diff --git a/src/xml_parser.py b/src/xml_parser.py new file mode 100644 index 0000000..cba352b --- /dev/null +++ b/src/xml_parser.py @@ -0,0 +1,121 @@ +import lxml.etree as ET +from pathlib import Path +import click + +def write_xml_data(root: ET.Element, file_path: Path, pretty: bool = True) -> None: + if file_path.exists(): + file_path.rename(file_path.with_suffix(".bak")) + xml_str = ET.tostring(root, encoding="utf-8", xml_declaration=True, pretty_print=pretty) + if pretty: + # Parse the XML string to ensure proper indentation + parser = ET.XMLParser(remove_blank_text=True, strip_cdata=False) + root = ET.fromstring(xml_str, parser=parser) + xml_str = ET.tostring(root, encoding="utf-8", xml_declaration=True, pretty_print=pretty) + file_path.write_bytes(xml_str) + +def _sanitize_name(name: str) -> str: + """ + Sanitize a name by removing all non-letter characters except hyphens and Scandinavian letters. + """ + # Replace Scandinavian letters + name = name.replace('ä', 'a').replace('ö', 'o') + name = name.replace('Ä', 'A').replace('Ö', 'O') + # Remove all non-letter characters except hyphens + return ''.join(c for c in name if c.isalpha() or c == '-') + +def add_connection(root: ET.Element, name: str, attr_list: list[str], description: str = "Default description") -> ET.Element: + """ + Add a new connection parameter to the XML configuration. + + Args: + root (ET.Element): The root element of the XML configuration. + name (str): The name of the connection parameter. + description (str, optional): The description of the connection parameter. Defaults to "Default description". + + Returns: + ET.Element: The modified root element with the new connection parameter added. + """ + connection_parameters = root.find("ConnectionParameters") + if connection_parameters is None: + connection_parameters = ET.SubElement(root, "ConnectionParameters") + ET.SubElement(connection_parameters, "ConnectionParameter", Name=f"VarhaUrl{name}", Description=f"Codeserver url for {description}") + + initialization = root.find("Initialization") + if initialization is not None: + predefined_commands = initialization.find("PredefinedCommands") + if predefined_commands is None: + predefined_commands = ET.SubElement(initialization, "PredefinedCommands") + ET.SubElement(predefined_commands, "Command", Name=f"Get-AllCodeServerPages{name}", Description=f"Get all pages for {name}") + ET.SubElement(predefined_commands, "Command", Name=f"Get-CodeServerPage{name}", Description=f"Get a specific page for {name}") + + schema = root.find("Schema") + if schema is not None: + + new_class = ET.SubElement(schema, "Class", Name=f"CodeServerObject{name}") + properties = ET.SubElement(new_class, "Properties") + property = ET.SubElement(properties, "Property", Name="conceptCodeId", DataType="String", IsMultivalue="false", IsAutoFill="false", IsUniqueKey="true") + return_binding = ET.SubElement(property, "ReturnBinding") + ET.SubElement(return_binding, "Bind", CommandResultOf=f"Get-AllCodeServerPages{name}", Path="conceptCodeId") + ET.SubElement(return_binding, "Bind", CommandResultOf=f"Get-CodeServerPage{name}", Path="conceptCodeId") + mapping = ET.SubElement(property, "CommandMappings") + ET.SubElement(mapping, "Map", ToCommand=f"Get-CodeServerPages{name}", Parameter="conceptCodeId") + + for attr in attr_list: + property = ET.SubElement(properties, "Property", Name=_sanitize_name(attr), DataType="String", IsMultivalue="false", IsAutoFill="false") + return_binding = ET.SubElement(property, "ReturnBinding") + ET.SubElement(return_binding, "Bind", CommandResultOf=f"Get-AllCodeServerPages{name}", Path=_sanitize_name(attr)) + ET.SubElement(return_binding, "Bind", CommandResultOf=f"Get-CodeServerPage{name}", Path=_sanitize_name(attr)) + + read_configuration = ET.SubElement(new_class, "ReadConfiguration") + listing_command = ET.SubElement(read_configuration, "ListingCommand", Command=f"Get-AllCodeServerPages{name}") + ET.SubElement(listing_command, "SetParameter", Param="Resource", Source="ConnectionParameter", Value=f"VarhaUrl{name}") + ET.SubElement(listing_command, "SetParameter", Param="RootProperty", Source="FixedValue", Value="conceptCodes") + cmd_squence = ET.SubElement(read_configuration, "CommandSequence") + item = ET.SubElement(cmd_squence, "Item", Command=f"Get-CodeServerPage{name}", Order="1") + ET.SubElement(item, "SetParameter", Param="Resource", Source="ConnectionParameter", Value=f"VarhaUrl{name}") + ET.SubElement(item, "SetParameter", Param="RootProperty", Source="FixedValue", Value="conceptCodes") + return root + +def _generate_xml_configuration() -> ET.Element: + """ + Generate a default XML configuration for a PowerShell connector. + + Returns: + ET.Element: The root element of the XML configuration. + """ + root = ET.Element("PowershellConnectorDefinition", Id="CodeServerConnector", Version="0.1", Description="CodeServer REST api connector") + ET.SubElement(root, "PluginAssemblies") + ET.SubElement(root, "ConnectionParameters") + initialization = ET.SubElement(root, "Initialization") + ET.SubElement(initialization, "PredefinedCommands") + custom_commands = ET.SubElement(initialization, "CustomCommands") + cmd = ET.SubElement(custom_commands, "CustomCommand", Name="Initialize-CodeServer") + cmd.text = ET.CDATA(r"""param ( + ) + Import-Module D:\bin\VSHVA\PowerShell\CodeServerConnector.psm1""") + environment_initialization = ET.SubElement(initialization, "EnvironmentInitialization") + connect = ET.SubElement(environment_initialization, "Connect") + command_sequence = ET.SubElement(connect, "CommandSequence") + ET.SubElement(command_sequence, "Item", Command="Initialize-CodeServer", Order="1") + ET.SubElement(environment_initialization, "Disconnect") + ET.SubElement(root, "Schema") + return root + +def get_xml_data(xml_file: Path) -> ET.Element: + """ + Get XML data from a file. If the file does not exist, generate a default configuration. + + Args: + xml_file (Path): The path to the XML file. + + Returns: + ET.Element: The root element of the XML configuration. + """ + if not xml_file.exists(): + return _generate_xml_configuration() + try: + parser = ET.XMLParser(strip_cdata=False) + tree = ET.parse(xml_file, parser=parser) + return tree + except ET.XMLSyntaxError as e: + raise click.FileError(xml_file, f"Not a valid XML file. {e}") \ No newline at end of file