Skip to content

Commit 8dc7f03

Browse files
authored
[SCHEMATIC-308] Add ability for JSON Schema type to be set by columnType attribute (#1625)
* add ability for JSON Schema type to bet set by columnType attribute * clean up docstrings * ran pre-commit
1 parent e8b3b61 commit 8dc7f03

File tree

2 files changed

+168
-6
lines changed

2 files changed

+168
-6
lines changed

schematic/schemas/create_json_schema.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import os
1313
from typing import Union, Any, Optional
1414
from dataclasses import dataclass, field, asdict
15+
import warnings
1516

1617
from schematic.schemas.data_model_graph import DataModelGraphExplorer
1718
from schematic.utils.schema_utils import get_json_schema_log_file_path
@@ -200,6 +201,9 @@ def __post_init__(self) -> None:
200201
self.description = self.dmge.get_node_comment(
201202
node_display_name=self.display_name
202203
)
204+
explicit_js_type = self.dmge.get_node_column_type(
205+
node_display_name=self.display_name
206+
)
203207

204208
(
205209
self.is_array,
@@ -208,11 +212,13 @@ def __post_init__(self) -> None:
208212
self.minimum,
209213
self.maximum,
210214
self.pattern,
211-
) = _get_validation_rule_based_fields(validation_rules)
215+
) = _get_validation_rule_based_fields(
216+
validation_rules, explicit_js_type, self.name
217+
)
212218

213219

