|
40 | 40 | """ |
41 | 41 |
|
42 | 42 | import operator |
| 43 | +import re |
43 | 44 | import uuid |
44 | 45 | import warnings |
45 | 46 | import zipfile |
@@ -2611,3 +2612,105 @@ def process_value(self, value: str) -> str: |
2611 | 2612 | # Read from local file |
2612 | 2613 | with open(value, encoding=self.encoding) as f: |
2613 | 2614 | return f.read() |
| 2615 | + |
| 2616 | + |
| 2617 | +class FixJsonSchemaOfParameterTypes(InstanceOperator): |
| 2618 | + main_field: str |
| 2619 | + |
| 2620 | + def prepare(self): |
| 2621 | + self.simple_mapping = { |
| 2622 | + "": "object", |
| 2623 | + "any": "object", |
| 2624 | + "Any": "object", |
| 2625 | + "Array": "array", |
| 2626 | + "ArrayList": "array", |
| 2627 | + "Bigint": "integer", |
| 2628 | + "bool": "boolean", |
| 2629 | + "Boolean": "boolean", |
| 2630 | + "byte": "integer", |
| 2631 | + "char": "string", |
| 2632 | + "dict": "object", |
| 2633 | + "Dict": "object", |
| 2634 | + "double": "number", |
| 2635 | + "float": "number", |
| 2636 | + "HashMap": "object", |
| 2637 | + "Hashtable": "object", |
| 2638 | + "int": "integer", |
| 2639 | + "list": "array", |
| 2640 | + "List": "array", |
| 2641 | + "long": "integer", |
| 2642 | + "Queue": "array", |
| 2643 | + "short": "integer", |
| 2644 | + "Stack": "array", |
| 2645 | + "tuple": "array", |
| 2646 | + "Set": "array", |
| 2647 | + "set": "array", |
| 2648 | + "str": "string", |
| 2649 | + "String": "string", |
| 2650 | + } |
| 2651 | + |
| 2652 | + def dict_type_of(self, type_str: str) -> dict: |
| 2653 | + return {"type": type_str} |
| 2654 | + |
| 2655 | + def recursive_trace_for_type_fields(self, containing_element): |
| 2656 | + if isinstance(containing_element, dict): |
| 2657 | + keys = list(containing_element.keys()) |
| 2658 | + for key in keys: |
| 2659 | + if key == "type" and isinstance(containing_element["type"], str): |
| 2660 | + jsonschema_dict = self.type_str_to_jsonschema_dict( |
| 2661 | + containing_element["type"] |
| 2662 | + ) |
| 2663 | + containing_element.pop("type") |
| 2664 | + containing_element.update(jsonschema_dict) |
| 2665 | + else: |
| 2666 | + self.recursive_trace_for_type_fields(containing_element[key]) |
| 2667 | + elif isinstance(containing_element, list): |
| 2668 | + for list_element in containing_element: |
| 2669 | + self.recursive_trace_for_type_fields(list_element) |
| 2670 | + |
| 2671 | + def type_str_to_jsonschema_dict(self, type_str: str) -> dict: |
| 2672 | + if type_str in self.simple_mapping: |
| 2673 | + return self.dict_type_of(self.simple_mapping[type_str]) |
| 2674 | + m = re.match(r"^(List|Tuple)\[(.*?)\]$", type_str) |
| 2675 | + if m: |
| 2676 | + basic_type = self.dict_type_of("array") |
| 2677 | + basic_type["items"] = self.type_str_to_jsonschema_dict( |
| 2678 | + m.group(2) if m.group(1) == "List" else m.group(2).split(",")[0].strip() |
| 2679 | + ) |
| 2680 | + return basic_type |
| 2681 | + |
| 2682 | + m = re.match(r"^(Union)\[(.*?)\]$", type_str) |
| 2683 | + if m: |
| 2684 | + args = m.group(2).split(",") |
| 2685 | + for i in range(len(args)): |
| 2686 | + args[i] = args[i].strip() |
| 2687 | + return {"anyOf": [self.type_str_to_jsonschema_dict(arg) for arg in args]} |
| 2688 | + if re.match(r"^(Callable)\[(.*?)\]$", type_str): |
| 2689 | + return self.dict_type_of("object") |
| 2690 | + if "," in type_str: |
| 2691 | + sub_types = type_str.split(",") |
| 2692 | + for i in range(len(sub_types)): |
| 2693 | + sub_types[i] = sub_types[i].strip() |
| 2694 | + assert len(sub_types) in [ |
| 2695 | + 2, |
| 2696 | + 3, |
| 2697 | + ], f"num of subtypes should be 2 or 3, got {type_str}" |
| 2698 | + basic_type = self.type_str_to_jsonschema_dict(sub_types[0]) |
| 2699 | + for sub_type in sub_types[1:]: |
| 2700 | + if sub_type.lower().startswith("default"): |
| 2701 | + basic_type["default"] = re.split(r"[= ]", sub_type, maxsplit=1)[1] |
| 2702 | + for sub_type in sub_types[1:]: |
| 2703 | + if sub_type.lower().startswith("optional"): |
| 2704 | + return {"anyOf": [basic_type, self.dict_type_of("null")]} |
| 2705 | + return basic_type |
| 2706 | + |
| 2707 | + return self.dict_type_of(type_str) # otherwise - return what arrived |
| 2708 | + |
| 2709 | + def process( |
| 2710 | + self, instance: Dict[str, Any], stream_name: Optional[str] = None |
| 2711 | + ) -> Dict[str, Any]: |
| 2712 | + assert ( |
| 2713 | + self.main_field in instance |
| 2714 | + ), f"field '{self.main_field}' must reside in instance in order to verify its jsonschema correctness. got {instance}" |
| 2715 | + self.recursive_trace_for_type_fields(instance[self.main_field]) |
| 2716 | + return instance |
0 commit comments