diff --git a/README.md b/README.md index 0f3e163..e43cd23 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,13 @@ If a monitoring list with the same name already exists, it gets overwritten with vilocify monitoringlist import --name "My Project v1.2.3" --from-cyclonedx my_project_v1.2.3.sbom.json ``` +### Example: Tenant with group feature enabled +If the example above gave you the error `Organization group must exist`, you must specify the `--group` parameter on import. +Valid values for `--group` can be listed with `vilocify groups`. +```bash +vilocify monitoringlist import --name "My Project v1.2.3" --group "ABC" --from-cyclonedx my_project_v1.2.3.sbom.json +``` + ## SDK usage The SDK is built on Vilocify's API. We recommend you also take a quick look at the raw API docs at https://portal.vilocify.com/documentation. diff --git a/vilocify/cli.py b/vilocify/cli.py index 1d00db5..d5aa3be 100644 --- a/vilocify/cli.py +++ b/vilocify/cli.py @@ -23,6 +23,7 @@ from vilocify.models import ( Component, ComponentRequest, + Group, MonitoringList, Notification, Vulnerability, @@ -132,11 +133,11 @@ def _load_bom(file: io.FileIO) -> Bom: return bom -def _load_ml(name: str, comment: str) -> MonitoringList: +def _load_ml(name: str, comment: str, group: str | None) -> MonitoringList: ml = MonitoringList.where("name", "eq", name).where("comment", "eq", comment).first() if ml is None: logger.info("No monitoring list with given name and comment found. Creating new list.") - ml = MonitoringList(name=name, comment=comment) + ml = MonitoringList(name=name, comment=comment, group=group) ml.create() logger.info("Using monitoring list %s", ml.id) @@ -190,9 +191,10 @@ def monitoringlist_show(monitoring_list_id: str, export_format: str): @monitoringlist.command("import") @click.option("--name", required=True, help="The monitoring list name.") @click.option("--comment", default="", help="The comment set for the monitoring list.") +@click.option("--group", help="The group of the monitoring list, in case a new list is created.") @click.option("--yes", is_flag=True, help="Skip interactive questions. Assumes 'yes' for all answers.") @click.option("--from-cyclonedx", type=click.File("rt"), required=True, help="The CycloneDX file to import.") -def monitoringlist_import(name: str, comment: str, yes: bool, from_cyclonedx: io.FileIO): +def monitoringlist_import(name: str, comment: str, group: str | None, yes: bool, from_cyclonedx: io.FileIO): """Creates or updates a monitoring list from a CycloneDX JSON or XML file. The monitoring list is identified by the given name and comment. Changing the name or comment between runs will @@ -205,7 +207,7 @@ def monitoringlist_import(name: str, comment: str, yes: bool, from_cyclonedx: io component_requests = [] bom = _load_bom(from_cyclonedx) - ml = _load_ml(name, comment) + ml = _load_ml(name, comment, group) components_cache = {(c.name, c.version): c for c in ml.components} components, unidentified_components = _match_bom(components_cache, bom) @@ -261,6 +263,16 @@ def component_request(state: tuple[str]): print() +@cli.command() +def groups(): + """List groups available on the current tenant. + + This lists the names of groups that are visible to the current membership. + """ + for group in Group.asc("name"): + print(group.name) + + def main(): try: exit_code = cli.main(standalone_mode=False) diff --git a/vilocify/models.py b/vilocify/models.py index c03dff6..75e64c2 100644 --- a/vilocify/models.py +++ b/vilocify/models.py @@ -114,3 +114,9 @@ class Subscription(Model): membership = RelationshipToOne(Membership) monitoring_list = RelationshipToOne(MonitoringList) + + +class Group(Model): + name = Attribute[str]("name") + created_at = Attribute[str]("createdAt") + updated_at = Attribute[str]("updatedAt")