@@ -5053,7 +5053,7 @@ def _update_nodes_to_process(self, nodes: list[str]) -> None:
50535053
50545054
50555055@dataclass
5056- class JSONSchema : # pylint: disable=too-many-instance-attributes
5056+ class JSONSchema :
50575057 """
50585058 A dataclass representing a JSON Schema.
50595059 Each attribute represents a keyword in a JSON Schema.
@@ -5066,7 +5066,9 @@ class JSONSchema: # pylint: disable=too-many-instance-attributes
50665066 description: An optional description of the object described by this schema.
50675067 properties: A list of property schemas.
50685068 required: A list of properties required by the schema.
5069- all_of: A list of conditions the schema must meet. This should be removed if empty.
5069+ _conditional_dependencies: A mapping of conditional dependencies to be added to the "allOf" keyword in JSON Schema.
5070+ The key is a tuple of (watched_property, enum_value)
5071+ The value is a list of properties that become required when watched_property has the value enum_value.
50705072 """
50715073
50725074 schema_id : str = ""
@@ -5076,7 +5078,9 @@ class JSONSchema: # pylint: disable=too-many-instance-attributes
50765078 description : str = "TBD"
50775079 properties : dict [str , Property ] = field (default_factory = dict )
50785080 required : list [str ] = field (default_factory = list )
5079- all_of : list [AllOf ] = field (default_factory = list )
5081+ _conditional_dependencies : dict [tuple [str , str ], list [str ]] = field (
5082+ default_factory = dict
5083+ )
50805084
50815085 def as_json_schema_dict (
50825086 self ,
@@ -5088,15 +5092,25 @@ def as_json_schema_dict(
50885092 The dataclass as a dict.
50895093 """
50905094 json_schema_dict = asdict (self )
5095+ # Change dataclass attribute names to JSON Schema keywords
5096+ # Dataclass attributes can't start with $
50915097 keywords_to_change = {
50925098 "schema_id" : "$id" ,
50935099 "schema" : "$schema" ,
5094- "all_of" : "allOf" ,
50955100 }
50965101 for old_word , new_word in keywords_to_change .items ():
50975102 json_schema_dict [new_word ] = json_schema_dict .pop (old_word )
5098- if not self .all_of :
5099- json_schema_dict .pop ("allOf" )
5103+
5104+ # Converts the conditional dependencies to allOf conditions and adds them to the JSON Schema dict
5105+ # The conditional dependencies are not added to the JSON Schema dict directly because they are not
5106+ # in the correct format to be added as is, and they need to be converted to allOf conditions first.
5107+ # Finally the conditional dependencies are removed from the JSON Schema dict because they are not a
5108+ # valid JSON Schema keyword
5109+ if self ._conditional_dependencies :
5110+ json_schema_dict ["allOf" ] = self ._convert_conditional_properties_to_all_of (
5111+ self ._conditional_dependencies
5112+ )
5113+ json_schema_dict .pop ("_conditional_dependencies" )
51005114 return json_schema_dict
51015115
51025116 def add_required_property (self , name : str ) -> None :
@@ -5108,15 +5122,6 @@ def add_required_property(self, name: str) -> None:
51085122 """
51095123 self .required .append (name )
51105124
5111- def add_to_all_of_list (self , item : AllOf ) -> None :
5112- """
5113- Adds a property to the all_of list
5114-
5115- Arguments:
5116- item: The item to add to the all_of list
5117- """
5118- self .all_of .append (item )
5119-
51205125 def update_property (self , property_dict : dict [str , Property ]) -> None :
51215126 """
51225127 Updates the property dict
@@ -5142,47 +5147,97 @@ def update_property(self, property_dict: dict[str, Property]) -> None:
51425147 )
51435148 self .properties .update (property_dict )
51445149
5150+ def add_conditional_dependency (
5151+ self , watched_property : str , enum_value : str , dependent_property : str
5152+ ) -> None :
5153+ """
5154+ Adds a conditional dependency to the conditional dependencies dict
5155+ Arguments:
5156+ watched_property: The property that is being watched
5157+ enum_value: The value of the watched property that triggers the condition
5158+ dependent_property: The property that becomes required when the condition is triggered
5159+ """
5160+ if (watched_property , enum_value ) not in self ._conditional_dependencies :
5161+ self ._conditional_dependencies [(watched_property , enum_value )] = [
5162+ dependent_property
5163+ ]
5164+ else :
5165+ self ._conditional_dependencies [(watched_property , enum_value )].append (
5166+ dependent_property
5167+ )
5168+
5169+ @staticmethod
5170+ def _convert_conditional_properties_to_all_of (
5171+ conditional_dependencies : dict [tuple [str , str ], list [str ]]
5172+ ) -> list [AllOf ]:
5173+ """
5174+ Converts the conditional dependencies dict to a list of JSON Schema allOf conditions
5175+
5176+ Arguments:
5177+ conditional_dependencies: A mapping of conditional dependencies to be added to the "allOf" keyword in JSON Schema.
5178+ The key is a tuple of (watched_property, enum_value)
5179+ The value is a list of properties that become required when watched_property has the value enum_value.
5180+
5181+ Returns:
5182+ A list of JSON Schema allOf conditions
5183+
5184+
5185+ For example:
5186+ In the example data model the Patient component has the Diagnosis attribute.
5187+ The Diagnosis attribute has valid values of ["Healthy", "Cancer"].
5188+ The Cancer valid value is also an attribute that dependsOn on the
5189+ attributes Cancer Type and Family History
5190+ Cancer Type and Family History are attributes with valid values.
5191+ Therefore: When Diagnosis == "Cancer", Cancer Type and Family History should become required
5192+
5193+ Example conditional schema:
5194+ "if":{
5195+ "properties":{
5196+ "Diagnosis":{
5197+ "enum":[
5198+ "Cancer"
5199+ ]
5200+ }
5201+ }
5202+ },
5203+ "then":{
5204+ "properties":{
5205+ "FamilyHistory":{
5206+ "not":{
5207+ "type":"null"
5208+ }
5209+ }
5210+ },
5211+ "required":["FamilyHistory"]
5212+ }
5213+ """
5214+ all_of = []
5215+ for (
5216+ watched_property ,
5217+ enum_value ,
5218+ ), dependent_properties in conditional_dependencies .items ():
5219+ conditional_dep = {
5220+ "if" : {"properties" : {watched_property : {"enum" : [enum_value ]}}},
5221+ "then" : {
5222+ "properties" : {
5223+ prop : {"not" : {"type" : "null" }} for prop in dependent_properties
5224+ },
5225+ "required" : dependent_properties ,
5226+ },
5227+ }
5228+ all_of .append (conditional_dep )
5229+ return all_of
5230+
51455231
51465232def _set_conditional_dependencies (
51475233 json_schema : JSONSchema ,
51485234 graph_state : GraphTraversalState ,
51495235 use_display_labels : bool = True ,
51505236) -> None :
51515237 """
5152- This sets conditional requirements in the "allOf" keyword.
5238+ This translates conditional requirements in a JSONSchema object from a GraphTraversalState object
51535239 This is used when certain properties are required depending on the value of another property.
51545240
5155- For example:
5156- In the example data model the Patient component has the Diagnosis attribute.
5157- The Diagnosis attribute has valid values of ["Healthy", "Cancer"].
5158- The Cancer valid value is also an attribute that dependsOn on the
5159- attributes Cancer Type and Family History
5160- Cancer Type and Family History are attributes with valid values.
5161- Therefore: When Diagnosis == "Cancer", Cancer Type and Family History should become required
5162-
5163- Example conditional schema:
5164- "if":{
5165- "properties":{
5166- "Diagnosis":{
5167- "enum":[
5168- "Cancer"
5169- ]
5170- }
5171- }
5172- },
5173- "then":{
5174- "properties":{
5175- "FamilyHistory":{
5176- "not":{
5177- "type":"null"
5178- }
5179- }
5180- },
5181- "required":[
5182- "FamilyHistory"
5183- ]
5184- }
5185-
51865241 Arguments:
51875242 json_schema: The JSON Scheme where the node might be set as a property
51885243 graph_state: The instance tracking the current state of the graph
@@ -5201,14 +5256,9 @@ def _set_conditional_dependencies(
52015256 conditional_properties = graph_state .get_conditional_properties (use_display_labels )
52025257 for prop in conditional_properties :
52035258 attribute , value = prop
5204- conditional_schema = {
5205- "if" : {"properties" : {attribute : {"enum" : [value ]}}},
5206- "then" : {
5207- "properties" : {node_name : {"not" : {"type" : "null" }}},
5208- "required" : [node_name ],
5209- },
5210- }
5211- json_schema .add_to_all_of_list (conditional_schema )
5259+ json_schema .add_conditional_dependency (
5260+ watched_property = attribute , enum_value = value , dependent_property = node_name
5261+ )
52125262
52135263
52145264def _create_enum_array_property (
0 commit comments