diff --git a/docs/schema/index.rst b/docs/schema/index.rst index 25314f241..f3bdffdd8 100644 --- a/docs/schema/index.rst +++ b/docs/schema/index.rst @@ -1035,6 +1035,7 @@ Array fields store lists of homogeneous typed values: - ``items``: Schema for all array elements (required). The dictionary can contain any of the basic type schemas outlined above. The ``items.type`` field is required. +- ``uniqueItems``: If set to ``true``, all elements in the array must be unique. - ``minItems`` / ``maxItems``: Array size constraints - ``contains`` Schema for some elements in the array. The dictionary can contain any of the basic type schemas outlined above. diff --git a/sphinx_needs/schema/config.py b/sphinx_needs/schema/config.py index 04e4578f6..8e14a0122 100644 --- a/sphinx_needs/schema/config.py +++ b/sphinx_needs/schema/config.py @@ -249,6 +249,8 @@ class ExtraOptionMultiValueSchemaType(TypedDict): """Minimum number of contains items in the array.""" maxContains: NotRequired[int] """Maximum number of contains items in the array.""" + uniqueItems: NotRequired[bool] + """Whether all items in the array must be unique.""" def validate_extra_option_multi_value_schema( diff --git a/sphinx_needs/schema/jsons/ExtraOptionMultiValueSchemaType.schema.json b/sphinx_needs/schema/jsons/ExtraOptionMultiValueSchemaType.schema.json index 36b237df4..68950f13c 100644 --- a/sphinx_needs/schema/jsons/ExtraOptionMultiValueSchemaType.schema.json +++ b/sphinx_needs/schema/jsons/ExtraOptionMultiValueSchemaType.schema.json @@ -224,6 +224,10 @@ "const": "array", "title": "Type", "type": "string" + }, + "uniqueItems": { + "title": "Uniqueitems", + "type": "boolean" } }, "required": [ diff --git a/sphinx_needs/schema/jsons/SchemasRootType.schema.json b/sphinx_needs/schema/jsons/SchemasRootType.schema.json index b26848046..b42287e86 100644 --- a/sphinx_needs/schema/jsons/SchemasRootType.schema.json +++ b/sphinx_needs/schema/jsons/SchemasRootType.schema.json @@ -212,6 +212,10 @@ "const": "array", "title": "Type", "type": "string" + }, + "uniqueItems": { + "title": "Uniqueitems", + "type": "boolean" } }, "required": [ diff --git a/tests/schema/__snapshots__/test_schema.ambr b/tests/schema/__snapshots__/test_schema.ambr index a7e1c2006..421f4711c 100644 --- a/tests/schema/__snapshots__/test_schema.ambr +++ b/tests/schema/__snapshots__/test_schema.ambr @@ -3115,6 +3115,40 @@ }), }) # --- +# name: test_schemas[schema/fixtures/extra_options-array_unique_items] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Field: array_field + Need path: IMPL_1 + Schema path: options > schema > properties > array_field > uniqueItems + Schema message: ["a","a"] has non-unique elements [sn_schema_violation.option_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/extra_options-array_unique_items].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'array_field', + 'need_path': 'IMPL_1', + 'schema_path': 'options > schema > properties > array_field > uniqueItems', + 'severity': 'violation', + 'validation_msg': '["a","a"] has non-unique elements', + }), + 'log_lvl': 'error', + 'subtype': 'option_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- # name: test_schemas[schema/fixtures/extra_options-auto_inject_type] '' # --- diff --git a/tests/schema/fixtures/extra_options.yml b/tests/schema/fixtures/extra_options.yml index b12cd0d44..b9a73a5ac 100644 --- a/tests/schema/fixtures/extra_options.yml +++ b/tests/schema/fixtures/extra_options.yml @@ -727,3 +727,18 @@ coerce_to_boolean_from_string_error: .. impl:: title :id: IMPL_1 :approved: not-a-boolean + +array_unique_items: + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + ubproject: | + [[needs.extra_options]] + name = "array_field" + schema.type = "array" + schema.items = {type = "string"} + schema.uniqueItems = true + rst: | + .. impl:: title + :id: IMPL_1 + :array_field: a, a