|
6 | 6 | from rest_framework import serializers |
7 | 7 |
|
8 | 8 | from ..components import api as components_api |
| 9 | +from ..components.models import ComponentType |
9 | 10 |
|
10 | 11 |
|
11 | 12 | class LearningPackageSerializer(serializers.Serializer): # pylint: disable=abstract-method |
@@ -59,52 +60,55 @@ class EntityVersionSerializer(serializers.Serializer): # pylint: disable=abstra |
59 | 60 | class ComponentSerializer(EntitySerializer): # pylint: disable=abstract-method |
60 | 61 | """ |
61 | 62 | Serializer for components. |
62 | | -
|
63 | | - Extracts component_type and component_code from the [entity.component] |
64 | | - section if present (archives created in Verawood or later). Falls back to |
65 | | - parsing the entity key for archives created in Ulmo. |
| 63 | + Contains logic to convert entity_key to component_type and component_code. |
66 | 64 | """ |
67 | 65 |
|
68 | | - component = serializers.DictField(required=False) |
69 | | - |
70 | 66 | def validate(self, attrs): |
71 | 67 | """ |
72 | | - Custom validation logic: resolve component_type and component_code. |
73 | | -
|
74 | | - Archives created in Verawood or later supply an [entity.component] |
75 | | - section with ``component_type`` (e.g. "xblock.v1:problem") and |
76 | | - ``component_code`` (e.g. "my_example"). Archives created in Ulmo only |
77 | | - have the entity ``key`` in the format |
78 | | - ``"{namespace}:{type_name}:{component_code}"``, so we fall back to |
79 | | - parsing that for backwards compatibility. |
| 68 | + Custom validation logic: |
| 69 | + parse the entity_key into (component_type, component_code). |
80 | 70 | """ |
81 | | - component_section = attrs.pop("component", None) |
82 | | - if component_section: |
83 | | - # Verawood+ format: component_type and component_code are explicit. |
84 | | - component_type_str = component_section.get("component_type", "") |
85 | | - component_code = component_section.get("component_code", "") |
86 | | - try: |
87 | | - namespace, type_name = component_type_str.split(":", 1) |
88 | | - except ValueError as exc: |
89 | | - raise serializers.ValidationError( |
90 | | - {"component": f"Invalid component_type format: {component_type_str!r}. " |
91 | | - "Expected '{namespace}:{type_name}'."} |
92 | | - ) from exc |
93 | | - component_type_obj = components_api.get_or_create_component_type(namespace, type_name) |
94 | | - else: |
95 | | - # Ulmo (legacy) format: parse the entity key. |
96 | | - entity_key = attrs["key"] |
97 | | - try: |
98 | | - component_type_obj, component_code = ( |
99 | | - components_api.get_or_create_component_type_by_entity_key(entity_key) |
100 | | - ) |
101 | | - except ValueError as exc: |
102 | | - raise serializers.ValidationError({"key": str(exc)}) |
103 | | - attrs["component_type"] = component_type_obj |
104 | | - attrs["component_code"] = component_code |
| 71 | + entity_key = attrs["key"] |
| 72 | + try: |
| 73 | + component_type_obj, component_code = _get_or_create_component_type_by_entity_key(entity_key) |
| 74 | + attrs["component_type"] = component_type_obj |
| 75 | + attrs["component_code"] = component_code |
| 76 | + except ValueError as exc: |
| 77 | + raise serializers.ValidationError({"key": str(exc)}) |
105 | 78 | return attrs |
106 | 79 |
|
107 | 80 |
|
| 81 | +def _get_or_create_component_type_by_entity_key(entity_key: str) -> tuple[ComponentType, str]: |
| 82 | + """ |
| 83 | + Get or create a ComponentType based on a full [entity].key string. |
| 84 | +
|
| 85 | + The entity key is expected to be in the format |
| 86 | + ``"{namespace}:{type_name}:{component_code}"``. This function will parse out |
| 87 | + the ``namespace`` and ``type_name`` parts and use those to get or create the |
| 88 | + ComponentType. |
| 89 | +
|
| 90 | + Raises ValueError if the entity_key is not in the expected format. |
| 91 | +
|
| 92 | + Historical note: In Ulmo, this function was part of the public API. This was |
| 93 | + inappropriate because the exact format of entity_keys is just a convention |
| 94 | + rather than something API callers should count on. That said, it is safe to |
| 95 | + assume that in all "v1" archives, the components' entity keys are safe to |
| 96 | + parse into (namespace, type, code). So, we have moved this parsing logic |
| 97 | + from the public API to just this internal halper function. Future devs, |
| 98 | + please to not make new external guarantees about the format of entity keys |
| 99 | + (aka entity_refs). A future "v2" backup-restore format will drop this |
| 100 | + assumption of parse-ability.. |
| 101 | + """ |
| 102 | + try: |
| 103 | + namespace, type_name, component_code = entity_key.split(':', 2) |
| 104 | + except ValueError as exc: |
| 105 | + raise ValueError( |
| 106 | + f"Invalid entity_key format: {entity_key!r}. " |
| 107 | + "Expected format: '{namespace}:{type_name}:{component_code}'" |
| 108 | + ) from exc |
| 109 | + return components_api.get_or_create_component_type(namespace, type_name), component_code |
| 110 | + |
| 111 | + |
108 | 112 | class ComponentVersionSerializer(EntityVersionSerializer): # pylint: disable=abstract-method |
109 | 113 | """ |
110 | 114 | Serializer for component versions. |
|
0 commit comments