diff --git a/griffon/commands/__init__.py b/griffon/commands/__init__.py new file mode 100644 index 0000000..9f67529 --- /dev/null +++ b/griffon/commands/__init__.py @@ -0,0 +1,3 @@ +""" +cli commands +""" diff --git a/griffon/commands/entities/osidb.py b/griffon/commands/entities/osidb.py deleted file mode 100644 index 59732ba..0000000 --- a/griffon/commands/entities/osidb.py +++ /dev/null @@ -1,994 +0,0 @@ -""" -osidb entity operations - -""" -import json -import logging - -import click -from osidb_bindings.bindings.python_client.api.osidb import ( - osidb_api_v1_affects_create, - osidb_api_v1_affects_list, - osidb_api_v1_affects_retrieve, - osidb_api_v1_affects_update, - osidb_api_v1_flaws_acknowledgments_create, - osidb_api_v1_flaws_acknowledgments_list, - osidb_api_v1_flaws_acknowledgments_retrieve, - osidb_api_v1_flaws_acknowledgments_update, - osidb_api_v1_flaws_comments_create, - osidb_api_v1_flaws_comments_list, - osidb_api_v1_flaws_comments_retrieve, - osidb_api_v1_flaws_create, - osidb_api_v1_flaws_list, - osidb_api_v1_flaws_references_create, - osidb_api_v1_flaws_references_list, - osidb_api_v1_flaws_references_retrieve, - osidb_api_v1_flaws_references_update, - osidb_api_v1_flaws_retrieve, - osidb_api_v1_flaws_update, - osidb_api_v1_trackers_list, - osidb_api_v1_trackers_retrieve, -) -from osidb_bindings.bindings.python_client.models import ( - Affect, - Flaw, - FlawAcknowledgment, - FlawComment, - FlawReference, - Tracker, -) -from requests import HTTPError - -from griffon import OSIDB_API_URL, OSIDBService, progress_bar -from griffon.commands.entities.helpers import ( - abort_if_false, - filter_request_fields, - get_editor, - multivalue_params_to_csv, - query_params_options, - request_body_options, -) -from griffon.output import console, cprint - -logger = logging.getLogger("griffon") - -default_conditions: dict = {} - - -@click.group(name="osidb") -@click.pass_context -def osidb_grp(ctx): - pass - - -# flaws -@osidb_grp.group(help=f"{OSIDB_API_URL}/osidb/api/v1/flaws") -@click.pass_context -def flaws(ctx): - """OSIDB Flaws.""" - - -@flaws.command(name="list") -@query_params_options( - entity="Flaw", - endpoint_module=osidb_api_v1_flaws_list, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(Flaw))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Flaw))}, - "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Flaw))}, - }, -) -@click.pass_context -@progress_bar -def list_flaws(ctx, **params): - # TODO: handle output - session = OSIDBService.create_session() - - params = multivalue_params_to_csv(params) - return cprint( - list(session.flaws.retrieve_list_iterator_async(max_results=5000, **params)), ctx=ctx - ) - - -@flaws.command(name="get") -@click.option( - "--id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@query_params_options( - entity="Flaw", - endpoint_module=osidb_api_v1_flaws_retrieve, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(Flaw))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Flaw))}, - "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Flaw))}, - }, -) -@click.pass_context -@progress_bar -def get_flaw(ctx, flaw_id, **params): - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - data = session.flaws.retrieve(flaw_id, **params) - return cprint(data, ctx=ctx) - - -@flaws.command(name="update") -@click.option( - "--id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@request_body_options( - endpoint_module=osidb_api_v1_flaws_update, exclude=["uuid", "trackers", "created_dt"] -) -@click.pass_context -def update_flaw(ctx, flaw_id, **params): - request_body_type = getattr(osidb_api_v1_flaws_update, "REQUEST_BODY_TYPE", None) - if request_body_type is None: - raise click.ClickException( - "No request body template for Flaw update. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields( - request_body_type.get_fields(), exclude=["uuid", "trackers", "created_dt"] - ) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - try: - data = session.flaws.retrieve(id=flaw_id, include_fields=",".join(fields)) - except Exception as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to fetch Flaw with ID '{flaw_id}'. " - "Flaw either does not exist or you have insufficient permissions. " - "Consider running griffon with -v option for verbose error log." - ) - - data = data.to_dict() - # remove status data from OSIDB server - [data.pop(key) for key in ["dt", "env", "revision", "version"]] - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit(text=json.dumps(data, indent=4), editor=get_editor(), require_save=False) - data = json.loads(data) - - try: - data = session.flaws.update(id=flaw_id, form_data=data) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to update Flaw with ID '{flaw_id}'. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -@flaws.command(name="create") -@request_body_options( - endpoint_module=osidb_api_v1_flaws_create, - exclude=["uuid", "trackers", "created_dt", "updated_dt"], -) -@click.pass_context -def create_flaw(ctx, **params): - request_body_type = getattr(osidb_api_v1_flaws_create, "REQUEST_BODY_TYPE", None) - if request_body_type is None: - raise click.ClickException( - "No request body template for Flaw create. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields( - request_body_type.get_fields(), - exclude=["uuid", "trackers", "created_dt", "updated_dt"], - ) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - data = {field: "" for field in fields} - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit( - text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False - ) - data = json.loads(data) - - try: - data = session.flaws.create(form_data=data) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - "Failed to create Flaw. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -# flaw comments -@flaws.group(help=f"{OSIDB_API_URL}/osidb/api/v1/flaws//comments", name="comments") -@click.pass_context -def flaw_comments(ctx): - """OSIDB Flaw Comments.""" - pass - - -@flaw_comments.command(name="get") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@click.option("--uuid", "comment_uuid", help="Comment UUID.", required=True) -@query_params_options( - entity="Flaw Comment", - endpoint_module=osidb_api_v1_flaws_comments_retrieve, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawComment))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawComment))}, - "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(FlawComment))}, - }, -) -@click.pass_context -@progress_bar -def get_flaw_comment(ctx, flaw_id, comment_uuid, **params): - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - data = session.flaws.comments.retrieve(flaw_id, comment_uuid, **params) - return cprint(data, ctx=ctx) - - -@flaw_comments.command(name="list") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@query_params_options( - entity="Flaw Comments", - endpoint_module=osidb_api_v1_flaws_comments_list, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawComment))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawComment))}, - "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(FlawComment))}, - }, -) -@click.pass_context -@progress_bar -def list_flaw_comments(ctx, flaw_id, **params): - # TODO: handle pagination - # TODO: handle output - session = OSIDBService.create_session() - - params = multivalue_params_to_csv(params) - data = session.flaws.comments.retrieve_list(flaw_id, **params).results - return cprint(data, ctx=ctx) - - -@flaw_comments.command(name="create") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@request_body_options( - endpoint_module=osidb_api_v1_flaws_comments_create, - exclude=["uuid", "created_dt"], -) -@click.pass_context -def create_flaw_comment(ctx, flaw_id, **params): - request_body_type = getattr(osidb_api_v1_flaws_comments_create, "REQUEST_BODY_TYPE", None) - if request_body_type is None: - raise click.ClickException( - "No request body template for Flaw Comment create. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields( - request_body_type.get_fields(), - exclude=["uuid", "created_dt"], - ) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - data = {field: "" for field in fields} - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit( - text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False - ) - data = json.loads(data) - - try: - data = session.flaws.comments.create(data, flaw_id=flaw_id) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - "Failed to create Flaw Comment. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -# flaw references -@flaws.group(help=f"{OSIDB_API_URL}/osidb/api/v1/flaws//references", name="references") -@click.pass_context -def flaw_references(ctx): - """OSIDB Flaw References.""" - pass - - -@flaw_references.command(name="get") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@click.option("--uuid", "reference_uuid", help="Reference UUID.", required=True) -@query_params_options( - entity="Flaw Reference", - endpoint_module=osidb_api_v1_flaws_references_retrieve, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawReference))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawReference))}, - "include_meta_attr": { - "type": click.Choice(OSIDBService.get_meta_attr_fields(FlawReference)) - }, - }, -) -@click.pass_context -@progress_bar -def get_flaw_reference(ctx, flaw_id, reference_uuid, **params): - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - data = session.flaws.references.retrieve(flaw_id, reference_uuid, **params) - return cprint(data, ctx=ctx) - - -@flaw_references.command(name="list") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@query_params_options( - entity="Flaw References", - endpoint_module=osidb_api_v1_flaws_references_list, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawReference))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawReference))}, - "include_meta_attr": { - "type": click.Choice(OSIDBService.get_meta_attr_fields(FlawReference)) - }, - }, -) -@click.pass_context -@progress_bar -def list_flaw_references(ctx, flaw_id, **params): - # TODO: handle pagination - # TODO: handle output - session = OSIDBService.create_session() - - params = multivalue_params_to_csv(params) - data = session.flaws.references.retrieve_list(flaw_id, **params).results - return cprint(data, ctx=ctx) - - -@flaw_references.command(name="create") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@request_body_options( - endpoint_module=osidb_api_v1_flaws_references_create, - exclude=["uuid", "created_dt"], -) -@click.pass_context -def create_flaw_reference(ctx, flaw_id, **params): - request_body_type = getattr(osidb_api_v1_flaws_references_create, "REQUEST_BODY_TYPE", None) - if request_body_type is None: - raise click.ClickException( - "No request body template for Flaw Reference create. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields( - request_body_type.get_fields(), - exclude=["uuid", "created_dt"], - ) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - data = {field: "" for field in fields} - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit( - text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False - ) - data = json.loads(data) - - try: - data = session.flaws.references.create(data, flaw_id=flaw_id) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - "Failed to create Flaw Reference. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -@flaw_references.command(name="update") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@click.option("--uuid", "reference_uuid", help="Reference UUID.", required=True) -@request_body_options(endpoint_module=osidb_api_v1_flaws_references_update, exclude=["uuid"]) -@click.pass_context -def update_flaw_references(ctx, flaw_id, reference_uuid, **params): - request_body_type = getattr(osidb_api_v1_flaws_references_update, "REQUEST_BODY_TYPE", None) - if request_body_type is None: - raise click.ClickException( - "No request body template for Flaw Reference update. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields(request_body_type.get_fields(), exclude=["uuid"]) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - try: - data = session.flaws.references.retrieve( - flaw_id, reference_uuid, include_fields=",".join(fields) - ) - except Exception as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to fetch Flaw Reference with ID '{reference_uuid}'. " - "Flaw or Flaw Reference either does not exist or you have insufficient permissions. " - "Consider running griffon with -v option for verbose error log." - ) - - data = data.to_dict() - # remove status data from OSIDB server - [data.pop(key) for key in ["dt", "env", "revision", "version"]] - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit(text=json.dumps(data, indent=4), editor=get_editor(), require_save=False) - data = json.loads(data) - - try: - data = session.flaws.references.update(flaw_id, data, reference_uuid) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to update Flaw Reference with ID '{reference_uuid}'. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -@flaw_references.command(name="delete") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@click.option("--uuid", "reference_uuid", help="Reference UUID.", required=True) -@click.option( - "--yes", - is_flag=True, - callback=abort_if_false, - expose_value=False, - prompt="Are you sure you want to delete Flaw Reference?", -) -@click.pass_context -def delete_flaw_references(ctx, flaw_id, reference_uuid, **params): - session = OSIDBService.create_session() - try: - data = session.flaws.references.delete(flaw_id, reference_uuid) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to delete Flaw Reference {reference_uuid}. " - "It either does not exist or you have insufficient permissions." - ) - return cprint(data, ctx=ctx) - - -# flaw acknowledgments -@flaws.group( - help=f"{OSIDB_API_URL}/osidb/api/v1/flaws//acknowledgments", name="acknowledgments" -) -@click.pass_context -def flaw_acknowledgments(ctx): - """OSIDB Flaw Acknowledgment.""" - pass - - -@flaw_acknowledgments.command(name="get") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@click.option("--uuid", "acknowledgment_uuid", help="Acknowledgment UUID.", required=True) -@query_params_options( - entity="Flaw Acknowledgment", - endpoint_module=osidb_api_v1_flaws_acknowledgments_retrieve, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawAcknowledgment))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawAcknowledgment))}, - "include_meta_attr": { - "type": click.Choice(OSIDBService.get_meta_attr_fields(FlawAcknowledgment)) - }, - }, -) -@click.pass_context -@progress_bar -def get_flaw_acknowledgment(ctx, flaw_id, acknowledgment_uuid, **params): - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - data = session.flaws.acknowledgments.retrieve(flaw_id, acknowledgment_uuid, **params) - return cprint(data, ctx=ctx) - - -@flaw_acknowledgments.command(name="list") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@query_params_options( - entity="Flaw Acknowledgment", - endpoint_module=osidb_api_v1_flaws_acknowledgments_list, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawAcknowledgment))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawAcknowledgment))}, - "include_meta_attr": { - "type": click.Choice(OSIDBService.get_meta_attr_fields(FlawAcknowledgment)) - }, - }, -) -@click.pass_context -@progress_bar -def list_flaw_acknowledgments(ctx, flaw_id, **params): - # TODO: handle pagination - # TODO: handle output - session = OSIDBService.create_session() - - params = multivalue_params_to_csv(params) - data = session.flaws.acknowledgments.retrieve_list(flaw_id, **params).results - return cprint(data, ctx=ctx) - - -@flaw_acknowledgments.command(name="create") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@request_body_options( - endpoint_module=osidb_api_v1_flaws_acknowledgments_create, - exclude=["uuid", "created_dt"], -) -@click.pass_context -def create_flaw_acknowledgment(ctx, flaw_id, **params): - request_body_type = getattr( - osidb_api_v1_flaws_acknowledgments_create, "REQUEST_BODY_TYPE", None - ) - if request_body_type is None: - raise click.ClickException( - "No request body template for Flaw Acknowledgment create. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields( - request_body_type.get_fields(), - exclude=["uuid", "created_dt"], - ) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - data = {field: "" for field in fields} - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit( - text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False - ) - data = json.loads(data) - - try: - data = session.flaws.acknowledgments.create(data, flaw_id=flaw_id) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - "Failed to create Flaw Acknowledgment. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -@flaw_acknowledgments.command(name="update") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@click.option("--uuid", "acknowledgment_uuid", help="Acknowledgment UUID.", required=True) -@request_body_options(endpoint_module=osidb_api_v1_flaws_acknowledgments_update, exclude=["uuid"]) -@click.pass_context -def update_flaw_acknowledgment(ctx, flaw_id, acknowledgment_uuid, **params): - request_body_type = getattr( - osidb_api_v1_flaws_acknowledgments_update, "REQUEST_BODY_TYPE", None - ) - if request_body_type is None: - raise click.ClickException( - "No request body template for Flaw Acknowledgment update. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields(request_body_type.get_fields(), exclude=["uuid"]) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - try: - data = session.flaws.acknowledgments.retrieve( - flaw_id, acknowledgment_uuid, include_fields=",".join(fields) - ) - except Exception as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to fetch Flaw Acknowledgment with ID '{acknowledgment_uuid}'. " - "Flaw or Flaw Acknowledgment either does not exist or you have " - "insufficient permissions. Consider running griffon with " - "-v option for verbose error log." - ) - - data = data.to_dict() - # remove status data from OSIDB server - [data.pop(key) for key in ["dt", "env", "revision", "version"]] - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit(text=json.dumps(data, indent=4), editor=get_editor(), require_save=False) - data = json.loads(data) - - try: - data = session.flaws.acknowledgments.update(flaw_id, data, acknowledgment_uuid) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to update Flaw Acknowledgment with ID '{acknowledgment_uuid}'. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -@flaw_acknowledgments.command(name="delete") -@click.option( - "--flaw-id", - "flaw_id", - help="Flaw CVE-ID or UUID.", - required=True, -) -@click.option("--uuid", "acknowledgment_uuid", help="Acknowledgment UUID.", required=True) -@click.option( - "--yes", - is_flag=True, - callback=abort_if_false, - expose_value=False, - prompt="Are you sure you want to delete Flaw Acknowledgment?", -) -@click.pass_context -def delete_flaw_acknowledgments(ctx, flaw_id, acknowledgment_uuid, **params): - session = OSIDBService.create_session() - try: - data = session.flaws.acknowledgments.delete(flaw_id, acknowledgment_uuid) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to delete Flaw Acknowledgment {acknowledgment_uuid}. " - "It either does not exist or you have insufficient permissions." - ) - return cprint(data, ctx=ctx) - - -# affects -@osidb_grp.group(help=f"{OSIDB_API_URL}/osidb/api/v1/affects") -@click.pass_context -def affects(ctx): - """OSIDB Affects.""" - pass - - -@affects.command(name="list") -@query_params_options( - entity="Affect", - endpoint_module=osidb_api_v1_affects_list, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(Affect))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Affect))}, - "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Affect))}, - }, -) -@click.pass_context -@progress_bar -def list_affects(ctx, **params): - # TODO: handle pagination - # TODO: handle output - session = OSIDBService.create_session() - - params = multivalue_params_to_csv(params) - return cprint( - list(session.affects.retrieve_list_iterator_async(max_results=5000, **params)), ctx=ctx - ) - - -@affects.command(name="get") -@click.option("--uuid", "affect_uuid", help="Affect UUID.", required=True) -@query_params_options( - entity="Affect", - endpoint_module=osidb_api_v1_affects_retrieve, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(Affect))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Affect))}, - "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Affect))}, - }, -) -@click.pass_context -@progress_bar -def get_affect(ctx, affect_uuid, **params): - """ - For parameter reference see: - /osidb/api/v1/schema/swagger-ui - /osidb/api/v1/affects/{uuid} - """ - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - data = session.affects.retrieve(affect_uuid, **params) - return cprint(data, ctx=ctx) - - -@affects.command(name="update") -@click.option("--uuid", "affect_uuid", help="Affect UUID.", required=True) -@request_body_options(endpoint_module=osidb_api_v1_affects_update, exclude=["uuid"]) -@click.pass_context -def update_affect(ctx, affect_uuid, **params): - request_body_type = getattr(osidb_api_v1_affects_update, "REQUEST_BODY_TYPE", None) - if request_body_type is None: - raise click.ClickException( - "No request body template for Affect update. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields(request_body_type.get_fields(), exclude=["uuid"]) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - try: - data = session.affects.retrieve(id=affect_uuid, include_fields=",".join(fields)) - except Exception as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to fetch Affect with ID '{affect_uuid}'. " - "Affect either does not exist or you have insufficient permissions. " - "Consider running griffon with -v option for verbose error log." - ) - - data = data.to_dict() - # remove status data from OSIDB server - [data.pop(key) for key in ["dt", "env", "revision", "version"]] - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit(text=json.dumps(data, indent=4), editor=get_editor(), require_save=False) - data = json.loads(data) - - try: - data = session.affects.update(id=affect_uuid, form_data=data) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to update Affect with ID '{affect_uuid}'. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -@affects.command(name="create") -@request_body_options( - endpoint_module=osidb_api_v1_affects_create, - exclude=["uuid", "created_dt", "updated_dt"], -) -@click.pass_context -def create_affect(ctx, **params): - request_body_type = getattr(osidb_api_v1_affects_create, "REQUEST_BODY_TYPE", None) - if request_body_type is None: - raise click.ClickException( - "No request body template for Affect create. " - "Is correct version of osidb-bindings installed?" - ) - - fields = filter_request_fields( - request_body_type.get_fields(), - exclude=["uuid", "created_dt", "updated_dt"], - ) - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - - data = {field: "" for field in fields} - data.update((field, value) for field, value in params.items() if value is not None) - - if ctx.obj["EDITOR"]: - data = click.edit( - text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False - ) - data = json.loads(data) - - try: - data = session.affects.create(form_data=data) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - "Failed to create Affect. " - "You might have insufficient permission or you've supplied malformed data. " - "Consider running griffon with -v option for verbose error log." - ) - return cprint(data, ctx=ctx) - - -@affects.command(name="delete") -@click.option("--uuid", "affect_uuid", help="Affect UUID.", required=True) -@click.option( - "--yes", - is_flag=True, - callback=abort_if_false, - expose_value=False, - prompt="Are you sure you delete affect?", -) -@click.pass_context -def delete_affect(ctx, affect_uuid, **params): - session = OSIDBService.create_session() - try: - data = session.affects.delete(affect_uuid) - except HTTPError as e: - if ctx.obj["VERBOSE"]: - console.log(e, e.response.json()) - raise click.ClickException( - f"Failed to delete {affect_uuid}. " - "It either does not exist or you have insufficient permissions." - ) - return cprint(data, ctx=ctx) - - -# trackers -@osidb_grp.group(help=f"{OSIDB_API_URL}/osidb/api/v1/trackers") -@click.pass_context -def trackers(ctx): - """OSIDB Trackers.""" - pass - - -@trackers.command(name="list") -@query_params_options( - entity="Tracker", - endpoint_module=osidb_api_v1_trackers_list, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(Tracker))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Tracker))}, - "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Tracker))}, - }, -) -@click.pass_context -def list_trackers(ctx, **params): - # TODO: handle pagination - # TODO: handle output - session = OSIDBService.create_session() - - params = multivalue_params_to_csv(params) - return cprint( - list(session.trackers.retrieve_list_iterator_async(max_results=5000, **params)), ctx=ctx - ) - - -@trackers.command(name="get") -@click.option("--uuid", "tracker_uuid", help="Tracker UUID.", required=True) -@query_params_options( - entity="Tracker", - endpoint_module=osidb_api_v1_trackers_retrieve, - options_overrides={ - "include_fields": {"type": click.Choice(OSIDBService.get_fields(Tracker))}, - "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Tracker))}, - "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Tracker))}, - }, -) -@click.pass_context -@progress_bar -def get_tracker(ctx, tracker_uuid, **params): - params = multivalue_params_to_csv(params) - - session = OSIDBService.create_session() - data = session.trackers.retrieve(tracker_uuid, **params) - return cprint(data, ctx=ctx) - - -@osidb_grp.group(name="admin") -@click.pass_context -def manage_grp(ctx): - """Manage osidb""" - pass - - -@manage_grp.command(name="status") -@click.pass_context -def osidb_status(ctx): - session = OSIDBService.create_session() - data = session.status() - return cprint(data, ctx=ctx) - - -@manage_grp.command(name="api_doc") -def osidb_api_docs(): - click.launch(f"{OSIDB_API_URL}/osidb/api/v1/schema/swagger-ui/") diff --git a/griffon/commands/entities/osidb/__init__.py b/griffon/commands/entities/osidb/__init__.py new file mode 100644 index 0000000..fa907fb --- /dev/null +++ b/griffon/commands/entities/osidb/__init__.py @@ -0,0 +1,28 @@ +""" +osidb entity operations + +""" +import logging + +import click + +from .affects import affects +from .flaws import flaws +from .manage import manage_grp +from .trackers import trackers + +logger = logging.getLogger("griffon") + +default_conditions: dict = {} + + +@click.group(name="osidb") +@click.pass_context +def osidb_grp(ctx): + pass + + +osidb_grp.add_command(flaws) +osidb_grp.add_command(affects) +osidb_grp.add_command(trackers) +osidb_grp.add_command(manage_grp) diff --git a/griffon/commands/entities/osidb/affects.py b/griffon/commands/entities/osidb/affects.py new file mode 100644 index 0000000..0f2c923 --- /dev/null +++ b/griffon/commands/entities/osidb/affects.py @@ -0,0 +1,202 @@ +""" +osidb entity operations + +""" +import json +import logging + +import click +from osidb_bindings.bindings.python_client.api.osidb import ( + osidb_api_v1_affects_create, + osidb_api_v1_affects_list, + osidb_api_v1_affects_retrieve, + osidb_api_v1_affects_update, +) +from osidb_bindings.bindings.python_client.models import Affect +from requests import HTTPError + +from griffon import OSIDB_API_URL, OSIDBService, progress_bar +from griffon.commands.entities.helpers import ( + abort_if_false, + filter_request_fields, + get_editor, + multivalue_params_to_csv, + query_params_options, + request_body_options, +) +from griffon.output import console, cprint + +logger = logging.getLogger("griffon") + + +@click.group(help=f"{OSIDB_API_URL}/osidb/api/v1/affects") +@click.pass_context +def affects(ctx): + """OSIDB Affects.""" + pass + + +@affects.command(name="list") +@query_params_options( + entity="Affect", + endpoint_module=osidb_api_v1_affects_list, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(Affect))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Affect))}, + "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Affect))}, + }, +) +@click.pass_context +@progress_bar +def list_affects(ctx, **params): + # TODO: handle pagination + # TODO: handle output + session = OSIDBService.create_session() + + params = multivalue_params_to_csv(params) + return cprint( + list(session.affects.retrieve_list_iterator_async(max_results=5000, **params)), ctx=ctx + ) + + +@affects.command(name="get") +@click.option("--uuid", "affect_uuid", help="Affect UUID.", required=True) +@query_params_options( + entity="Affect", + endpoint_module=osidb_api_v1_affects_retrieve, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(Affect))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Affect))}, + "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Affect))}, + }, +) +@click.pass_context +@progress_bar +def get_affect(ctx, affect_uuid, **params): + """ + For parameter reference see: + /osidb/api/v1/schema/swagger-ui - /osidb/api/v1/affects/{uuid} + """ + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + data = session.affects.retrieve(affect_uuid, **params) + return cprint(data, ctx=ctx) + + +@affects.command(name="update") +@click.option("--uuid", "affect_uuid", help="Affect UUID.", required=True) +@request_body_options(endpoint_module=osidb_api_v1_affects_update, exclude=["uuid"]) +@click.pass_context +def update_affect(ctx, affect_uuid, **params): + request_body_type = getattr(osidb_api_v1_affects_update, "REQUEST_BODY_TYPE", None) + if request_body_type is None: + raise click.ClickException( + "No request body template for Affect update. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields(request_body_type.get_fields(), exclude=["uuid"]) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + try: + data = session.affects.retrieve(id=affect_uuid, include_fields=",".join(fields)) + except Exception as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to fetch Affect with ID '{affect_uuid}'. " + "Affect either does not exist or you have insufficient permissions. " + "Consider running griffon with -v option for verbose error log." + ) + + data = data.to_dict() + # remove status data from OSIDB server + [data.pop(key) for key in ["dt", "env", "revision", "version"]] + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit(text=json.dumps(data, indent=4), editor=get_editor(), require_save=False) + data = json.loads(data) + + try: + data = session.affects.update(id=affect_uuid, form_data=data) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to update Affect with ID '{affect_uuid}'. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) + + +@affects.command(name="create") +@request_body_options( + endpoint_module=osidb_api_v1_affects_create, + exclude=["uuid", "created_dt", "updated_dt"], +) +@click.pass_context +def create_affect(ctx, **params): + request_body_type = getattr(osidb_api_v1_affects_create, "REQUEST_BODY_TYPE", None) + if request_body_type is None: + raise click.ClickException( + "No request body template for Affect create. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields( + request_body_type.get_fields(), + exclude=["uuid", "created_dt", "updated_dt"], + ) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + data = {field: "" for field in fields} + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit( + text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False + ) + data = json.loads(data) + + try: + data = session.affects.create(form_data=data) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + "Failed to create Affect. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) + + +@affects.command(name="delete") +@click.option("--uuid", "affect_uuid", help="Affect UUID.", required=True) +@click.option( + "--yes", + is_flag=True, + callback=abort_if_false, + expose_value=False, + prompt="Are you sure you delete affect?", +) +@click.pass_context +def delete_affect(ctx, affect_uuid, **params): + session = OSIDBService.create_session() + try: + data = session.affects.delete(affect_uuid) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to delete {affect_uuid}. " + "It either does not exist or you have insufficient permissions." + ) + return cprint(data, ctx=ctx) diff --git a/griffon/commands/entities/osidb/flaws/__init__.py b/griffon/commands/entities/osidb/flaws/__init__.py new file mode 100644 index 0000000..9fdf20f --- /dev/null +++ b/griffon/commands/entities/osidb/flaws/__init__.py @@ -0,0 +1,194 @@ +""" +osidb flaw entities operations +""" + +import json +import logging + +import click +from osidb_bindings.bindings.python_client.api.osidb import ( + osidb_api_v1_flaws_create, + osidb_api_v1_flaws_list, + osidb_api_v1_flaws_retrieve, + osidb_api_v1_flaws_update, +) +from osidb_bindings.bindings.python_client.models import Flaw +from requests import HTTPError + +from griffon import OSIDB_API_URL, OSIDBService, progress_bar +from griffon.commands.entities.helpers import ( + filter_request_fields, + get_editor, + multivalue_params_to_csv, + query_params_options, + request_body_options, +) +from griffon.output import console, cprint + +from .acknowledgments import flaw_acknowledgments +from .comments import flaw_comments +from .references import flaw_references + +logger = logging.getLogger("griffon") + + +@click.group(help=f"{OSIDB_API_URL}/osidb/api/v1/flaws") +@click.pass_context +def flaws(ctx): + """OSIDB Flaws.""" + + +@flaws.command(name="list") +@query_params_options( + entity="Flaw", + endpoint_module=osidb_api_v1_flaws_list, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(Flaw))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Flaw))}, + "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Flaw))}, + }, +) +@click.pass_context +@progress_bar +def list_flaws(ctx, **params): + # TODO: handle output + session = OSIDBService.create_session() + + params = multivalue_params_to_csv(params) + return cprint( + list(session.flaws.retrieve_list_iterator_async(max_results=5000, **params)), ctx=ctx + ) + + +@flaws.command(name="get") +@click.option( + "--id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@query_params_options( + entity="Flaw", + endpoint_module=osidb_api_v1_flaws_retrieve, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(Flaw))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Flaw))}, + "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Flaw))}, + }, +) +@click.pass_context +@progress_bar +def get_flaw(ctx, flaw_id, **params): + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + data = session.flaws.retrieve(flaw_id, **params) + return cprint(data, ctx=ctx) + + +@flaws.command(name="update") +@click.option( + "--id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@request_body_options( + endpoint_module=osidb_api_v1_flaws_update, exclude=["uuid", "trackers", "created_dt"] +) +@click.pass_context +def update_flaw(ctx, flaw_id, **params): + request_body_type = getattr(osidb_api_v1_flaws_update, "REQUEST_BODY_TYPE", None) + if request_body_type is None: + raise click.ClickException( + "No request body template for Flaw update. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields( + request_body_type.get_fields(), exclude=["uuid", "trackers", "created_dt"] + ) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + try: + data = session.flaws.retrieve(id=flaw_id, include_fields=",".join(fields)) + except Exception as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to fetch Flaw with ID '{flaw_id}'. " + "Flaw either does not exist or you have insufficient permissions. " + "Consider running griffon with -v option for verbose error log." + ) + + data = data.to_dict() + # remove status data from OSIDB server + [data.pop(key) for key in ["dt", "env", "revision", "version"]] + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit(text=json.dumps(data, indent=4), editor=get_editor(), require_save=False) + data = json.loads(data) + + try: + data = session.flaws.update(id=flaw_id, form_data=data) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to update Flaw with ID '{flaw_id}'. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) + + +@flaws.command(name="create") +@request_body_options( + endpoint_module=osidb_api_v1_flaws_create, + exclude=["uuid", "trackers", "created_dt", "updated_dt"], +) +@click.pass_context +def create_flaw(ctx, **params): + request_body_type = getattr(osidb_api_v1_flaws_create, "REQUEST_BODY_TYPE", None) + if request_body_type is None: + raise click.ClickException( + "No request body template for Flaw create. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields( + request_body_type.get_fields(), + exclude=["uuid", "trackers", "created_dt", "updated_dt"], + ) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + data = {field: "" for field in fields} + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit( + text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False + ) + data = json.loads(data) + + try: + data = session.flaws.create(form_data=data) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + "Failed to create Flaw. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) + + +flaws.add_command(flaw_comments) +flaws.add_command(flaw_references) +flaws.add_command(flaw_acknowledgments) diff --git a/griffon/commands/entities/osidb/flaws/acknowledgments.py b/griffon/commands/entities/osidb/flaws/acknowledgments.py new file mode 100644 index 0000000..9192d20 --- /dev/null +++ b/griffon/commands/entities/osidb/flaws/acknowledgments.py @@ -0,0 +1,242 @@ +""" +osidb flaw acknowledgment entities operations +""" + +import json +import logging + +import click +from osidb_bindings.bindings.python_client.api.osidb import ( + osidb_api_v1_flaws_acknowledgments_create, + osidb_api_v1_flaws_acknowledgments_list, + osidb_api_v1_flaws_acknowledgments_retrieve, + osidb_api_v1_flaws_acknowledgments_update, +) +from osidb_bindings.bindings.python_client.models import FlawAcknowledgment +from requests import HTTPError + +from griffon import OSIDB_API_URL, OSIDBService, progress_bar +from griffon.commands.entities.helpers import ( + abort_if_false, + filter_request_fields, + get_editor, + multivalue_params_to_csv, + query_params_options, + request_body_options, +) +from griffon.output import console, cprint + +logger = logging.getLogger("griffon") + +default_conditions: dict = {} + + +@click.group( + help=f"{OSIDB_API_URL}/osidb/api/v1/flaws//acknowledgments", name="acknowledgments" +) +@click.pass_context +def flaw_acknowledgments(ctx): + """OSIDB Flaw Acknowledgment.""" + pass + + +@flaw_acknowledgments.command(name="get") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@click.option("--uuid", "acknowledgment_uuid", help="Acknowledgment UUID.", required=True) +@query_params_options( + entity="Flaw Acknowledgment", + endpoint_module=osidb_api_v1_flaws_acknowledgments_retrieve, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawAcknowledgment))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawAcknowledgment))}, + "include_meta_attr": { + "type": click.Choice(OSIDBService.get_meta_attr_fields(FlawAcknowledgment)) + }, + }, +) +@click.pass_context +@progress_bar +def get_flaw_acknowledgment(ctx, flaw_id, acknowledgment_uuid, **params): + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + data = session.flaws.acknowledgments.retrieve(flaw_id, acknowledgment_uuid, **params) + return cprint(data, ctx=ctx) + + +@flaw_acknowledgments.command(name="list") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@query_params_options( + entity="Flaw Acknowledgment", + endpoint_module=osidb_api_v1_flaws_acknowledgments_list, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawAcknowledgment))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawAcknowledgment))}, + "include_meta_attr": { + "type": click.Choice(OSIDBService.get_meta_attr_fields(FlawAcknowledgment)) + }, + }, +) +@click.pass_context +@progress_bar +def list_flaw_acknowledgments(ctx, flaw_id, **params): + # TODO: handle pagination + # TODO: handle output + session = OSIDBService.create_session() + + params = multivalue_params_to_csv(params) + data = session.flaws.acknowledgments.retrieve_list(flaw_id, **params).results + return cprint(data, ctx=ctx) + + +@flaw_acknowledgments.command(name="create") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@request_body_options( + endpoint_module=osidb_api_v1_flaws_acknowledgments_create, + exclude=["uuid", "created_dt"], +) +@click.pass_context +def create_flaw_acknowledgment(ctx, flaw_id, **params): + request_body_type = getattr( + osidb_api_v1_flaws_acknowledgments_create, "REQUEST_BODY_TYPE", None + ) + if request_body_type is None: + raise click.ClickException( + "No request body template for Flaw Acknowledgment create. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields( + request_body_type.get_fields(), + exclude=["uuid", "created_dt"], + ) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + data = {field: "" for field in fields} + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit( + text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False + ) + data = json.loads(data) + + try: + data = session.flaws.acknowledgments.create(data, flaw_id=flaw_id) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + "Failed to create Flaw Acknowledgment. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) + + +@flaw_acknowledgments.command(name="update") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@click.option("--uuid", "acknowledgment_uuid", help="Acknowledgment UUID.", required=True) +@request_body_options(endpoint_module=osidb_api_v1_flaws_acknowledgments_update, exclude=["uuid"]) +@click.pass_context +def update_flaw_acknowledgment(ctx, flaw_id, acknowledgment_uuid, **params): + request_body_type = getattr( + osidb_api_v1_flaws_acknowledgments_update, "REQUEST_BODY_TYPE", None + ) + if request_body_type is None: + raise click.ClickException( + "No request body template for Flaw Acknowledgment update. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields(request_body_type.get_fields(), exclude=["uuid"]) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + try: + data = session.flaws.acknowledgments.retrieve( + flaw_id, acknowledgment_uuid, include_fields=",".join(fields) + ) + except Exception as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to fetch Flaw Acknowledgment with ID '{acknowledgment_uuid}'. " + "Flaw or Flaw Acknowledgment either does not exist or you have " + "insufficient permissions. Consider running griffon with " + "-v option for verbose error log." + ) + + data = data.to_dict() + # remove status data from OSIDB server + [data.pop(key) for key in ["dt", "env", "revision", "version"]] + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit(text=json.dumps(data, indent=4), editor=get_editor(), require_save=False) + data = json.loads(data) + + try: + data = session.flaws.acknowledgments.update(flaw_id, data, acknowledgment_uuid) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to update Flaw Acknowledgment with ID '{acknowledgment_uuid}'. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) + + +@flaw_acknowledgments.command(name="delete") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@click.option("--uuid", "acknowledgment_uuid", help="Acknowledgment UUID.", required=True) +@click.option( + "--yes", + is_flag=True, + callback=abort_if_false, + expose_value=False, + prompt="Are you sure you want to delete Flaw Acknowledgment?", +) +@click.pass_context +def delete_flaw_acknowledgments(ctx, flaw_id, acknowledgment_uuid, **params): + session = OSIDBService.create_session() + try: + data = session.flaws.acknowledgments.delete(flaw_id, acknowledgment_uuid) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to delete Flaw Acknowledgment {acknowledgment_uuid}. " + "It either does not exist or you have insufficient permissions." + ) + return cprint(data, ctx=ctx) diff --git a/griffon/commands/entities/osidb/flaws/comments.py b/griffon/commands/entities/osidb/flaws/comments.py new file mode 100644 index 0000000..3e3bcec --- /dev/null +++ b/griffon/commands/entities/osidb/flaws/comments.py @@ -0,0 +1,139 @@ +""" +osidb flaw comment entities operations +""" + +import json +import logging + +import click +from osidb_bindings.bindings.python_client.api.osidb import ( + osidb_api_v1_flaws_comments_create, + osidb_api_v1_flaws_comments_list, + osidb_api_v1_flaws_comments_retrieve, +) +from osidb_bindings.bindings.python_client.models import FlawComment +from requests import HTTPError + +from griffon import OSIDB_API_URL, OSIDBService, progress_bar +from griffon.commands.entities.helpers import ( + filter_request_fields, + get_editor, + multivalue_params_to_csv, + query_params_options, + request_body_options, +) +from griffon.output import console, cprint + +logger = logging.getLogger("griffon") + + +@click.group(help=f"{OSIDB_API_URL}/osidb/api/v1/flaws//comments", name="comments") +@click.pass_context +def flaw_comments(ctx): + """OSIDB Flaw Comments.""" + pass + + +@flaw_comments.command(name="get") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@click.option("--uuid", "comment_uuid", help="Comment UUID.", required=True) +@query_params_options( + entity="Flaw Comment", + endpoint_module=osidb_api_v1_flaws_comments_retrieve, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawComment))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawComment))}, + "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(FlawComment))}, + }, +) +@click.pass_context +@progress_bar +def get_flaw_comment(ctx, flaw_id, comment_uuid, **params): + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + data = session.flaws.comments.retrieve(flaw_id, comment_uuid, **params) + return cprint(data, ctx=ctx) + + +@flaw_comments.command(name="list") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@query_params_options( + entity="Flaw Comments", + endpoint_module=osidb_api_v1_flaws_comments_list, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawComment))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawComment))}, + "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(FlawComment))}, + }, +) +@click.pass_context +@progress_bar +def list_flaw_comments(ctx, flaw_id, **params): + # TODO: handle pagination + # TODO: handle output + session = OSIDBService.create_session() + + params = multivalue_params_to_csv(params) + data = session.flaws.comments.retrieve_list(flaw_id, **params).results + return cprint(data, ctx=ctx) + + +@flaw_comments.command(name="create") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@request_body_options( + endpoint_module=osidb_api_v1_flaws_comments_create, + exclude=["uuid", "created_dt"], +) +@click.pass_context +def create_flaw_comment(ctx, flaw_id, **params): + request_body_type = getattr(osidb_api_v1_flaws_comments_create, "REQUEST_BODY_TYPE", None) + if request_body_type is None: + raise click.ClickException( + "No request body template for Flaw Comment create. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields( + request_body_type.get_fields(), + exclude=["uuid", "created_dt"], + ) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + data = {field: "" for field in fields} + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit( + text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False + ) + data = json.loads(data) + + try: + data = session.flaws.comments.create(data, flaw_id=flaw_id) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + "Failed to create Flaw Comment. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) diff --git a/griffon/commands/entities/osidb/flaws/references.py b/griffon/commands/entities/osidb/flaws/references.py new file mode 100644 index 0000000..88af4df --- /dev/null +++ b/griffon/commands/entities/osidb/flaws/references.py @@ -0,0 +1,233 @@ +""" +osidb flaw reference entities operations +""" + +import json +import logging + +import click +from osidb_bindings.bindings.python_client.api.osidb import ( + osidb_api_v1_flaws_references_create, + osidb_api_v1_flaws_references_list, + osidb_api_v1_flaws_references_retrieve, + osidb_api_v1_flaws_references_update, +) +from osidb_bindings.bindings.python_client.models import FlawReference +from requests import HTTPError + +from griffon import OSIDB_API_URL, OSIDBService, progress_bar +from griffon.commands.entities.helpers import ( + abort_if_false, + filter_request_fields, + get_editor, + multivalue_params_to_csv, + query_params_options, + request_body_options, +) +from griffon.output import console, cprint + +logger = logging.getLogger("griffon") + + +@click.group(help=f"{OSIDB_API_URL}/osidb/api/v1/flaws//references", name="references") +@click.pass_context +def flaw_references(ctx): + """OSIDB Flaw References.""" + pass + + +@flaw_references.command(name="get") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@click.option("--uuid", "reference_uuid", help="Reference UUID.", required=True) +@query_params_options( + entity="Flaw Reference", + endpoint_module=osidb_api_v1_flaws_references_retrieve, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawReference))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawReference))}, + "include_meta_attr": { + "type": click.Choice(OSIDBService.get_meta_attr_fields(FlawReference)) + }, + }, +) +@click.pass_context +@progress_bar +def get_flaw_reference(ctx, flaw_id, reference_uuid, **params): + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + data = session.flaws.references.retrieve(flaw_id, reference_uuid, **params) + return cprint(data, ctx=ctx) + + +@flaw_references.command(name="list") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@query_params_options( + entity="Flaw References", + endpoint_module=osidb_api_v1_flaws_references_list, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(FlawReference))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(FlawReference))}, + "include_meta_attr": { + "type": click.Choice(OSIDBService.get_meta_attr_fields(FlawReference)) + }, + }, +) +@click.pass_context +@progress_bar +def list_flaw_references(ctx, flaw_id, **params): + # TODO: handle pagination + # TODO: handle output + session = OSIDBService.create_session() + + params = multivalue_params_to_csv(params) + data = session.flaws.references.retrieve_list(flaw_id, **params).results + return cprint(data, ctx=ctx) + + +@flaw_references.command(name="create") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@request_body_options( + endpoint_module=osidb_api_v1_flaws_references_create, + exclude=["uuid", "created_dt"], +) +@click.pass_context +def create_flaw_reference(ctx, flaw_id, **params): + request_body_type = getattr(osidb_api_v1_flaws_references_create, "REQUEST_BODY_TYPE", None) + if request_body_type is None: + raise click.ClickException( + "No request body template for Flaw Reference create. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields( + request_body_type.get_fields(), + exclude=["uuid", "created_dt"], + ) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + data = {field: "" for field in fields} + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit( + text=json.dumps(data, indent=4, default=str), editor=get_editor(), require_save=False + ) + data = json.loads(data) + + try: + data = session.flaws.references.create(data, flaw_id=flaw_id) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + "Failed to create Flaw Reference. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) + + +@flaw_references.command(name="update") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@click.option("--uuid", "reference_uuid", help="Reference UUID.", required=True) +@request_body_options(endpoint_module=osidb_api_v1_flaws_references_update, exclude=["uuid"]) +@click.pass_context +def update_flaw_references(ctx, flaw_id, reference_uuid, **params): + request_body_type = getattr(osidb_api_v1_flaws_references_update, "REQUEST_BODY_TYPE", None) + if request_body_type is None: + raise click.ClickException( + "No request body template for Flaw Reference update. " + "Is correct version of osidb-bindings installed?" + ) + + fields = filter_request_fields(request_body_type.get_fields(), exclude=["uuid"]) + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + + try: + data = session.flaws.references.retrieve( + flaw_id, reference_uuid, include_fields=",".join(fields) + ) + except Exception as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to fetch Flaw Reference with ID '{reference_uuid}'. " + "Flaw or Flaw Reference either does not exist or you have insufficient permissions. " + "Consider running griffon with -v option for verbose error log." + ) + + data = data.to_dict() + # remove status data from OSIDB server + [data.pop(key) for key in ["dt", "env", "revision", "version"]] + data.update((field, value) for field, value in params.items() if value is not None) + + if ctx.obj["EDITOR"]: + data = click.edit(text=json.dumps(data, indent=4), editor=get_editor(), require_save=False) + data = json.loads(data) + + try: + data = session.flaws.references.update(flaw_id, data, reference_uuid) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to update Flaw Reference with ID '{reference_uuid}'. " + "You might have insufficient permission or you've supplied malformed data. " + "Consider running griffon with -v option for verbose error log." + ) + return cprint(data, ctx=ctx) + + +@flaw_references.command(name="delete") +@click.option( + "--flaw-id", + "flaw_id", + help="Flaw CVE-ID or UUID.", + required=True, +) +@click.option("--uuid", "reference_uuid", help="Reference UUID.", required=True) +@click.option( + "--yes", + is_flag=True, + callback=abort_if_false, + expose_value=False, + prompt="Are you sure you want to delete Flaw Reference?", +) +@click.pass_context +def delete_flaw_references(ctx, flaw_id, reference_uuid, **params): + session = OSIDBService.create_session() + try: + data = session.flaws.references.delete(flaw_id, reference_uuid) + except HTTPError as e: + if ctx.obj["VERBOSE"]: + console.log(e, e.response.json()) + raise click.ClickException( + f"Failed to delete Flaw Reference {reference_uuid}. " + "It either does not exist or you have insufficient permissions." + ) + return cprint(data, ctx=ctx) diff --git a/griffon/commands/entities/osidb/manage.py b/griffon/commands/entities/osidb/manage.py new file mode 100644 index 0000000..2ebbe38 --- /dev/null +++ b/griffon/commands/entities/osidb/manage.py @@ -0,0 +1,34 @@ +""" +osidb manage operations + +""" +import logging + +import click + +from griffon import OSIDB_API_URL, OSIDBService +from griffon.output import cprint + +logger = logging.getLogger("griffon") + +default_conditions: dict = {} + + +@click.group(name="admin") +@click.pass_context +def manage_grp(ctx): + """Manage osidb""" + pass + + +@manage_grp.command(name="status") +@click.pass_context +def osidb_status(ctx): + session = OSIDBService.create_session() + data = session.status() + return cprint(data, ctx=ctx) + + +@manage_grp.command(name="api_doc") +def osidb_api_docs(): + click.launch(f"{OSIDB_API_URL}/osidb/api/v1/schema/swagger-ui/") diff --git a/griffon/commands/entities/osidb/trackers.py b/griffon/commands/entities/osidb/trackers.py new file mode 100644 index 0000000..d3ec4fe --- /dev/null +++ b/griffon/commands/entities/osidb/trackers.py @@ -0,0 +1,71 @@ +""" +osidb tracker entities operations +""" + +import logging + +import click +from osidb_bindings.bindings.python_client.api.osidb import ( + osidb_api_v1_trackers_list, + osidb_api_v1_trackers_retrieve, +) +from osidb_bindings.bindings.python_client.models import Tracker + +from griffon import OSIDB_API_URL, OSIDBService, progress_bar +from griffon.commands.entities.helpers import ( + multivalue_params_to_csv, + query_params_options, +) +from griffon.output import cprint + +logger = logging.getLogger("griffon") + + +@click.group(help=f"{OSIDB_API_URL}/osidb/api/v1/trackers") +@click.pass_context +def trackers(ctx): + """OSIDB Trackers.""" + pass + + +@trackers.command(name="list") +@query_params_options( + entity="Tracker", + endpoint_module=osidb_api_v1_trackers_list, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(Tracker))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Tracker))}, + "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Tracker))}, + }, +) +@click.pass_context +def list_trackers(ctx, **params): + # TODO: handle pagination + # TODO: handle output + session = OSIDBService.create_session() + + params = multivalue_params_to_csv(params) + return cprint( + list(session.trackers.retrieve_list_iterator_async(max_results=5000, **params)), ctx=ctx + ) + + +@trackers.command(name="get") +@click.option("--uuid", "tracker_uuid", help="Tracker UUID.", required=True) +@query_params_options( + entity="Tracker", + endpoint_module=osidb_api_v1_trackers_retrieve, + options_overrides={ + "include_fields": {"type": click.Choice(OSIDBService.get_fields(Tracker))}, + "exclude_fields": {"type": click.Choice(OSIDBService.get_fields(Tracker))}, + "include_meta_attr": {"type": click.Choice(OSIDBService.get_meta_attr_fields(Tracker))}, + }, +) +@click.pass_context +@progress_bar +def get_tracker(ctx, tracker_uuid, **params): + params = multivalue_params_to_csv(params) + + session = OSIDBService.create_session() + data = session.trackers.retrieve(tracker_uuid, **params) + return cprint(data, ctx=ctx) diff --git a/griffon/commands/plugins/__init__.py b/griffon/commands/plugins/__init__.py new file mode 100644 index 0000000..bd5bdc3 --- /dev/null +++ b/griffon/commands/plugins/__init__.py @@ -0,0 +1,3 @@ +""" +griffon plugins +""" diff --git a/setup.py b/setup.py index 5164001..8c5b93f 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ This setup.py file uses setuptools to install the `griffon` package """ -from setuptools import setup +from setuptools import find_packages, setup with open("README.md") as f: readme = f.read() @@ -25,14 +25,7 @@ url="https://github.com/RedHatProductSecurity/griffon", description="Red Hat Product Security CLI", package_data={"griffon": ["static/*"]}, - packages=[ - "griffon", - "griffon/commands", - "griffon/commands/entities", - "griffon/commands/plugins", - "griffon/services", - "griffon/autocomplete", - ], + packages=find_packages(), install_requires=[ "click", "click-completion",