Skip to content

Commit

Permalink
[core][feat] Add kind description in multi tenant mode (#2204)
Browse files Browse the repository at this point in the history
  • Loading branch information
aquamatthias authored Sep 24, 2024
1 parent 634d527 commit 14887b6
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 11 deletions.
1 change: 0 additions & 1 deletion fixcore/fixcore/model/exportable_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ def json_export_simple_schema(
with_properties: bool = True,
with_relatives: bool = True,
with_metadata: bool = True,
aggregate_roots_only: bool = False,
) -> List[Json]:
def export_simple(kind: SimpleKind) -> Json:
result = kind.as_json()
Expand Down
2 changes: 1 addition & 1 deletion fixcore/fixcore/model/model_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def code_model() -> Model:
The model is only loaded on demand and only once.
"""
load_plugin_classes()
return Model.from_kinds([from_js(m, Kind) for m in export_model()]) # type: ignore
return Model.from_kinds([from_js(m, Kind) for m in export_model(with_kind_description=True)]) # type: ignore


class ModelHandlerFromCodeAndDB(ModelHandlerDB):
Expand Down
20 changes: 14 additions & 6 deletions fixlib/fixlib/core/model_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ def dataclasses_to_fixcore_model(
aggregate_root: Optional[Type[Any]] = None,
walk_subclasses: bool = True,
use_optional_as_required: bool = False,
with_description: bool = True,
with_kind_description: bool = False,
with_prop_description: bool = False,
) -> List[Json]:
"""
Analyze all transitive dataclasses and create the model
Expand All @@ -163,7 +164,8 @@ def dataclasses_to_fixcore_model(
:param aggregate_root: if a type is a subtype of this type, it will be considered an aggregate root.
:param walk_subclasses: if true, all subclasses of the given classes will be analyzed as well.
:param use_optional_as_required: if true, all non-optional fields will be considered required.
:param with_description: if true, include the description for classes and properties.
:param with_kind_description: if true, include the description for classes.
:param with_prop_description: if true, include the description for properties.
:return: the model definition in the fixcore json format.
"""

Expand All @@ -176,7 +178,7 @@ def prop(field: Attribute) -> List[Json]: # type: ignore
meta = field.metadata.copy()
kind = meta.pop("type_hint", model_name(field.type))
desc = meta.pop("description", None)
desc = desc if with_description else None
desc = desc if with_prop_description else None
required = meta.pop("required", use_optional_as_required and not is_optional(field.type)) # type: ignore
synthetic = meta.pop("synthetic", None)
synthetic = synthetic if synthetic else {}
Expand Down Expand Up @@ -253,7 +255,13 @@ def export_data_class(clazz: type) -> None:
metadata["service"] = s
if (slc := getattr(clazz, "categories", None)) and callable(slc) and (sl := slc()):
metadata["categories"] = sl
if with_description and (s := clazz.__dict__.get("kind_description", None)) and isinstance(s, str):
if ( # only export kind description on aggregate roots
with_kind_description
and (ar := aggregate_root)
and issubclass(clazz, ar)
and (s := clazz.__dict__.get("kind_description", None))
and isinstance(s, str)
):
metadata["description"] = s

model.append(
Expand Down Expand Up @@ -293,9 +301,9 @@ def literal_name(en: Enum) -> str:
# Use this model exporter, if a dynamic object is exported
# with given name and properties.
def dynamic_object_to_fixcore_model(
name: str, properties: Dict[str, type], aggregate_root: bool = True, traverse_dependant: bool = True
name: str, properties: Dict[str, type], aggregate_root: bool = True, traverse_dependant: bool = True, **kwargs: Any
) -> List[Json]:
dependant = dataclasses_to_fixcore_model(set(properties.values())) if traverse_dependant else []
dependant = dataclasses_to_fixcore_model(set(properties.values()), **kwargs) if traverse_dependant else []
# append definition for top level object
dependant.append(
{
Expand Down
2 changes: 1 addition & 1 deletion fixlib/fixlib/graph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def export_model(graph: Optional[Graph] = None, **kwargs: Any) -> List[Json]:
for node in graph.nodes:
classes.add(type(node))

model = resource_classes_to_fixcore_model(classes, aggregate_root=BaseResource, with_description=False, **kwargs)
model = resource_classes_to_fixcore_model(classes, aggregate_root=BaseResource, **kwargs)
for resource_model in model:
if resource_model.get("fqn") == "resource":
resource_model.get("properties", []).append(
Expand Down
9 changes: 7 additions & 2 deletions fixlib/test/core/model_export_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def test_enum_to_model() -> None:


def test_dataclasses_to_fixcore_model() -> None:
result = dataclasses_to_fixcore_model({DataClassExample})
result = dataclasses_to_fixcore_model({DataClassExample}, with_kind_description=True, with_prop_description=True)
assert len(result) == 5
for r in result:
props = {p["name"]: p for p in r.get("properties", [])}
Expand Down Expand Up @@ -187,7 +187,12 @@ class GcpTestConfigConfig:
def test_config_export():
# Let's assume a dynamic top level object of name Config
# The properties are defined by name and related type.
result = dynamic_object_to_fixcore_model("config", {"aws": AwsTestConfig, "gcp": GcpTestConfigConfig})
result = dynamic_object_to_fixcore_model(
"config",
{"aws": AwsTestConfig, "gcp": GcpTestConfigConfig},
with_kind_description=True,
with_prop_description=True,
)
result_dict = {a["fqn"]: a for a in result}
assert len(result_dict["gcp_config"]["properties"]) == 1
assert len(result_dict["aws_config"]["properties"]) == 2
Expand Down

0 comments on commit 14887b6

Please sign in to comment.