214220
def _get_validation_rule_based_fields(
215-
validation_rules: list[str],
221+
validation_rules: list[str], explicit_js_type: Optional[JSONSchemaType], name: str
216222
) -> tuple[
217223
bool,
218224
Optional[JSONSchemaType],
@@ -234,6 +240,13 @@ def _get_validation_rule_based_fields(
234240
235241
Arguments:
236242
validation_rules: A list of input validation rules
243+
explicit_js_type: A JSONSchemaType if set explicitly in the data model, otherwise None
244+
name: The name of the node the validation rules belong to
245+
246+
Raises:
247+
ValueError: When an explicit JSON Schema type is given, but the implicit type is different
248+
Warning: When no explicit JSON Schema type is given,
249+
and an implicit type is derived from the validation rules
237250
238251
Returns:
239252
A tuple containing fields for a Node object:
@@ -262,7 +275,38 @@ def _get_validation_rule_based_fields(
262275

263276
js_is_array = ValidationRuleName.LIST in validation_rule_names
264277

265-
js_type = get_js_type_from_inputted_rules(validation_rules)
278+
# The explicit JSON Schema type is the one set in the data model
279+
# The implicit JSON Schema type is the one implied by the presence
280+
# of certain validation rules
281+
# Schematic will use the implicit type if the explicit type isn't specified for now,
282+
# but this behavior is deprecated and will be removed in the future by SCHEMATIC-326
283+
implicit_js_type = get_js_type_from_inputted_rules(validation_rules)
284+
# If there is an explicit type set...
285+
if explicit_js_type:
286+
# and the implicit type conflicts with the explicit type, then an exception is raised
287+
if explicit_js_type != implicit_js_type:
288+
msg = (
289+
f"Property: '{name}', has explicit type: '{explicit_js_type}' "
290+
f"that conflicts with the implicit type: '{implicit_js_type}' "
291+
f"derived from its validation rules: {validation_rules}"
292+
)
293+
raise ValueError(msg)
294+
# otherwise the explicit type is used
295+
js_type = explicit_js_type
296+
# If there is no explicit type...
297+
else:
298+
# and there is an implicit type...
299+
if implicit_js_type:
300+
# then the implicit type is used...
301+
js_type = implicit_js_type
302+
# and a warning is raised since this behavior is deprecated
303+
msg = (
304+
f"No explicit type set for property: '{name}', "
305+
"using validation rules to set the type. "
306+
"Using validation rules to set type is deprecated. "
307+
"You should set the columnType for this property in your data model."
308+
)
309+
warnings.warn(msg)
266310

267311
if ValidationRuleName.URL in validation_rule_names:
268312
js_format = JSONSchemaFormat.URI

tests/unit/test_create_json_schema.py

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ def test_node_init(
231231
],
232232
ids=["No rules", "String", "List", "ListString", "InRange", "Regex", "Date", "URL"],
233233
)
234-
def test_get_validation_rule_based_fields(
234+
def test_get_validation_rule_based_fields_no_explicit_type(
235235
validation_rules: list[str],
236236
expected_type: Optional[JSONSchemaType],
237237
expected_is_array: bool,
@@ -240,15 +240,104 @@ def test_get_validation_rule_based_fields(
240240
expected_pattern: Optional[str],
241241
expected_format: Optional[JSONSchemaFormat],
242242
) -> None:
243-
"""Tests for _get_validation_rule_based_fields"""
243+
"""
244+
Test for _get_validation_rule_based_fields
245+
Tests that output is expected based on the input validation rules
246+
"""
247+
(
248+
is_array,
249+
property_type,
250+
property_format,
251+
minimum,
252+
maximum,
253+
pattern,
254+
) = _get_validation_rule_based_fields(validation_rules, None, "name")
255+
assert property_type == expected_type
256+
assert property_format == expected_format
257+
assert is_array == expected_is_array
258+
assert minimum == expected_min
259+
assert maximum == expected_max
260+
assert pattern == expected_pattern
261+
262+
263+
@pytest.mark.parametrize(
264+
"validation_rules, explicit_type, expected_type, expected_is_array, expected_min, expected_max, expected_pattern, expected_format",
265+
[
266+
(
267+
["str"],
268+
JSONSchemaType.STRING,
269+
JSONSchemaType.STRING,
270+
False,
271+
None,
272+
None,
273+
None,
274+
None,
275+
),
276+
(
277+
["inRange 50 100"],
278+
JSONSchemaType.NUMBER,
279+
JSONSchemaType.NUMBER,
280+
False,
281+
50,
282+
100,
283+
None,
284+
None,
285+
),
286+
(
287+
["regex search [a-f]"],
288+
JSONSchemaType.STRING,
289+
JSONSchemaType.STRING,
290+
False,
291+
None,
292+
None,
293+
"[a-f]",
294+
None,
295+
),
296+
(
297+
["date"],
298+
JSONSchemaType.STRING,
299+
JSONSchemaType.STRING,
300+
False,
301+
None,
302+
None,
303+
None,
304+
JSONSchemaFormat.DATE,
305+
),
306+
(
307+
["url"],
308+
JSONSchemaType.STRING,
309+
JSONSchemaType.STRING,
310+
False,
311+
None,
312+
None,
313+
None,
314+
JSONSchemaFormat.URI,
315+
),
316+
],
317+
ids=["String", "InRange", "Regex", "Date", "URL"],
318+
)
319+
def test_get_validation_rule_based_fields_with_explicit_type(
320+
validation_rules: list[str],
321+
explicit_type: JSONSchemaType,
322+
expected_type: Optional[JSONSchemaType],
323+
expected_is_array: bool,
324+
expected_min: Optional[float],
325+
expected_max: Optional[float],
326+
expected_pattern: Optional[str],
327+
expected_format: Optional[JSONSchemaFormat],
328+
) -> None:
329+
"""
330+
Test for _get_validation_rule_based_fields
331+
Tests that output is expected based on the input validation rules, and explicit type
332+
"""
244333
(
245334
is_array,
246335
property_type,
247336
property_format,
248337
minimum,
249338
maximum,
250339
pattern,
251-
) = _get_validation_rule_based_fields(validation_rules)
340+
) = _get_validation_rule_based_fields(validation_rules, explicit_type, "name")
252341
assert property_type == expected_type
253342
assert property_format == expected_format
254343
assert is_array == expected_is_array
@@ -257,6 +346,35 @@ def test_get_validation_rule_based_fields(
257346
assert pattern == expected_pattern
258347

259348

349+
@pytest.mark.parametrize(
350+
"validation_rules, explicit_type",
351+
[
352+
(["str"], JSONSchemaType.INTEGER),
353+
(["inRange 50 100"], JSONSchemaType.STRING),
354+
(["regex search [a-f]"], JSONSchemaType.INTEGER),
355+
(["date"], JSONSchemaType.INTEGER),
356+
(["url"], JSONSchemaType.INTEGER),
357+
],
358+
ids=[
359+
"String rule, integer type",
360+
"InRange rule, string type",
361+
"Regex rule, integer type",
362+
"Date rule, integer type",
363+
"Url rule, integer type",
364+
],
365+
)
366+
def test_get_validation_rule_based_fields_with_exception(
367+
validation_rules: list[str],
368+
explicit_type: JSONSchemaType,
369+
) -> None:
370+
"""
371+
Test for _get_validation_rule_based_fields
372+
Tests that output is expected based on the input validation rules, and explicit type
373+
"""
374+
with pytest.raises(ValueError):
375+
_get_validation_rule_based_fields(validation_rules, explicit_type, "name")
376+
377+
260378
class TestGraphTraversalState:
261379
"""Tests for GraphTraversalState class"""
262380

0 commit comments

Comments
 (0)