diff --git a/.mapping.json b/.mapping.json index dba3301f5..830093917 100644 --- a/.mapping.json +++ b/.mapping.json @@ -20584,6 +20584,7 @@ "test_data/integration_test_data/local_functions_number.json":"divkit/public/test_data/integration_test_data/local_functions_number.json", "test_data/integration_test_data/local_functions_string.json":"divkit/public/test_data/integration_test_data/local_functions_string.json", "test_data/integration_test_data/local_functions_url.json":"divkit/public/test_data/integration_test_data/local_functions_url.json", + "test_data/integration_test_data/set_dict_variable.json":"divkit/public/test_data/integration_test_data/set_dict_variable.json", "test_data/interactive_snapshot_test_data/div-action/array-variable-mutation.json":"divkit/public/test_data/interactive_snapshot_test_data/div-action/array-variable-mutation.json", "test_data/interactive_snapshot_test_data/div-action/base.json":"divkit/public/test_data/interactive_snapshot_test_data/div-action/base.json", "test_data/interactive_snapshot_test_data/div-action/focus-actions.json":"divkit/public/test_data/interactive_snapshot_test_data/div-action/focus-actions.json", diff --git a/api_generator/api_generator/generators/dart/dart_entities.py b/api_generator/api_generator/generators/dart/dart_entities.py index ba12b42bf..ca7810be8 100644 --- a/api_generator/api_generator/generators/dart/dart_entities.py +++ b/api_generator/api_generator/generators/dart/dart_entities.py @@ -20,6 +20,7 @@ from ...schema.modeling.entities import ( Color, String, Dictionary, + RawObject, RawArray, StringEnumeration, EntityEnumeration, @@ -46,6 +47,8 @@ def update_property_type_base(property_type: PropertyType): property_type.__class__ = DartColor elif isinstance(property_type, Dictionary): property_type.__class__ = DartDictionary + elif isinstance(property_type, RawObject): + property_type.__class__ = DartRawObject elif isinstance(property_type, RawArray): property_type.__class__ = DartRawArray elif isinstance(property_type, Double): @@ -208,7 +211,7 @@ class DartProperty(Property): return f"safeParseBool{expr}({src},{fallback})" elif isinstance(prop_type, (String, StaticString)): return f"safeParseStr{expr}({src},{fallback})" - elif isinstance(prop_type, Dictionary): + elif isinstance(prop_type, (Dictionary, RawObject)): return f"safeParseMap{expr}({src},{fallback})" elif isinstance(prop_type, RawArray): return f"safeParseList{expr}({src},{fallback})" @@ -233,7 +236,7 @@ class DartProperty(Property): strategy = "reqProp(safeParseBool(v),)" elif isinstance(list_item_type, (String, StaticString)): strategy = "reqProp(safeParseStr(v),)" - elif isinstance(list_item_type, Dictionary): + elif isinstance(list_item_type, (Dictionary, RawObject)): strategy = "reqProp(safeParseMap(v),)" elif isinstance(list_item_type, RawArray): strategy = "reqProp(safeParseList(v),)" @@ -333,7 +336,7 @@ class DartPropertyType(PropertyType): return 'String' elif isinstance(self, Color): return 'Color' - elif isinstance(self, Dictionary): + elif isinstance(self, (Dictionary, RawObject)): return 'Obj' elif isinstance(self, RawArray): return 'Arr' @@ -477,6 +480,10 @@ class DartDictionary(DartPropertyType, Dictionary): pass +class DartRawObject(DartPropertyType, RawObject): + pass + + class DartRawArray(DartPropertyType, RawArray): pass diff --git a/api_generator/api_generator/generators/divan/divan_entities.py b/api_generator/api_generator/generators/divan/divan_entities.py index 6877752cc..d0058f612 100644 --- a/api_generator/api_generator/generators/divan/divan_entities.py +++ b/api_generator/api_generator/generators/divan/divan_entities.py @@ -21,6 +21,7 @@ from ...schema.modeling.entities import ( Color, String, Dictionary, + RawObject, RawArray, DivanGeneratorProperties, ) @@ -123,6 +124,8 @@ def update_property_type_base(property_type: PropertyType): property_type.__class__ = DivanColor elif isinstance(property_type, Dictionary): property_type.__class__ = DivanDictionary + elif isinstance(property_type, RawObject): + property_type.__class__ = DivanRawObject elif isinstance(property_type, RawArray): property_type.__class__ = DivanRawArray elif isinstance(property_type, Double): @@ -628,7 +631,7 @@ class DivanPropertyType(PropertyType): return 'Color' elif isinstance(self, Url): return 'Url' - elif isinstance(self, Dictionary): + elif isinstance(self, (Dictionary, RawObject)): return 'Map' elif isinstance(self, RawArray): return 'List' @@ -656,7 +659,7 @@ class DivanPropertyType(PropertyType): def is_primitive(self) -> bool: if isinstance(self, (Int, Double, Bool, BoolInt, String, StaticString, Color, Url, RawArray)): return True - elif isinstance(self, (Dictionary, Array)): + elif isinstance(self, (Dictionary, RawObject, Array)): return False elif isinstance(self, Object): inner_obj = self.object @@ -690,6 +693,10 @@ class DivanDictionary(DivanPropertyType, Dictionary): pass +class DivanRawObject(DivanPropertyType, RawObject): + pass + + class DivanRawArray(DivanPropertyType, RawArray): pass diff --git a/api_generator/api_generator/generators/documentation/documentation_entities.py b/api_generator/api_generator/generators/documentation/documentation_entities.py index f7f855bdd..73e560f1c 100644 --- a/api_generator/api_generator/generators/documentation/documentation_entities.py +++ b/api_generator/api_generator/generators/documentation/documentation_entities.py @@ -14,6 +14,7 @@ from ...schema.modeling.entities import ( BoolInt, Color, Dictionary, + RawObject, RawArray, Double, Int, @@ -105,6 +106,8 @@ def update_property_type_base(property_type: PropertyType): property_type.__class__ = DocumentationColor elif isinstance(property_type, Dictionary): property_type.__class__ = DocumentationDictionary + elif isinstance(property_type, RawObject): + property_type.__class__ = DocumentationRawObject elif isinstance(property_type, RawArray): property_type.__class__ = DocumentationRawArray elif isinstance(property_type, Double): @@ -263,6 +266,16 @@ class DocumentationColor(Color, DocumentationPropertyType): class DocumentationDictionary(Dictionary, DocumentationPropertyType): + @property + def description(self) -> str: + return 'dictionary' + + def constraints(self, dictionary: Dict[str, str]) -> str: + return '' + + +class DocumentationRawObject(RawObject, DocumentationPropertyType): + @property def description(self) -> str: return 'object' @@ -271,7 +284,7 @@ class DocumentationDictionary(Dictionary, DocumentationPropertyType): return '' -class DocumentationRawArray(Dictionary, DocumentationPropertyType): +class DocumentationRawArray(RawArray, DocumentationPropertyType): @property def description(self) -> str: diff --git a/api_generator/api_generator/generators/kotlin/kotlin_entities.py b/api_generator/api_generator/generators/kotlin/kotlin_entities.py index d4c9d3e2f..6b20ff9ff 100644 --- a/api_generator/api_generator/generators/kotlin/kotlin_entities.py +++ b/api_generator/api_generator/generators/kotlin/kotlin_entities.py @@ -22,6 +22,7 @@ from ...schema.modeling.entities import ( Color, String, Dictionary, + RawObject, RawArray, ObjectFormat, KotlinGeneratorProperties @@ -73,6 +74,7 @@ class KotlinEntity(Entity): Color.__bases__ = (KotlinPropertyType, PropertyType,) String.__bases__ = (KotlinPropertyType, PropertyType,) Dictionary.__bases__ = (KotlinPropertyType, PropertyType,) + RawObject.__bases__ = (KotlinPropertyType, PropertyType,) RawArray.__bases__ = (KotlinPropertyType, PropertyType,) for prop in self.properties: prop.__class__ = KotlinProperty @@ -1280,7 +1282,7 @@ class KotlinPropertyType(PropertyType): args.append(f'{prop.declaration_name} = {declaration}') args = ', '.join(args) return wrap(f'{entity.resolved_prefixed_declaration}({args})') - elif isinstance(self, Dictionary): + elif isinstance(self, (Dictionary, RawObject)): return wrap(f'JSONObject("""\n{default_value}\n""")') else: return None @@ -1335,7 +1337,7 @@ class KotlinPropertyType(PropertyType): return 'Boolean' elif isinstance(self, String): return 'CharSequence' if self.formatted else 'String' - elif isinstance(self, Dictionary): + elif isinstance(self, (Dictionary, RawObject)): return 'JSONObject' elif isinstance(self, RawArray): return 'JSONArray' @@ -1513,6 +1515,8 @@ class KotlinPropertyType(PropertyType): return f'{prefix}INT' elif isinstance(self, Double): return f'{prefix}DOUBLE' + elif isinstance(self, Dictionary): + return f'{prefix}DICT' elif isinstance(self, RawArray): return f'{prefix}JSON_ARRAY' elif isinstance(self, Array): diff --git a/api_generator/api_generator/generators/kotlin_dsl/kotlin_dsl_entities.py b/api_generator/api_generator/generators/kotlin_dsl/kotlin_dsl_entities.py index 7cd18253c..985c3b175 100644 --- a/api_generator/api_generator/generators/kotlin_dsl/kotlin_dsl_entities.py +++ b/api_generator/api_generator/generators/kotlin_dsl/kotlin_dsl_entities.py @@ -18,6 +18,7 @@ from ...schema.modeling.entities import ( Color, String, Dictionary, + RawObject, RawArray ) from ... import utils @@ -47,6 +48,8 @@ def update_property_type_base(property_type: PropertyType): property_type.__class__ = KotlinDSLColor elif isinstance(property_type, Dictionary): property_type.__class__ = KotlinDSLDictionary + elif isinstance(property_type, RawObject): + property_type.__class__ = KotlinDSLRawObject elif isinstance(property_type, RawArray): property_type.__class__ = KotlinDSLRawArray elif isinstance(property_type, Double): @@ -274,7 +277,7 @@ class KotlinDSLPropertyType(PropertyType): return 'Color' elif isinstance(self, Url): return 'URI' - elif isinstance(self, Dictionary): + elif isinstance(self, (Dictionary, RawObject)): return 'Map' elif isinstance(self, RawArray): return 'List' @@ -322,6 +325,10 @@ class KotlinDSLDictionary(KotlinDSLPropertyType, Dictionary): pass +class KotlinDSLRawObject(KotlinDSLPropertyType, RawObject): + pass + + class KotlinDSLRawArray(KotlinDSLPropertyType, RawArray): pass diff --git a/api_generator/api_generator/generators/python/python_entities.py b/api_generator/api_generator/generators/python/python_entities.py index d8113485c..d959dc17a 100644 --- a/api_generator/api_generator/generators/python/python_entities.py +++ b/api_generator/api_generator/generators/python/python_entities.py @@ -11,6 +11,7 @@ from ...schema.modeling.entities import ( BoolInt, Color, Dictionary, + RawObject, RawArray, Double, Int, @@ -43,6 +44,8 @@ def update_property_type_base(property_type: PropertyType): property_type.__class__ = PythonColor elif isinstance(property_type, Dictionary): property_type.__class__ = PythonDictionary + elif isinstance(property_type, RawObject): + property_type.__class__ = PythonRawObject elif isinstance(property_type, RawArray): property_type.__class__ = PythonRawArray elif isinstance(property_type, Double): @@ -288,7 +291,7 @@ class PythonPropertyType(PropertyType): return 'str' elif isinstance(self, StaticString): raise TypeError(f'{self} is a static type') - elif isinstance(self, Dictionary): + elif isinstance(self, (Dictionary, RawObject)): return 'typing.Dict[str, typing.Any]' elif isinstance(self, RawArray): return 'typing.Sequence[typing.Any]' @@ -306,7 +309,7 @@ class PythonPropertyType(PropertyType): @property def constraints(self) -> str: - if isinstance(self, (Bool, BoolInt, Dictionary, RawArray)): + if isinstance(self, (Bool, BoolInt, Dictionary, RawObject, RawArray)): return '' elif isinstance(self, Array): result = '' @@ -358,6 +361,10 @@ class PythonDictionary(PythonPropertyType, Dictionary): pass +class PythonRawObject(PythonPropertyType, RawObject): + pass + + class PythonRawArray(PythonPropertyType, RawArray): pass diff --git a/api_generator/api_generator/generators/swift/generator.py b/api_generator/api_generator/generators/swift/generator.py index c10680964..43f4eeb28 100644 --- a/api_generator/api_generator/generators/swift/generator.py +++ b/api_generator/api_generator/generators/swift/generator.py @@ -15,6 +15,7 @@ from ... import utils from ...schema.modeling.entities import ( Declarable, Dictionary, + RawObject, EntityEnumeration, Entity, Property, @@ -290,7 +291,7 @@ class SwiftGenerator(Generator): properties_to_declare = entity.properties_to_declare_swift if properties_to_declare: for prop in properties_to_declare: - if isinstance(prop.property_type, Dictionary) or isinstance(prop.property_type, RawArray): + if isinstance(prop.property_type, (Dictionary, RawObject, RawArray)): sendable_conformance = '@unchecked Sendable' protocols = list(filter(None, [entity.protocol_plus_super_entities(), sendable_conformance])) conformance = f': {", ".join(protocols)}' diff --git a/api_generator/api_generator/generators/swift/swift_entities.py b/api_generator/api_generator/generators/swift/swift_entities.py index e8f0f05ee..0b491f678 100644 --- a/api_generator/api_generator/generators/swift/swift_entities.py +++ b/api_generator/api_generator/generators/swift/swift_entities.py @@ -20,6 +20,7 @@ from ...schema.modeling.entities import ( Color, String, Dictionary, + RawObject, RawArray, ObjectFormat, SwiftGeneratorProperties, @@ -84,6 +85,7 @@ class SwiftEntity(Entity): Color.__bases__ = (SwiftPropertyType, PropertyType,) String.__bases__ = (SwiftPropertyType, PropertyType,) Dictionary.__bases__ = (SwiftPropertyType, PropertyType,) + RawObject.__bases__ = (SwiftPropertyType, PropertyType,) RawArray.__bases__ = (SwiftPropertyType, PropertyType,) for prop in self.properties: prop.__class__ = SwiftProperty @@ -476,6 +478,8 @@ class SwiftProperty(Property): return 'resolveNumeric' elif isinstance(property_type, Object) and isinstance(property_type.object, StringEnumeration): return 'resolveEnum' + elif isinstance(property_type, Dictionary): + return 'resolveDict' elif isinstance(property_type, RawArray): return 'resolveArray' elif isinstance(property_type, Array): @@ -678,7 +682,7 @@ class SwiftPropertyType(PropertyType): return 'URL' elif isinstance(self, Color): return 'Color' - elif isinstance(self, Dictionary): + elif isinstance(self, (Dictionary, RawObject)): return '[String: Any]' elif isinstance(self, RawArray): return '[Any]' @@ -783,7 +787,7 @@ class SwiftPropertyType(PropertyType): args.append(f'{prop.declaration_name}: {default_value}') args = ', '.join(args) return f'{entity.declaration_prefix}{utils.capitalize_camel_case(entity.original_name)}({args})' - elif isinstance(self, Dictionary): + elif isinstance(self, (Dictionary, RawObject)): return f'(try! JSONSerialization.jsonObject(jsonString: """\n{default_value}\n""") as! [String: Any])' else: return None @@ -806,7 +810,7 @@ class SwiftPropertyType(PropertyType): @property def is_equatable(self) -> bool: - if isinstance(self, (Dictionary, RawArray)): + if isinstance(self, (Dictionary, RawObject, RawArray)): return False elif isinstance(self, Array): return cast(SwiftPropertyType, self.property_type).is_equatable @@ -814,9 +818,9 @@ class SwiftPropertyType(PropertyType): return True def serialization_suffix(self, use_expressions: bool) -> str: - if isinstance(self, (Dictionary)): + if isinstance(self, RawObject): return '' - elif isinstance(self, (String, Int, Double, Bool, BoolInt, RawArray)): + elif isinstance(self, (String, Int, Double, Bool, BoolInt, Dictionary, RawArray)): return '.toValidSerializationValue()' if use_expressions else '' elif isinstance(self, Object): if isinstance(self.object, StringEnumeration): diff --git a/api_generator/api_generator/generators/type_script/typescript_entities.py b/api_generator/api_generator/generators/type_script/typescript_entities.py index 0dbe0b43b..94d8ec9bc 100644 --- a/api_generator/api_generator/generators/type_script/typescript_entities.py +++ b/api_generator/api_generator/generators/type_script/typescript_entities.py @@ -16,6 +16,7 @@ from ...schema.modeling.entities import ( Color, String, Dictionary, + RawObject, RawArray, Declarable, TypeScriptGeneratorProperties @@ -67,7 +68,7 @@ def _type_script_type_name(property_type: PropertyType, supports_expressions: bo return 'string' elif isinstance(property_type, StaticString): raise TypeError('Is a static type') - elif isinstance(property_type, Dictionary): + elif isinstance(property_type, (Dictionary, RawObject)): return '{}' elif isinstance(property_type, RawArray): return 'unknown[]' diff --git a/api_generator/api_generator/schema/modeling/builders.py b/api_generator/api_generator/schema/modeling/builders.py index e186621c1..91b322bda 100644 --- a/api_generator/api_generator/schema/modeling/builders.py +++ b/api_generator/api_generator/schema/modeling/builders.py @@ -30,6 +30,7 @@ from .entities import ( Color, String, Dictionary, + RawObject, BoolInt, RawArray, _build_documentation_generator_properties @@ -276,9 +277,11 @@ def type_property_build(dictionary: Dict[str, any], mode=mode, config=config) return Array(property_type=property_type, min_items=min_items), declarations + elif type_value == 'dict': + return Dictionary(), [] elif type_value == 'object': if dictionary.get('additionalProperties', False) and 'properties' not in dictionary: - return Dictionary(), [] + return RawObject(), [] entity: Entity = Entity(name=name, original_name=outer_name, dictionary=dictionary, diff --git a/api_generator/api_generator/schema/modeling/entities.py b/api_generator/api_generator/schema/modeling/entities.py index 0c7975452..07e31f56f 100644 --- a/api_generator/api_generator/schema/modeling/entities.py +++ b/api_generator/api_generator/schema/modeling/entities.py @@ -797,9 +797,9 @@ def default_value(lang: GeneratedLanguage, class PropertyType(ABC): @property def supports_expressions(self) -> bool: - if isinstance(self, (Int, Double, Bool, BoolInt, String, Color, Url, RawArray)): + if isinstance(self, (Int, Double, Bool, BoolInt, String, Color, Url, RawArray, Dictionary)): return True - elif isinstance(self, (Dictionary, StaticString)): + elif isinstance(self, (RawObject, StaticString)): return False elif isinstance(self, Array): if isinstance(self.property_type, Object) and \ @@ -837,7 +837,7 @@ class PropertyType(ABC): @property def as_json(self) -> Dict: - if isinstance(self, (Int, Double, Bool, BoolInt, String, StaticString, Color, Url, Dictionary)): + if isinstance(self, (Int, Double, Bool, BoolInt, String, StaticString, Color, Url, Dictionary, RawObject)): return { 'value': str(type(self).__name__) } @@ -907,6 +907,11 @@ class Dictionary(PropertyType): pass +@dataclass +class RawObject(PropertyType): + pass + + @dataclass class RawArray(PropertyType): min_items: int diff --git a/client/android/div-data/src/main/java/com/yandex/div/internal/parser/TypeHelpers.kt b/client/android/div-data/src/main/java/com/yandex/div/internal/parser/TypeHelpers.kt index 89d070b46..e6cee3b41 100644 --- a/client/android/div-data/src/main/java/com/yandex/div/internal/parser/TypeHelpers.kt +++ b/client/android/div-data/src/main/java/com/yandex/div/internal/parser/TypeHelpers.kt @@ -3,6 +3,7 @@ package com.yandex.div.internal.parser import android.graphics.Color import android.net.Uri import org.json.JSONArray +import org.json.JSONObject interface TypeHelper { @@ -63,3 +64,9 @@ val TYPE_HELPER_JSON_ARRAY = object : TypeHelper { override val typeDefault = JSONArray() override fun isTypeValid(value: Any) = value is JSONArray } + +@JvmField +val TYPE_HELPER_DICT = object : TypeHelper { + override val typeDefault = JSONObject() + override fun isTypeValid(value: Any) = value is JSONObject +} diff --git a/client/android/div/src/main/java/com/yandex/div/core/actions/DivActionTypedSetStoredValueHandler.kt b/client/android/div/src/main/java/com/yandex/div/core/actions/DivActionTypedSetStoredValueHandler.kt index fb432645b..951fb9c80 100644 --- a/client/android/div/src/main/java/com/yandex/div/core/actions/DivActionTypedSetStoredValueHandler.kt +++ b/client/android/div/src/main/java/com/yandex/div/core/actions/DivActionTypedSetStoredValueHandler.kt @@ -52,7 +52,7 @@ internal class DivActionTypedSetStoredValueHandler @Inject constructor() : DivAc is DivTypedValue.Color -> StoredValue.ColorStoredValue(name, Color(value.value.value.evaluate(resolver))) is DivTypedValue.Url -> StoredValue.UrlStoredValue(name, Url.from(value.value.value.evaluate(resolver).toString())) is DivTypedValue.Array -> StoredValue.ArrayStoredValue(name, value.value.value.evaluate(resolver)) - is DivTypedValue.Dict -> StoredValue.DictStoredValue(name, value.value.value) + is DivTypedValue.Dict -> StoredValue.DictStoredValue(name, value.value.value.evaluate(resolver)) } } diff --git a/client/android/div/src/main/java/com/yandex/div/core/actions/DivActionTypedUtils.kt b/client/android/div/src/main/java/com/yandex/div/core/actions/DivActionTypedUtils.kt index 61333bdf1..9c974f7ac 100644 --- a/client/android/div/src/main/java/com/yandex/div/core/actions/DivActionTypedUtils.kt +++ b/client/android/div/src/main/java/com/yandex/div/core/actions/DivActionTypedUtils.kt @@ -31,7 +31,7 @@ internal fun DivTypedValue.evaluate(expressionResolver: ExpressionResolver): Any is DivTypedValue.Number -> value.value.evaluate(expressionResolver) is DivTypedValue.Url -> value.value.evaluate(expressionResolver) is DivTypedValue.Array -> value.value.evaluate(expressionResolver) - is DivTypedValue.Dict -> value.value + is DivTypedValue.Dict -> value.value.evaluate(expressionResolver) } return newValue } @@ -46,7 +46,7 @@ internal fun DivTypedValue.longValue(expressionResolver: ExpressionResolver): Lo internal fun DivTypedValue.doubleValue(expressionResolver: ExpressionResolver): Double? { return when (this) { is DivTypedValue.Integer -> value.value.evaluate(expressionResolver).toDouble() - is DivTypedValue.Number -> value.value.evaluate(expressionResolver).toDouble() + is DivTypedValue.Number -> value.value.evaluate(expressionResolver) else -> null } } diff --git a/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCaseUtils.kt b/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCaseUtils.kt index c273ba132..2bf88f87a 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCaseUtils.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/expression/ExpressionTestCaseUtils.kt @@ -33,8 +33,8 @@ object ExpressionTestCaseUtils { private const val VALUE_TYPE_DATE_TIME = "datetime" private const val VALUE_TYPE_URL = "url" private const val VALUE_TYPE_COLOR = "color" - private const val VALUE_TYPE_DICT = "dict" - private const val VALUE_TYPE_ARRAY = "array" + const val VALUE_TYPE_DICT = "dict" + const val VALUE_TYPE_ARRAY = "array" private const val VALUE_TYPE_UNIT = "unit" private const val VALUE_TYPE_ERROR = "error" private const val VALUE_TYPE_VARIABLE = "variable" diff --git a/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationMultiplatformTest.kt b/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationMultiplatformTest.kt index 9f7b0a10f..fbd60b280 100644 --- a/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationMultiplatformTest.kt +++ b/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationMultiplatformTest.kt @@ -6,6 +6,8 @@ import com.yandex.div.DivDataTag import com.yandex.div.core.Div2Context import com.yandex.div.core.DivConfiguration import com.yandex.div.core.expression.ExpressionTestCaseUtils +import com.yandex.div.core.expression.ExpressionTestCaseUtils.VALUE_TYPE_ARRAY +import com.yandex.div.core.expression.ExpressionTestCaseUtils.VALUE_TYPE_DICT import com.yandex.div.core.expression.ExpressionTestCaseUtils.createVariable import com.yandex.div.core.images.DivImageDownloadCallback import com.yandex.div.core.images.DivImageLoader @@ -53,8 +55,15 @@ class IntegrationMultiplatformTest(testCase: TestCaseOrError - Assert.assertEquals(it.value, variableController.get(it.name)?.getValue()) + is IntegrationTestCase.ExpectedResult.Variable -> { + val expectedValue = it.value + val actualValue = variableController.get(it.name)?.getValue() + if (it.type == VALUE_TYPE_DICT || it.type == VALUE_TYPE_ARRAY) { + Assert.assertEquals(expectedValue.toString(), actualValue.toString()) + } else { + Assert.assertEquals(expectedValue, actualValue) + } + } } } } diff --git a/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationTestCase.kt b/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationTestCase.kt index f0a4970d4..80ca73012 100644 --- a/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationTestCase.kt +++ b/client/android/div/src/test/java/com/yandex/div/interactive/IntegrationTestCase.kt @@ -1,6 +1,7 @@ package com.yandex.div.interactive import com.yandex.div.core.expression.ExpressionTestCaseUtils.getVariableValue +import com.yandex.div.core.expression.ExpressionTestCaseUtils.type import com.yandex.div2.DivAction import com.yandex.div2.DivData import org.json.JSONObject @@ -16,7 +17,7 @@ class IntegrationTestCase( sealed interface ExpectedResult { class Variable(val name: String, json: JSONObject): ExpectedResult { - val type: String = json.getString("type") + val type: String = json.type val value = json.getVariableValue(type) } diff --git a/client/ios/DivKit/Expressions/Serialization/Expression+ValidSerializationValue.swift b/client/ios/DivKit/Expressions/Serialization/Expression+ValidSerializationValue.swift index f1d107ef2..bad7a8f8e 100644 --- a/client/ios/DivKit/Expressions/Serialization/Expression+ValidSerializationValue.swift +++ b/client/ios/DivKit/Expressions/Serialization/Expression+ValidSerializationValue.swift @@ -89,3 +89,14 @@ extension Expression where T: RawRepresentable, T.RawValue == String { } } } + +extension Expression where T == [String: Any] { + func toValidSerializationValue() -> ValidSerializationValue { + switch self { + case let .value(value): + value + case let .link(link): + link.rawValue + } + } +} diff --git a/client/ios/DivKit/Extensions/DivTypedValueExtensions.swift b/client/ios/DivKit/Extensions/DivTypedValueExtensions.swift index 19b9831e5..d44f62151 100644 --- a/client/ios/DivKit/Extensions/DivTypedValueExtensions.swift +++ b/client/ios/DivKit/Extensions/DivTypedValueExtensions.swift @@ -22,8 +22,9 @@ extension DivTypedValue { } return nil case let .dictValue(value): - if let dictValue = DivDictionary.fromAny(value.value) { - return .dict(dictValue) + if let dictValue = value.resolveValue(expressionResolver), + let divDict = DivDictionary.fromAny(dictValue) { + return .dict(divDict) } return nil case let .integerValue(value): @@ -69,7 +70,10 @@ extension DivTypedValue { } return nil case let .dictValue(value): - return DivDictionary.fromAny(value.value) + if let dictValue = value.resolveValue(expressionResolver) { + return DivDictionary.fromAny(dictValue) + } + return nil case let .integerValue(value): if let integerValue = value.resolveValue(expressionResolver) { return integerValue diff --git a/client/ios/DivKit/generated_sources/DictValue.swift b/client/ios/DivKit/generated_sources/DictValue.swift index f79ed7e58..3fd508d6b 100644 --- a/client/ios/DivKit/generated_sources/DictValue.swift +++ b/client/ios/DivKit/generated_sources/DictValue.swift @@ -6,10 +6,14 @@ import VGSL public final class DictValue: @unchecked Sendable { public static let type: String = "dict" - public let value: [String: Any] + public let value: Expression<[String: Any]> + + public func resolveValue(_ resolver: ExpressionResolver) -> [String: Any]? { + resolver.resolveDict(value) + } init( - value: [String: Any] + value: Expression<[String: Any]> ) { self.value = value } @@ -28,7 +32,7 @@ extension DictValue: Serializable { public func toDictionary() -> [String: ValidSerializationValue] { var result: [String: ValidSerializationValue] = [:] result["type"] = Self.type - result["value"] = value + result["value"] = value.toValidSerializationValue() return result } } diff --git a/client/ios/DivKit/generated_sources/DictValueTemplate.swift b/client/ios/DivKit/generated_sources/DictValueTemplate.swift index e1d543d72..e5239c485 100644 --- a/client/ios/DivKit/generated_sources/DictValueTemplate.swift +++ b/client/ios/DivKit/generated_sources/DictValueTemplate.swift @@ -7,18 +7,18 @@ import VGSL public final class DictValueTemplate: TemplateValue, @unchecked Sendable { public static let type: String = "dict" public let parent: String? - public let value: Field<[String: Any]>? + public let value: Field>? public convenience init(dictionary: [String: Any], templateToType: [TemplateName: String]) throws { self.init( parent: dictionary["type"] as? String, - value: dictionary.getOptionalField("value") + value: dictionary.getOptionalExpressionField("value") ) } init( parent: String?, - value: Field<[String: Any]>? = nil + value: Field>? = nil ) { self.parent = parent self.value = value @@ -47,7 +47,7 @@ public final class DictValueTemplate: TemplateValue, @unchecked Sendable { if useOnlyLinks { return resolveOnlyLinks(context: context, parent: parent) } - var valueValue: DeserializationResult<[String: Any]> = { parent?.value?.value() ?? .noValue }() + var valueValue: DeserializationResult> = { parent?.value?.value() ?? .noValue }() _ = { // Each field is parsed in its own lambda to keep the stack size managable // Otherwise the compiler will allocate stack for each intermediate variable diff --git a/client/ios/DivKitTests/Actions/DivActionHandlerTests.swift b/client/ios/DivKitTests/Actions/DivActionHandlerTests.swift index 8d30bc08d..95e5ed77b 100644 --- a/client/ios/DivKitTests/Actions/DivActionHandlerTests.swift +++ b/client/ios/DivKitTests/Actions/DivActionHandlerTests.swift @@ -247,7 +247,7 @@ final class DivActionHandlerTests: XCTestCase { handle(.divActionArraySetValue( DivActionArraySetValue( index: .value(1), - value: .dictValue(DictValue(value: ["key1": "value", "key2": 123.45])), + value: dictValue(["key1": "value", "key2": 123.45]), variableName: .value("array_var") ) )) @@ -306,7 +306,7 @@ final class DivActionHandlerTests: XCTestCase { handle(.divActionDictSetValue( DivActionDictSetValue( key: .value("key"), - value: .dictValue(DictValue(value: ["new_key": "new value"])), + value: dictValue(["new_key": "new value"]), variableName: .value("dict_var") ) )) @@ -325,7 +325,7 @@ final class DivActionHandlerTests: XCTestCase { divAction( typed: .divActionDictSetValue(DivActionDictSetValue( key: .value("key"), - value: .dictValue(DictValue(value: ["new_key": "new value"])), + value: dictValue(["new_key": "new value"]), variableName: .value("dict_var") )) ), @@ -507,7 +507,7 @@ final class DivActionHandlerTests: XCTestCase { let value: [String: Any] = ["key1": "value", "key2": 123.45, "nested": ["key": "value"]] handle(.divActionSetVariable( DivActionSetVariable( - value: .dictValue(DictValue(value: value)), + value: dictValue(value), variableName: .value("dict_var") ) )) @@ -621,6 +621,10 @@ private func stringValue(_ value: String) -> DivTypedValue { .stringValue(StringValue(value: .value(value))) } +private func dictValue(_ value: [String: Any]) -> DivTypedValue { + .dictValue(DictValue(value: .value(value))) +} + private let cardId: DivCardID = "test_card" private final class MockReporter: DivReporter { diff --git a/client/ios/DivKitTests/Actions/SetStoredValueActionHandlerTests.swift b/client/ios/DivKitTests/Actions/SetStoredValueActionHandlerTests.swift index a35bdedc4..9791b01d7 100644 --- a/client/ios/DivKitTests/Actions/SetStoredValueActionHandlerTests.swift +++ b/client/ios/DivKitTests/Actions/SetStoredValueActionHandlerTests.swift @@ -123,7 +123,7 @@ final class SetStoredValueActionHandlerTests: XCTestCase { handle( action( name: "name", - value: .dictValue(DictValue(value: ["key": "value"])) + value: .dictValue(DictValue(value: .value(["key": "value"]))) ) ) diff --git a/schema/div-typed-value.json b/schema/div-typed-value.json index b4544d3d5..78df2a25d 100644 --- a/schema/div-typed-value.json +++ b/schema/div-typed-value.json @@ -120,8 +120,7 @@ ] }, "value": { - "type": "object", - "additionalProperties": true + "type": "dict" } }, "required": [ diff --git a/schema/div-variable.json b/schema/div-variable.json index c14767684..89efe617c 100644 --- a/schema/div-variable.json +++ b/schema/div-variable.json @@ -176,8 +176,7 @@ }, "value": { "supports_expressions": false, - "type": "object", - "additionalProperties": true, + "type": "dict", "$description": "translations.json#/div_variable_value" } }, diff --git a/test_data/integration_test_data/set_dict_variable.json b/test_data/integration_test_data/set_dict_variable.json new file mode 100644 index 000000000..c24e041c5 --- /dev/null +++ b/test_data/integration_test_data/set_dict_variable.json @@ -0,0 +1,90 @@ +{ + "description": "Set dict variable from variable.", + "div_data": { + "card": { + "log_id": "dict_variable", + "variables": [ + { + "name": "dict_var", + "type": "dict", + "value": { + "boolean": true, + "integer": 1, + "number": 1.0, + "string": "value" + } + } + ], + "states": [ + { + "state_id": 0, + "div": { + "type": "text", + "text": "text" + } + } + ] + } + }, + "cases": [ + { + "div_actions": [ + { + "log_id": "check usual action", + "url": "div-action://set_variable?name=result&value=@{dict_var}" + } + ], + "expected": [ + { + "type": "variable", + "variable_name": "result", + "value": { + "type": "dict", + "value": { + "boolean": true, + "integer": 1, + "number": 1.0, + "string": "value" + } + } + } + ], + "platforms": [ + "android" + ] + }, + { + "div_actions": [ + { + "log_id": "check typed action", + "typed": { + "type": "set_variable", + "variable_name": "result", + "value": { + "type": "dict", + "value": "@{dict_var}" + } + } + } + ], + "expected": [ + { + "type": "variable", + "variable_name": "result", + "value": { + "type": "dict", + "value": { + "boolean": true, + "integer": 1, + "number": 1.0, + "string": "value" + } + } + } + ], + "platforms": [ + "android" + ] + } + ] +}