|
20 | 20 |
|
21 | 21 | from . import geojson, specs |
22 | 22 | from .clients.destinations import DEFAULT_DESTINATION_REF |
| 23 | +from .constants import _SUBSCRIPTION_TOOL_WEIGHT |
23 | 24 | from .exceptions import ClientError |
24 | 25 |
|
25 | 26 | NOTIFICATIONS_TOPICS = ('delivery.success', |
@@ -128,16 +129,17 @@ def build_request(name: str, |
128 | 129 | if tools or clip_to_source: |
129 | 130 | tool_list = [dict(tool) for tool in (tools or [])] |
130 | 131 |
|
131 | | - # If clip_to_source is True a clip configuration will be added |
132 | | - # to the list of requested tools unless an existing clip tool |
133 | | - # exists. In that case an exception is raised. |
| 132 | + # Validate that input tool_list is in correct order |
| 133 | + _validate_tool_order(tool_list) |
| 134 | + |
| 135 | + # If clip_to_source is True, insert clip at correct position |
134 | 136 | if clip_to_source: |
135 | 137 | if any(tool.get('type', None) == 'clip' for tool in tool_list): |
136 | 138 | raise ClientError( |
137 | 139 | "clip_to_source option conflicts with a configured clip tool." |
138 | 140 | ) |
139 | 141 | else: |
140 | | - tool_list.append({'type': 'clip', 'parameters': {}}) |
| 142 | + _insert_clip_tool(tool_list) |
141 | 143 |
|
142 | 144 | details['tools'] = tool_list |
143 | 145 |
|
@@ -837,3 +839,71 @@ def sentinel_hub(collection_id: Optional[str], |
837 | 839 | if create_configuration: |
838 | 840 | parameters['create_configuration'] = create_configuration |
839 | 841 | return _hosting("sentinel_hub", parameters) |
| 842 | + |
| 843 | + |
| 844 | +def _validate_tool_order(tool_list: List[dict]) -> None: |
| 845 | + """Validate that tools are ordered according to their processing weights. |
| 846 | +
|
| 847 | + Args: |
| 848 | + tool_list: List of tool configurations to validate. |
| 849 | +
|
| 850 | + Raises: |
| 851 | + ClientError: If tools are not in the correct order or if an invalid |
| 852 | + tool is encountered. |
| 853 | + """ |
| 854 | + for i in range(len(tool_list)): |
| 855 | + tool_type = tool_list[i].get('type') |
| 856 | + |
| 857 | + # Check if tool has a type field |
| 858 | + if tool_type is None: |
| 859 | + raise ClientError( |
| 860 | + f"Tool at position {i} is missing required 'type' field.") |
| 861 | + |
| 862 | + # Check if tool type is valid |
| 863 | + if tool_type not in _SUBSCRIPTION_TOOL_WEIGHT: |
| 864 | + raise ClientError( |
| 865 | + f"Invalid tool type '{tool_type}' at position {i}. " |
| 866 | + f"Valid types are: {', '.join(_SUBSCRIPTION_TOOL_WEIGHT.keys())}" |
| 867 | + ) |
| 868 | + |
| 869 | + # Validate ordering if not the first tool |
| 870 | + if i > 0: |
| 871 | + prev_type = tool_list[i - 1]['type'] |
| 872 | + curr_weight = _SUBSCRIPTION_TOOL_WEIGHT[tool_type] |
| 873 | + prev_weight = _SUBSCRIPTION_TOOL_WEIGHT[prev_type] |
| 874 | + |
| 875 | + if prev_weight > curr_weight: |
| 876 | + raise ClientError( |
| 877 | + f"Tools must be ordered according to their processing order. " |
| 878 | + f"Tool '{prev_type}' cannot come before tool '{tool_type}'." |
| 879 | + ) |
| 880 | + |
| 881 | + |
| 882 | +def _insert_clip_tool(tool_list: List[dict]) -> None: |
| 883 | + """Insert clip tool at the correct position in the tool list. |
| 884 | +
|
| 885 | + The clip tool is inserted based on its position relative to other tools in |
| 886 | + the _SUBSCRIPTION_TOOL_WEIGHT dictionary order (i.e. the order of the |
| 887 | + dictionary keys), not on the numeric weight values themselves. This means |
| 888 | + that the clip tool is placed before any tool whose key appears after |
| 889 | + "clip" in that dictionary. |
| 890 | +
|
| 891 | + Args: |
| 892 | + tool_list: List of tool configurations (modified in place). |
| 893 | + """ |
| 894 | + # Create a position mapping from the _SUBSCRIPTION_TOOL_WEIGHT dictionary |
| 895 | + tool_order = { |
| 896 | + name: idx |
| 897 | + for idx, name in enumerate(_SUBSCRIPTION_TOOL_WEIGHT.keys()) |
| 898 | + } |
| 899 | + clip_position = tool_order['clip'] |
| 900 | + insert_index = len(tool_list) # default to end |
| 901 | + |
| 902 | + for i, tool in enumerate(tool_list): |
| 903 | + tool_type = tool.get('type') |
| 904 | + if tool_type in tool_order: |
| 905 | + if tool_order[tool_type] > clip_position: |
| 906 | + insert_index = i |
| 907 | + break |
| 908 | + |
| 909 | + tool_list.insert(insert_index, {'type': 'clip', 'parameters': {}}) |
0 commit comments