def get_singular_alias(alias_path: str, contextual_mode: bool = False) -> str: """ Get the alias in the singular form from a jsonpath. If contextual_mode is True and contextual_path is None, it assumes alias_path is relative to the directory the user is running trestle from. """ if len(alias_path.strip()) == 0: raise err.TrestleError('Invalid jsonpath.') singular_alias: str = '' full_alias_path = alias_path if contextual_mode: _, full_model_alias = get_contextual_model_type() first_alias_a = full_model_alias.split('.')[-1] first_alias_b = alias_path.split('.')[0] if first_alias_a == first_alias_b: full_model_alias = '.'.join(full_model_alias.split('.')[:-1]) full_alias_path = '.'.join([full_model_alias, alias_path]).strip('.') path_parts = full_alias_path.split(const.ALIAS_PATH_SEPARATOR) if len(path_parts) < 2: raise err.TrestleError('Invalid jsonpath.') model_types = [] root_model_alias = path_parts[0] found = False for module_name in const.MODELTYPE_TO_MODELMODULE.values(): model_type, model_alias = utils.get_root_model(module_name) if root_model_alias == model_alias: found = True model_types.append(model_type) break if not found: raise err.TrestleError( f'{root_model_alias} is an invalid root model alias.') model_type = model_types[0] for i in range(1, len(path_parts)): if utils.is_collection_field_type(model_type): model_type = utils.get_inner_type(model_type) i = i + 1 else: model_type = model_type.alias_to_field_map()[ path_parts[i]].outer_type_ model_types.append(model_type) if not utils.is_collection_field_type(model_type): raise err.TrestleError('Not a valid generic collection model.') last_alias = path_parts[-1] parent_model_type = model_types[-2] singular_alias = utils.classname_to_alias( utils.get_inner_type(parent_model_type.alias_to_field_map() [last_alias].outer_type_).__name__, 'json') return singular_alias
def test_get_root_model() -> None: """Test looking for the root model of a trestle oscal module.""" with pytest.raises(err.TrestleError): mutils.get_root_model('invalid') with pytest.raises(err.TrestleError): mutils.get_root_model('pydantic') malias_to_mtype = { 'catalog': catalog.Catalog, 'profile': profile.Profile, 'target-definition': target.TargetDefinition, 'component-definition': component.ComponentDefinition, 'system-security-plan': ssp.SystemSecurityPlan, 'assessment-plan': assessment_plan.AssessmentPlan, 'assessment-results': assessment_results.AssessmentResults, 'plan-of-action-and-milestones': poam.PlanOfActionAndMilestones } for key in malias_to_mtype: module_name = malias_to_mtype[key].__module__ model_type, model_alias = mutils.get_root_model(module_name) assert model_type == malias_to_mtype[key] assert model_alias == key
def get_contextual_model_type( path: pathlib.Path = None) -> Tuple[Type[OscalBaseModel], str]: """Get the full contextual model class and full jsonpath for the alias based on the contextual path.""" if path is None: path = pathlib.Path.cwd() if not is_valid_project_model_path(path): raise err.TrestleError(f'Trestle project not found at {path}') root_path = get_trestle_project_root(path) project_model_path = get_project_model_path(path) if root_path is None or project_model_path is None: raise err.TrestleError('Trestle project not found') relative_path = path.relative_to(str(root_path)) project_type = relative_path.parts[0] # catalogs, profiles, etc module_name = const.MODELTYPE_TO_MODELMODULE[project_type] model_relative_path = pathlib.Path(*relative_path.parts[2:]) model_type, model_alias = utils.get_root_model(module_name) full_alias = model_alias for i in range(len(model_relative_path.parts)): tmp_path = root_path.joinpath(*relative_path.parts[:2], *model_relative_path.parts[:i + 1]) alias = extract_alias(tmp_path) if i > 0 or model_alias != alias: model_alias = alias full_alias = f'{full_alias}.{model_alias}' if utils.is_collection_field_type(model_type): model_type = utils.get_inner_type(model_type) else: model_type = model_type.alias_to_field_map()[alias].outer_type_ return model_type, full_alias