From 854dcb00b534df21b6a1976c7a4e79fca613f246 Mon Sep 17 00:00:00 2001 From: Jeff Schnitter Date: Thu, 14 May 2026 14:39:57 -0700 Subject: [PATCH 1/5] fix: add __init__.py to trigger publish workflow for urllib3 update Co-Authored-By: Claude Opus 4.6 --- cortexapps_cli/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cortexapps_cli/__init__.py diff --git a/cortexapps_cli/__init__.py b/cortexapps_cli/__init__.py new file mode 100644 index 0000000..e69de29 From 37ef6d5332088fd0749d19acfdb61629a0ad47eb Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 14 May 2026 21:40:29 +0000 Subject: [PATCH 2/5] chore: update HISTORY.md for main --- HISTORY.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index ea61845..1781d17 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,33 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.16.0](https://github.com/cortexapps/cli/releases/tag/1.16.0) - 2026-05-14 + +[Compare with 1.15.0](https://github.com/cortexapps/cli/compare/1.15.0...1.16.0) + +### Features + +- shared minikube cluster, env prompting, cortex deploy recipes ([ff7c67d](https://github.com/cortexapps/cli/commit/ff7c67db11b47c32be7018b1f8660d9e7e148ed7) by Jeff Schnitter). +- add Cortex Helm values and env vars for minikube deploy ([23a8994](https://github.com/cortexapps/cli/commit/23a89943e20164c73630fdaee0071cd88cc553f9) by Jeff Schnitter). +- consolidate internal tooling — axon, k8s, prometheus, ui, functional tests ([1fd70fe](https://github.com/cortexapps/cli/commit/1fd70fe5b212516704e91e0022c10e6c6c3aed3c) by Jeff Schnitter). +- add ske-setup, ske-test, and ske-stop Justfile recipes ([6f56dca](https://github.com/cortexapps/cli/commit/6f56dca1afcd48ebc23fe3cf5a70bc2760b60257) by Jeff Schnitter). +- add SKE Helm values and ArgoCD Application manifest ([3d83ca5](https://github.com/cortexapps/cli/commit/3d83ca567861c64cea08d2dedee545fee0aff278) by Jeff Schnitter). +- add ConfigMap Promise for SKE Cortex integration test ([7942967](https://github.com/cortexapps/cli/commit/7942967b83eb121fbd2ec606a14dfd86c5a73036) by Jeff Schnitter). +- add SKE directory structure and env config ([dc0cd54](https://github.com/cortexapps/cli/commit/dc0cd54e6126bc4e5f3498db85f7cf862ea261c2) by Jeff Schnitter). + +### Bug Fixes + +- add __init__.py to trigger publish workflow for urllib3 update ([854dcb0](https://github.com/cortexapps/cli/commit/854dcb00b534df21b6a1976c7a4e79fca613f246) by Jeff Schnitter). +- update urllib3 to 2.7.0 to resolve high-severity CVEs ([904a144](https://github.com/cortexapps/cli/commit/904a144e02e65067f2029d85baa5efdf83c535f6) by Jeff Schnitter). +- prompt for env vars before starting minikube ([3da7a87](https://github.com/cortexapps/cli/commit/3da7a870853591f48f0eb3e80013e0d6573f9f62) by Jeff Schnitter). +- review fixes — quote .env values, fix pod selector, remove dead PID files ([4931dae](https://github.com/cortexapps/cli/commit/4931daed4e8a10ab6b9842d6aa70e6b4ce81c94b) by Jeff Schnitter). +- use external script for env prompting to fix just quoting bug ([5816d65](https://github.com/cortexapps/cli/commit/5816d65c7ca55834b7eb0a1e819b5192d2463853) by Jeff Schnitter). +- use correct Cortex API endpoint for Entity Type check in ske-test ([94f774e](https://github.com/cortexapps/cli/commit/94f774e43d7849175224794cfd8c8d7744b6c925) by Jeff Schnitter). +- wait for cert-manager webhook CA, let SKE auto-create Destination ([a4983fe](https://github.com/cortexapps/cli/commit/a4983fe75946c37e2eb582098a21754a10c498c8) by Jeff Schnitter). +- update SKE recipes with GitStateStore, Destination, and git-based resource requests ([8dad6be](https://github.com/cortexapps/cli/commit/8dad6bea12ff52011a2cbe1f15898d2eb91f9ad7) by Jeff Schnitter). +- correct SKE setup — add cert-manager, use latest version, fix integrationAlias ([4e1d2d7](https://github.com/cortexapps/cli/commit/4e1d2d74a096da9d6ff28e4cbedb7c6c50b4d8d6) by Jeff Schnitter). +- indent inline Python in ske-test recipe for just compatibility ([8a57c3c](https://github.com/cortexapps/cli/commit/8a57c3c21a10795baa1a59ca290af14f018ff813) by Jeff Schnitter). + ## [1.15.0](https://github.com/cortexapps/cli/releases/tag/1.15.0) - 2026-04-15 [Compare with 1.14.0](https://github.com/cortexapps/cli/compare/1.14.0...1.15.0) From ea614541e4c9be2d52ed2d4256fa672185480e30 Mon Sep 17 00:00:00 2001 From: Jeff Schnitter Date: Thu, 14 May 2026 14:50:03 -0700 Subject: [PATCH 3/5] docs: restrict feat:/fix: prefixes to CLI-deliverable changes only Commits touching only internal/, docs/superpowers/, or other non-deliverable paths must use chore: or docs: to avoid unintended version bumps from the github-tag-action. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index b0217e6..29db243 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -212,6 +212,8 @@ Commits use **conventional commit** prefixes which affect both versioning and ch Best practice: Use `feat:` for new features (will bump minor) and `fix:` for bug fixes (will bump patch). These prefixes satisfy both the version bumping and changelog generation. +**Important**: `feat:` and `fix:` must only be used for commits that affect the CLI deliverable (i.e., changes under `cortexapps_cli/`, `pyproject.toml`, `poetry.lock`, `tests/`, `docker/`). Commits that only touch non-deliverable paths (`internal/`, `docs/superpowers/`, Justfile recipes for internal tooling, etc.) must use `chore:` or `docs:` prefixes to avoid unintended version bumps. + ### HISTORY.md Merge Conflicts The `HISTORY.md` file is auto-generated when `staging` is merged to `main`. This means: - `main` always has the latest HISTORY.md From e1c706ebf36c738b4bcbb35c7dcff612ccb4e9a2 Mon Sep 17 00:00:00 2001 From: Jeff Schnitter Date: Wed, 20 May 2026 14:10:49 -0700 Subject: [PATCH 4/5] docs: add design spec for 32 new integration commands Co-Authored-By: Claude Opus 4.6 --- .../2026-05-20-new-integrations-design.md | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-20-new-integrations-design.md diff --git a/docs/superpowers/specs/2026-05-20-new-integrations-design.md b/docs/superpowers/specs/2026-05-20-new-integrations-design.md new file mode 100644 index 0000000..60c76a2 --- /dev/null +++ b/docs/superpowers/specs/2026-05-20-new-integrations-design.md @@ -0,0 +1,102 @@ +# Add 32 New Integration Commands + +**Date**: 2026-05-20 +**Version target**: 1.17.0 (minor bump via `feat:` prefix) + +## Summary + +Add CLI support for 32 integrations that have API endpoints but no CLI commands yet. Each integration gets its own command file following the existing patterns in `integrations_commands/`. + +## Two Patterns + +### Multi-config (alias-based) — 7 integrations + +Commands: `add`, `add-multiple`, `get`, `list`, `get-default`, `update`, `delete`, `delete-all`, `validate`, `validate-all` + +Modeled after existing `incidentio.py` / `pagerduty.py` pattern. + +| Integration | CLI name | API path | Add fields | +|---|---|---|---| +| Apiiro | `apiiro` | `apiiro` | alias, apiKey, isDefault | +| ArgoCD | `argocd` | `argocd` | alias, host, isDefault, password, username | +| Bitbucket | `bitbucket` | `bitbucket` | alias, isDefault, type + auth fields (4 types) | +| Jenkins | `jenkins` | `jenkins` | alias, apiKey, host, isDefault, username | +| Jira | `jira` | `jira` | alias, isDefault, type + auth fields (3 types) | +| Rootly | `rootly` | `rootly` | alias, apiKey, isDefault | +| Semgrep | `semgrep` | `semgrep` | alias, apiKey, isDefault, organizationId, organizationSlug | + +### Single-config — 25 integrations + +Commands: `add`, `get`, `update`, `validate`, `delete` + +No alias support. Simpler CRUD pattern. + +| Integration | CLI name | API path | Add fields | +|---|---|---|---| +| Azure AD (Entra ID) | `azure-active-directory` | `active-directory` | clientId, clientSecret, tenantId | +| BambooHR | `bamboohr` | `bamboohr` | apiToken, subdomain | +| Bugsnag | `bugsnag` | `bugsnag` | authToken, organizationId | +| Buildkite | `buildkite` | `buildkite` | apiToken, organizationSlug | +| Checkmarx SAST | `checkmarx-sast` | `checkmarx/sast` | username, password | +| ClickUp | `clickup` | `clickup` | personalApiToken | +| Codecov | `codecov` | `codecov` | apiToken | +| Dynatrace | `dynatrace` | `dynatrace` | apiKey, domain | +| Firehydrant | `firehydrant` | `firehydrant` | apiToken | +| Instana | `instana` | `instana` | apiToken, endpoint | +| Mend SAST | `mend-sast` | `mend/sast` | apiKey | +| Mend SCA | `mend-sca` | `mend/sca` | orgKey, userKey, orgType, urlType | +| Okta | `okta` | `okta` | apiToken, domain | +| Opsgenie | `opsgenie` | `opsgenie` | apiToken, isEu, subdomain | +| Rollbar | `rollbar` | `rollbar` | accessToken, organizationSlug | +| Sentry | `sentry` | `sentry` | authToken, organizationSlug, host (optional) | +| ServiceNow | `servicenow` | `servicenow` | instanceName, password, username | +| ServiceNow Cloud Obs | `servicenow-cloud-observability` | `lightstep` | authToken, organizationId, projectId | +| Snyk | `snyk` | `snyk` | authToken, region (enum: USA/US2/EU/AUS) | +| Splunk Obs Cloud | `splunk-observability-cloud` | `signalfx` | accessToken, realm | +| Splunk On Call | `splunk-on-call` | `victorops` | apiId, apiKey, organization | +| Sumo Logic | `sumo-logic` | `sumologic` | accessId, accessKey, deployment | +| Veracode | `veracode` | `veracode` | apiKey, keyId, region | +| Wiz | `wiz` | `wiz` | clientId, clientSecret, dataCenter, identityProvider | +| Workday | `workday` | `workday` | username, password, ownershipReportUrl | +| xMatters | `xmatters` | `xmatters` | organizationSlug, password, username | + +## Single-config command details + +- **`add`**: POST `/api/v1/{path}/configuration`. Accepts `--file/-f` for JSON input OR individual flags. Raises `BadParameter` if both provided. +- **`get`**: GET `/api/v1/{path}/default-configuration` +- **`update`**: PUT `/api/v1/{path}/configuration`. Same flags as add. +- **`validate`**: POST `/api/v1/{path}/configuration/validate` +- **`delete`**: DELETE `/api/v1/{path}/configurations` + +## Multi-config command details + +Same as existing multi-config integrations (incidentio, pagerduty, coralogix pattern): +- **`add`**: POST `/api/v1/{path}/configuration` with `--alias/-a`, `--is-default/-i`, integration-specific flags, or `--file/-f` +- **`add-multiple`**: POST `/api/v1/{path}/configurations` from `--file/-f` +- **`get`**: GET `/api/v1/{path}/configuration/{alias}` with `--alias/-a` +- **`list`**: GET `/api/v1/{path}/configurations` +- **`get-default`**: GET `/api/v1/{path}/default-configuration` +- **`update`**: PUT `/api/v1/{path}/configuration/{alias}` with `--alias/-a`, `--is-default/-i` +- **`delete`**: DELETE `/api/v1/{path}/configuration/{alias}` with `--alias/-a` +- **`delete-all`**: DELETE `/api/v1/{path}/configurations` +- **`validate`**: POST `/api/v1/{path}/configuration/validate/{alias}` with `--alias/-a` +- **`validate-all`**: POST `/api/v1/{path}/configuration/validate` + +## Special cases + +- **Bitbucket**: 4 auth types (cloud-atlassian, cloud-personal, on-prem-basic, workspace-token). Use `--file/-f` for add; CLI flags not practical for polymorphic types. +- **Jira**: 3 auth types (cloud-basic, cloud-scoped, on-prem-basic). Same approach — `--file/-f` for add. +- **Snyk**: Region enum (USA, US2, EU, AUS) +- **Veracode**: Region field (string, not enum — passed through) +- **Opsgenie**: `--is-eu` boolean flag +- **Mend**: Two separate sub-integrations (SAST and SCA) each get their own command file + +## File changes + +1. **32 new files** in `cortexapps_cli/commands/integrations_commands/` +2. **Update** `cortexapps_cli/commands/integrations.py` — add 32 imports and `add_typer()` calls +3. **32 new test files** in `tests/` following existing `test_integrations_*.py` pattern with `responses` mocking + +## Testing + +Each integration gets a test file with mocked HTTP responses (using `responses` library). Tests verify CLI invocation reaches the correct API endpoints with correct HTTP methods. No real API calls needed. From 58c8960c0359b25c49084a399f49dbb210a78507 Mon Sep 17 00:00:00 2001 From: Jeff Schnitter Date: Wed, 20 May 2026 14:23:19 -0700 Subject: [PATCH 5/5] feat: add CLI support for 32 new integrations Adds commands for: Apiiro, ArgoCD, Azure Active Directory, BambooHR, Bitbucket, Bugsnag, Buildkite, Checkmarx SAST, ClickUp, Codecov, Dynatrace, Firehydrant, Instana, Jenkins, Jira, Mend SAST, Mend SCA, Okta, Opsgenie, Rollbar, Rootly, Semgrep, Sentry, ServiceNow, ServiceNow Cloud Observability, Snyk, Splunk Observability Cloud, Splunk On Call, Sumo Logic, Veracode, Wiz, Workday, and xMatters. 7 multi-config (alias-based): Apiiro, ArgoCD, Bitbucket, Jenkins, Jira, Rootly, Semgrep with full CRUD + validate commands. 25 single-config: add, get, update, validate, delete commands. Closes #204 Co-Authored-By: Claude Opus 4.6 --- cortexapps_cli/commands/integrations.py | 66 +++++++ .../commands/integrations_commands/apiiro.py | 167 +++++++++++++++++ .../commands/integrations_commands/argocd.py | 171 ++++++++++++++++++ .../azure_active_directory.py | 89 +++++++++ .../integrations_commands/bamboohr.py | 85 +++++++++ .../integrations_commands/bitbucket.py | 152 ++++++++++++++++ .../commands/integrations_commands/bugsnag.py | 85 +++++++++ .../integrations_commands/buildkite.py | 85 +++++++++ .../integrations_commands/checkmarx_sast.py | 85 +++++++++ .../commands/integrations_commands/clickup.py | 81 +++++++++ .../commands/integrations_commands/codecov.py | 81 +++++++++ .../integrations_commands/dynatrace.py | 85 +++++++++ .../integrations_commands/firehydrant.py | 81 +++++++++ .../commands/integrations_commands/instana.py | 85 +++++++++ .../commands/integrations_commands/jenkins.py | 171 ++++++++++++++++++ .../commands/integrations_commands/jira.py | 152 ++++++++++++++++ .../integrations_commands/mend_sast.py | 81 +++++++++ .../integrations_commands/mend_sca.py | 93 ++++++++++ .../commands/integrations_commands/okta.py | 85 +++++++++ .../integrations_commands/opsgenie.py | 89 +++++++++ .../commands/integrations_commands/rollbar.py | 85 +++++++++ .../commands/integrations_commands/rootly.py | 167 +++++++++++++++++ .../commands/integrations_commands/semgrep.py | 171 ++++++++++++++++++ .../commands/integrations_commands/sentry.py | 89 +++++++++ .../integrations_commands/servicenow.py | 89 +++++++++ .../servicenow_cloud_observability.py | 89 +++++++++ .../commands/integrations_commands/snyk.py | 92 ++++++++++ .../splunk_observability_cloud.py | 85 +++++++++ .../integrations_commands/splunk_on_call.py | 89 +++++++++ .../integrations_commands/sumologic.py | 89 +++++++++ .../integrations_commands/veracode.py | 89 +++++++++ .../commands/integrations_commands/wiz.py | 93 ++++++++++ .../commands/integrations_commands/workday.py | 89 +++++++++ .../integrations_commands/xmatters.py | 89 +++++++++ tests/test_integrations_apiiro.py | 59 ++++++ tests/test_integrations_argocd.py | 59 ++++++ ...est_integrations_azure_active_directory.py | 35 ++++ tests/test_integrations_bamboohr.py | 35 ++++ tests/test_integrations_bitbucket.py | 60 ++++++ tests/test_integrations_bugsnag.py | 35 ++++ tests/test_integrations_buildkite.py | 35 ++++ tests/test_integrations_checkmarx_sast.py | 35 ++++ tests/test_integrations_clickup.py | 35 ++++ tests/test_integrations_codecov.py | 35 ++++ tests/test_integrations_dynatrace.py | 35 ++++ tests/test_integrations_firehydrant.py | 35 ++++ tests/test_integrations_instana.py | 35 ++++ tests/test_integrations_jenkins.py | 59 ++++++ tests/test_integrations_jira.py | 60 ++++++ tests/test_integrations_mend_sast.py | 35 ++++ tests/test_integrations_mend_sca.py | 35 ++++ tests/test_integrations_okta.py | 35 ++++ tests/test_integrations_opsgenie.py | 35 ++++ tests/test_integrations_rollbar.py | 35 ++++ tests/test_integrations_rootly.py | 59 ++++++ tests/test_integrations_semgrep.py | 59 ++++++ tests/test_integrations_sentry.py | 35 ++++ tests/test_integrations_servicenow.py | 35 ++++ ...grations_servicenow_cloud_observability.py | 35 ++++ tests/test_integrations_snyk.py | 35 ++++ ...integrations_splunk_observability_cloud.py | 35 ++++ tests/test_integrations_splunk_on_call.py | 35 ++++ tests/test_integrations_sumologic.py | 35 ++++ tests/test_integrations_veracode.py | 35 ++++ tests/test_integrations_wiz.py | 35 ++++ tests/test_integrations_workday.py | 35 ++++ tests/test_integrations_xmatters.py | 35 ++++ 67 files changed, 4799 insertions(+) create mode 100644 cortexapps_cli/commands/integrations_commands/apiiro.py create mode 100644 cortexapps_cli/commands/integrations_commands/argocd.py create mode 100644 cortexapps_cli/commands/integrations_commands/azure_active_directory.py create mode 100644 cortexapps_cli/commands/integrations_commands/bamboohr.py create mode 100644 cortexapps_cli/commands/integrations_commands/bitbucket.py create mode 100644 cortexapps_cli/commands/integrations_commands/bugsnag.py create mode 100644 cortexapps_cli/commands/integrations_commands/buildkite.py create mode 100644 cortexapps_cli/commands/integrations_commands/checkmarx_sast.py create mode 100644 cortexapps_cli/commands/integrations_commands/clickup.py create mode 100644 cortexapps_cli/commands/integrations_commands/codecov.py create mode 100644 cortexapps_cli/commands/integrations_commands/dynatrace.py create mode 100644 cortexapps_cli/commands/integrations_commands/firehydrant.py create mode 100644 cortexapps_cli/commands/integrations_commands/instana.py create mode 100644 cortexapps_cli/commands/integrations_commands/jenkins.py create mode 100644 cortexapps_cli/commands/integrations_commands/jira.py create mode 100644 cortexapps_cli/commands/integrations_commands/mend_sast.py create mode 100644 cortexapps_cli/commands/integrations_commands/mend_sca.py create mode 100644 cortexapps_cli/commands/integrations_commands/okta.py create mode 100644 cortexapps_cli/commands/integrations_commands/opsgenie.py create mode 100644 cortexapps_cli/commands/integrations_commands/rollbar.py create mode 100644 cortexapps_cli/commands/integrations_commands/rootly.py create mode 100644 cortexapps_cli/commands/integrations_commands/semgrep.py create mode 100644 cortexapps_cli/commands/integrations_commands/sentry.py create mode 100644 cortexapps_cli/commands/integrations_commands/servicenow.py create mode 100644 cortexapps_cli/commands/integrations_commands/servicenow_cloud_observability.py create mode 100644 cortexapps_cli/commands/integrations_commands/snyk.py create mode 100644 cortexapps_cli/commands/integrations_commands/splunk_observability_cloud.py create mode 100644 cortexapps_cli/commands/integrations_commands/splunk_on_call.py create mode 100644 cortexapps_cli/commands/integrations_commands/sumologic.py create mode 100644 cortexapps_cli/commands/integrations_commands/veracode.py create mode 100644 cortexapps_cli/commands/integrations_commands/wiz.py create mode 100644 cortexapps_cli/commands/integrations_commands/workday.py create mode 100644 cortexapps_cli/commands/integrations_commands/xmatters.py create mode 100644 tests/test_integrations_apiiro.py create mode 100644 tests/test_integrations_argocd.py create mode 100644 tests/test_integrations_azure_active_directory.py create mode 100644 tests/test_integrations_bamboohr.py create mode 100644 tests/test_integrations_bitbucket.py create mode 100644 tests/test_integrations_bugsnag.py create mode 100644 tests/test_integrations_buildkite.py create mode 100644 tests/test_integrations_checkmarx_sast.py create mode 100644 tests/test_integrations_clickup.py create mode 100644 tests/test_integrations_codecov.py create mode 100644 tests/test_integrations_dynatrace.py create mode 100644 tests/test_integrations_firehydrant.py create mode 100644 tests/test_integrations_instana.py create mode 100644 tests/test_integrations_jenkins.py create mode 100644 tests/test_integrations_jira.py create mode 100644 tests/test_integrations_mend_sast.py create mode 100644 tests/test_integrations_mend_sca.py create mode 100644 tests/test_integrations_okta.py create mode 100644 tests/test_integrations_opsgenie.py create mode 100644 tests/test_integrations_rollbar.py create mode 100644 tests/test_integrations_rootly.py create mode 100644 tests/test_integrations_semgrep.py create mode 100644 tests/test_integrations_sentry.py create mode 100644 tests/test_integrations_servicenow.py create mode 100644 tests/test_integrations_servicenow_cloud_observability.py create mode 100644 tests/test_integrations_snyk.py create mode 100644 tests/test_integrations_splunk_observability_cloud.py create mode 100644 tests/test_integrations_splunk_on_call.py create mode 100644 tests/test_integrations_sumologic.py create mode 100644 tests/test_integrations_veracode.py create mode 100644 tests/test_integrations_wiz.py create mode 100644 tests/test_integrations_workday.py create mode 100644 tests/test_integrations_xmatters.py diff --git a/cortexapps_cli/commands/integrations.py b/cortexapps_cli/commands/integrations.py index 5e2f7c7..0586928 100644 --- a/cortexapps_cli/commands/integrations.py +++ b/cortexapps_cli/commands/integrations.py @@ -3,33 +3,99 @@ import typer from typing_extensions import Annotated +import cortexapps_cli.commands.integrations_commands.apiiro as apiiro +import cortexapps_cli.commands.integrations_commands.argocd as argocd import cortexapps_cli.commands.integrations_commands.aws as aws +import cortexapps_cli.commands.integrations_commands.azure_active_directory as azure_active_directory import cortexapps_cli.commands.integrations_commands.azure_resources as azure_resources import cortexapps_cli.commands.integrations_commands.azure_devops as azure_devops +import cortexapps_cli.commands.integrations_commands.bamboohr as bamboohr +import cortexapps_cli.commands.integrations_commands.bitbucket as bitbucket +import cortexapps_cli.commands.integrations_commands.bugsnag as bugsnag +import cortexapps_cli.commands.integrations_commands.buildkite as buildkite +import cortexapps_cli.commands.integrations_commands.checkmarx_sast as checkmarx_sast import cortexapps_cli.commands.integrations_commands.circleci as circleci +import cortexapps_cli.commands.integrations_commands.clickup as clickup +import cortexapps_cli.commands.integrations_commands.codecov as codecov import cortexapps_cli.commands.integrations_commands.coralogix as coralogix import cortexapps_cli.commands.integrations_commands.datadog as datadog +import cortexapps_cli.commands.integrations_commands.dynatrace as dynatrace +import cortexapps_cli.commands.integrations_commands.firehydrant as firehydrant import cortexapps_cli.commands.integrations_commands.github as github import cortexapps_cli.commands.integrations_commands.gitlab as gitlab import cortexapps_cli.commands.integrations_commands.incidentio as incidentio +import cortexapps_cli.commands.integrations_commands.instana as instana +import cortexapps_cli.commands.integrations_commands.jenkins as jenkins +import cortexapps_cli.commands.integrations_commands.jira as jira import cortexapps_cli.commands.integrations_commands.launchdarkly as launchdarkly +import cortexapps_cli.commands.integrations_commands.mend_sast as mend_sast +import cortexapps_cli.commands.integrations_commands.mend_sca as mend_sca import cortexapps_cli.commands.integrations_commands.newrelic as newrelic +import cortexapps_cli.commands.integrations_commands.okta as okta +import cortexapps_cli.commands.integrations_commands.opsgenie as opsgenie import cortexapps_cli.commands.integrations_commands.pagerduty as pagerduty import cortexapps_cli.commands.integrations_commands.prometheus as prometheus +import cortexapps_cli.commands.integrations_commands.rollbar as rollbar +import cortexapps_cli.commands.integrations_commands.rootly as rootly +import cortexapps_cli.commands.integrations_commands.semgrep as semgrep +import cortexapps_cli.commands.integrations_commands.sentry as sentry +import cortexapps_cli.commands.integrations_commands.servicenow as servicenow +import cortexapps_cli.commands.integrations_commands.servicenow_cloud_observability as servicenow_cloud_observability +import cortexapps_cli.commands.integrations_commands.snyk as snyk import cortexapps_cli.commands.integrations_commands.sonarqube as sonarqube +import cortexapps_cli.commands.integrations_commands.splunk_observability_cloud as splunk_observability_cloud +import cortexapps_cli.commands.integrations_commands.splunk_on_call as splunk_on_call +import cortexapps_cli.commands.integrations_commands.sumologic as sumologic +import cortexapps_cli.commands.integrations_commands.veracode as veracode +import cortexapps_cli.commands.integrations_commands.wiz as wiz +import cortexapps_cli.commands.integrations_commands.workday as workday +import cortexapps_cli.commands.integrations_commands.xmatters as xmatters app = typer.Typer(help="Integrations commands", no_args_is_help=True) +app.add_typer(apiiro.app, name="apiiro") +app.add_typer(argocd.app, name="argocd") app.add_typer(aws.app, name="aws") +app.add_typer(azure_active_directory.app, name="azure-active-directory") app.add_typer(azure_resources.app, name="azure-resources") app.add_typer(azure_devops.app, name="azure-devops") +app.add_typer(bamboohr.app, name="bamboohr") +app.add_typer(bitbucket.app, name="bitbucket") +app.add_typer(bugsnag.app, name="bugsnag") +app.add_typer(buildkite.app, name="buildkite") +app.add_typer(checkmarx_sast.app, name="checkmarx-sast") app.add_typer(circleci.app, name="circleci") +app.add_typer(clickup.app, name="clickup") +app.add_typer(codecov.app, name="codecov") app.add_typer(coralogix.app, name="coralogix") app.add_typer(datadog.app, name="datadog") +app.add_typer(dynatrace.app, name="dynatrace") +app.add_typer(firehydrant.app, name="firehydrant") app.add_typer(github.app, name="github") app.add_typer(gitlab.app, name="gitlab") app.add_typer(incidentio.app, name="incidentio") +app.add_typer(instana.app, name="instana") +app.add_typer(jenkins.app, name="jenkins") +app.add_typer(jira.app, name="jira") app.add_typer(launchdarkly.app, name="launchdarkly") +app.add_typer(mend_sast.app, name="mend-sast") +app.add_typer(mend_sca.app, name="mend-sca") app.add_typer(newrelic.app, name="newrelic") +app.add_typer(okta.app, name="okta") +app.add_typer(opsgenie.app, name="opsgenie") app.add_typer(pagerduty.app, name="pagerduty") app.add_typer(prometheus.app, name="prometheus") +app.add_typer(rollbar.app, name="rollbar") +app.add_typer(rootly.app, name="rootly") +app.add_typer(semgrep.app, name="semgrep") +app.add_typer(sentry.app, name="sentry") +app.add_typer(servicenow.app, name="servicenow") +app.add_typer(servicenow_cloud_observability.app, name="servicenow-cloud-observability") +app.add_typer(snyk.app, name="snyk") app.add_typer(sonarqube.app, name="sonarqube") +app.add_typer(splunk_observability_cloud.app, name="splunk-observability-cloud") +app.add_typer(splunk_on_call.app, name="splunk-on-call") +app.add_typer(sumologic.app, name="sumo-logic") +app.add_typer(veracode.app, name="veracode") +app.add_typer(wiz.app, name="wiz") +app.add_typer(workday.app, name="workday") +app.add_typer(xmatters.app, name="xmatters") diff --git a/cortexapps_cli/commands/integrations_commands/apiiro.py b/cortexapps_cli/commands/integrations_commands/apiiro.py new file mode 100644 index 0000000..48b0207 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/apiiro.py @@ -0,0 +1,167 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Apiiro commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="Alias for this configuration"), + api_key: str = typer.Option(..., "--api-key", "-api", help="API key"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a single configuration + """ + + client = ctx.obj["client"] + + if file_input: + if alias or api_key or is_default: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "alias": alias, + "apiKey": api_key, + "isDefault": is_default, + } + + # remove any data elements that are None + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/apiiro/configuration", data=data) + print_json(data=r) + +@app.command() +def add_multiple( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add multiple configurations + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.put("api/v1/apiiro/configurations", data=data) + print_json(data=r) + +@app.command() +def delete( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Delete a configuration + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/apiiro/configuration/" + alias) + print_json(data=r) + +@app.command() +def delete_all( + ctx: typer.Context, +): + """ + Delete all configurations + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/apiiro/configurations") + print_json(data=r) + +@app.command() +def get( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Get a configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/apiiro/configuration/" + alias) + print_json(data=r) + +@app.command() +def list( + ctx: typer.Context, +): + """ + Get all configurations + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/apiiro/configurations") + print_json(data=r) + +@app.command() +def get_default( + ctx: typer.Context, +): + """ + Get default configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/apiiro/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), +): + """ + Update a configuration + """ + + client = ctx.obj["client"] + + data = { + "alias": alias, + "isDefault": is_default + } + + r = client.put("api/v1/apiiro/configuration/" + alias, data=data) + print_json(data=r) + +@app.command() +def validate( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Validate a configuration + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/apiiro/configuration/validate/" + alias) + print_json(data=r) + +@app.command() +def validate_all( + ctx: typer.Context, +): + """ + Validate all configurations + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/apiiro/configuration/validate") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/argocd.py b/cortexapps_cli/commands/integrations_commands/argocd.py new file mode 100644 index 0000000..58801dc --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/argocd.py @@ -0,0 +1,171 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="ArgoCD commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="Alias for this configuration"), + host: str = typer.Option(..., "--host", "-h", help="Host name"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), + password: str = typer.Option(..., "--password", "-p", help="Password"), + username: str = typer.Option(..., "--username", "-u", help="Username"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a single configuration + """ + + client = ctx.obj["client"] + + if file_input: + if alias or host or is_default or password or username: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "alias": alias, + "host": host, + "isDefault": is_default, + "password": password, + "username": username, + } + + # remove any data elements that are None + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/argocd/configuration", data=data) + print_json(data=r) + +@app.command() +def add_multiple( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add multiple configurations + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.put("api/v1/argocd/configurations", data=data) + print_json(data=r) + +@app.command() +def delete( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Delete a configuration + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/argocd/configuration/" + alias) + print_json(data=r) + +@app.command() +def delete_all( + ctx: typer.Context, +): + """ + Delete all configurations + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/argocd/configurations") + print_json(data=r) + +@app.command() +def get( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Get a configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/argocd/configuration/" + alias) + print_json(data=r) + +@app.command() +def list( + ctx: typer.Context, +): + """ + Get all configurations + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/argocd/configurations") + print_json(data=r) + +@app.command() +def get_default( + ctx: typer.Context, +): + """ + Get default configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/argocd/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), +): + """ + Update a configuration + """ + + client = ctx.obj["client"] + + data = { + "alias": alias, + "isDefault": is_default + } + + r = client.put("api/v1/argocd/configuration/" + alias, data=data) + print_json(data=r) + +@app.command() +def validate( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Validate a configuration + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/argocd/configuration/validate/" + alias) + print_json(data=r) + +@app.command() +def validate_all( + ctx: typer.Context, +): + """ + Validate all configurations + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/argocd/configuration/validate") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/azure_active_directory.py b/cortexapps_cli/commands/integrations_commands/azure_active_directory.py new file mode 100644 index 0000000..1003912 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/azure_active_directory.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Azure Active Directory commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + client_id: str = typer.Option(None, "--client-id", "-ci", help="Client ID"), + client_secret: str = typer.Option(None, "--client-secret", "-cs", help="Client secret"), + tenant_id: str = typer.Option(None, "--tenant-id", "-ti", help="Tenant ID"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if client_id or client_secret or tenant_id: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "clientId": client_id, + "clientSecret": client_secret, + "tenantId": tenant_id, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/active-directory/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/active-directory/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + client_id: str = typer.Option(None, "--client-id", "-ci", help="Client ID"), + client_secret: str = typer.Option(None, "--client-secret", "-cs", help="Client secret"), + tenant_id: str = typer.Option(None, "--tenant-id", "-ti", help="Tenant ID"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if client_id or client_secret or tenant_id: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "clientId": client_id, + "clientSecret": client_secret, + "tenantId": tenant_id, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/active-directory/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/active-directory/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/active-directory/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/bamboohr.py b/cortexapps_cli/commands/integrations_commands/bamboohr.py new file mode 100644 index 0000000..0e4e43a --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/bamboohr.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="BambooHR commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + subdomain: str = typer.Option(None, "--subdomain", "-s", help="Subdomain"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or subdomain: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "subdomain": subdomain, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/bamboohr/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/bamboohr/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + subdomain: str = typer.Option(None, "--subdomain", "-s", help="Subdomain"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or subdomain: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "subdomain": subdomain, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/bamboohr/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/bamboohr/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/bamboohr/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/bitbucket.py b/cortexapps_cli/commands/integrations_commands/bitbucket.py new file mode 100644 index 0000000..bf8e619 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/bitbucket.py @@ -0,0 +1,152 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Bitbucket commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a single configuration (use --file for JSON input) + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.post("api/v1/bitbucket/configuration", data=data) + print_json(data=r) + +@app.command() +def add_multiple( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add multiple configurations + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.put("api/v1/bitbucket/configurations", data=data) + print_json(data=r) + +@app.command() +def delete( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Delete a configuration + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/bitbucket/configuration/" + alias) + print_json(data=r) + +@app.command() +def delete_all( + ctx: typer.Context, +): + """ + Delete all configurations + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/bitbucket/configurations") + print_json(data=r) + +@app.command() +def get( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Get a configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/bitbucket/configuration/" + alias) + print_json(data=r) + +@app.command() +def list( + ctx: typer.Context, +): + """ + Get all configurations + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/bitbucket/configurations") + print_json(data=r) + +@app.command() +def get_default( + ctx: typer.Context, +): + """ + Get default configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/bitbucket/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), +): + """ + Update a configuration + """ + + client = ctx.obj["client"] + + data = { + "alias": alias, + "isDefault": is_default + } + + r = client.put("api/v1/bitbucket/configuration/" + alias, data=data) + print_json(data=r) + +@app.command() +def validate( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Validate a configuration + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/bitbucket/configuration/validate/" + alias) + print_json(data=r) + +@app.command() +def validate_all( + ctx: typer.Context, +): + """ + Validate all configurations + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/bitbucket/configuration/validate") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/bugsnag.py b/cortexapps_cli/commands/integrations_commands/bugsnag.py new file mode 100644 index 0000000..a15d226 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/bugsnag.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Bugsnag commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + auth_token: str = typer.Option(None, "--auth-token", "-at", help="Auth token"), + organization_id: str = typer.Option(None, "--organization-id", "-o", help="Organization ID"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if auth_token or organization_id: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "authToken": auth_token, + "organizationId": organization_id, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/bugsnag/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/bugsnag/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + auth_token: str = typer.Option(None, "--auth-token", "-at", help="Auth token"), + organization_id: str = typer.Option(None, "--organization-id", "-o", help="Organization ID"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if auth_token or organization_id: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "authToken": auth_token, + "organizationId": organization_id, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/bugsnag/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/bugsnag/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/bugsnag/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/buildkite.py b/cortexapps_cli/commands/integrations_commands/buildkite.py new file mode 100644 index 0000000..c5b0b31 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/buildkite.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Buildkite commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + organization_slug: str = typer.Option(None, "--organization-slug", "-o", help="Organization slug"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or organization_slug: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "organizationSlug": organization_slug, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/buildkite/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/buildkite/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + organization_slug: str = typer.Option(None, "--organization-slug", "-o", help="Organization slug"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or organization_slug: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "organizationSlug": organization_slug, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/buildkite/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/buildkite/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/buildkite/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/checkmarx_sast.py b/cortexapps_cli/commands/integrations_commands/checkmarx_sast.py new file mode 100644 index 0000000..75d1258 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/checkmarx_sast.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Checkmarx SAST commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + username: str = typer.Option(None, "--username", "-u", help="Username"), + password: str = typer.Option(None, "--password", "-p", help="Password"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if username or password: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "username": username, + "password": password, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/checkmarx/sast/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/checkmarx/sast/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + username: str = typer.Option(None, "--username", "-u", help="Username"), + password: str = typer.Option(None, "--password", "-p", help="Password"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if username or password: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "username": username, + "password": password, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/checkmarx/sast/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/checkmarx/sast/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/checkmarx/sast/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/clickup.py b/cortexapps_cli/commands/integrations_commands/clickup.py new file mode 100644 index 0000000..f787925 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/clickup.py @@ -0,0 +1,81 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="ClickUp commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + personal_api_token: str = typer.Option(None, "--personal-api-token", "-pat", help="Personal API token"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if personal_api_token: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "personalApiToken": personal_api_token, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/clickup/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/clickup/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + personal_api_token: str = typer.Option(None, "--personal-api-token", "-pat", help="Personal API token"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if personal_api_token: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "personalApiToken": personal_api_token, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/clickup/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/clickup/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/clickup/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/codecov.py b/cortexapps_cli/commands/integrations_commands/codecov.py new file mode 100644 index 0000000..e39a914 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/codecov.py @@ -0,0 +1,81 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Codecov commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/codecov/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/codecov/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/codecov/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/codecov/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/codecov/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/dynatrace.py b/cortexapps_cli/commands/integrations_commands/dynatrace.py new file mode 100644 index 0000000..cfbe596 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/dynatrace.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Dynatrace commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_key: str = typer.Option(None, "--api-key", "-api", help="API key"), + domain: str = typer.Option(None, "--domain", "-d", help="Domain"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_key or domain: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiKey": api_key, + "domain": domain, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/dynatrace/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/dynatrace/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_key: str = typer.Option(None, "--api-key", "-api", help="API key"), + domain: str = typer.Option(None, "--domain", "-d", help="Domain"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_key or domain: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiKey": api_key, + "domain": domain, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/dynatrace/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/dynatrace/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/dynatrace/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/firehydrant.py b/cortexapps_cli/commands/integrations_commands/firehydrant.py new file mode 100644 index 0000000..245ca59 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/firehydrant.py @@ -0,0 +1,81 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Firehydrant commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/firehydrant/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/firehydrant/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/firehydrant/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/firehydrant/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/firehydrant/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/instana.py b/cortexapps_cli/commands/integrations_commands/instana.py new file mode 100644 index 0000000..61ac3fc --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/instana.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Instana commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + endpoint: str = typer.Option(None, "--endpoint", "-e", help="Endpoint"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or endpoint: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "endpoint": endpoint, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/instana/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/instana/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + endpoint: str = typer.Option(None, "--endpoint", "-e", help="Endpoint"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or endpoint: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "endpoint": endpoint, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/instana/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/instana/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/instana/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/jenkins.py b/cortexapps_cli/commands/integrations_commands/jenkins.py new file mode 100644 index 0000000..fa5ddec --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/jenkins.py @@ -0,0 +1,171 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Jenkins commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="Alias for this configuration"), + api_key: str = typer.Option(..., "--api-key", "-api", help="API key"), + host: str = typer.Option(..., "--host", "-h", help="Host name"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), + username: str = typer.Option(..., "--username", "-u", help="Username"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a single configuration + """ + + client = ctx.obj["client"] + + if file_input: + if alias or api_key or host or is_default or username: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "alias": alias, + "apiKey": api_key, + "host": host, + "isDefault": is_default, + "username": username, + } + + # remove any data elements that are None + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/jenkins/configuration", data=data) + print_json(data=r) + +@app.command() +def add_multiple( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add multiple configurations + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.put("api/v1/jenkins/configurations", data=data) + print_json(data=r) + +@app.command() +def delete( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Delete a configuration + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/jenkins/configuration/" + alias) + print_json(data=r) + +@app.command() +def delete_all( + ctx: typer.Context, +): + """ + Delete all configurations + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/jenkins/configurations") + print_json(data=r) + +@app.command() +def get( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Get a configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/jenkins/configuration/" + alias) + print_json(data=r) + +@app.command() +def list( + ctx: typer.Context, +): + """ + Get all configurations + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/jenkins/configurations") + print_json(data=r) + +@app.command() +def get_default( + ctx: typer.Context, +): + """ + Get default configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/jenkins/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), +): + """ + Update a configuration + """ + + client = ctx.obj["client"] + + data = { + "alias": alias, + "isDefault": is_default + } + + r = client.put("api/v1/jenkins/configuration/" + alias, data=data) + print_json(data=r) + +@app.command() +def validate( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Validate a configuration + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/jenkins/configuration/validate/" + alias) + print_json(data=r) + +@app.command() +def validate_all( + ctx: typer.Context, +): + """ + Validate all configurations + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/jenkins/configuration/validate") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/jira.py b/cortexapps_cli/commands/integrations_commands/jira.py new file mode 100644 index 0000000..ef91741 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/jira.py @@ -0,0 +1,152 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Jira commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a single configuration (use --file for JSON input) + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.post("api/v1/jira/configuration", data=data) + print_json(data=r) + +@app.command() +def add_multiple( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add multiple configurations + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.put("api/v1/jira/configurations", data=data) + print_json(data=r) + +@app.command() +def delete( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Delete a configuration + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/jira/configuration/" + alias) + print_json(data=r) + +@app.command() +def delete_all( + ctx: typer.Context, +): + """ + Delete all configurations + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/jira/configurations") + print_json(data=r) + +@app.command() +def get( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Get a configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/jira/configuration/" + alias) + print_json(data=r) + +@app.command() +def list( + ctx: typer.Context, +): + """ + Get all configurations + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/jira/configurations") + print_json(data=r) + +@app.command() +def get_default( + ctx: typer.Context, +): + """ + Get default configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/jira/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), +): + """ + Update a configuration + """ + + client = ctx.obj["client"] + + data = { + "alias": alias, + "isDefault": is_default + } + + r = client.put("api/v1/jira/configuration/" + alias, data=data) + print_json(data=r) + +@app.command() +def validate( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Validate a configuration + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/jira/configuration/validate/" + alias) + print_json(data=r) + +@app.command() +def validate_all( + ctx: typer.Context, +): + """ + Validate all configurations + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/jira/configuration/validate") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/mend_sast.py b/cortexapps_cli/commands/integrations_commands/mend_sast.py new file mode 100644 index 0000000..1279ff2 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/mend_sast.py @@ -0,0 +1,81 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Mend SAST commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_key: str = typer.Option(None, "--api-key", "-api", help="API key"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_key: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiKey": api_key, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/mend/sast/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/mend/sast/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_key: str = typer.Option(None, "--api-key", "-api", help="API key"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_key: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiKey": api_key, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/mend/sast/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/mend/sast/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/mend/sast/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/mend_sca.py b/cortexapps_cli/commands/integrations_commands/mend_sca.py new file mode 100644 index 0000000..d32da10 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/mend_sca.py @@ -0,0 +1,93 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Mend SCA commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + org_key: str = typer.Option(None, "--org-key", "-ok", help="Organization key"), + user_key: str = typer.Option(None, "--user-key", "-uk", help="User key"), + org_type: str = typer.Option(None, "--org-type", "-ot", help="Organization type"), + url_type: str = typer.Option(None, "--url-type", "-ut", help="URL type"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if org_key or user_key or org_type or url_type: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "orgKey": org_key, + "userKey": user_key, + "orgType": org_type, + "urlType": url_type, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/mend/sca/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/mend/sca/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + org_key: str = typer.Option(None, "--org-key", "-ok", help="Organization key"), + user_key: str = typer.Option(None, "--user-key", "-uk", help="User key"), + org_type: str = typer.Option(None, "--org-type", "-ot", help="Organization type"), + url_type: str = typer.Option(None, "--url-type", "-ut", help="URL type"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if org_key or user_key or org_type or url_type: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "orgKey": org_key, + "userKey": user_key, + "orgType": org_type, + "urlType": url_type, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/mend/sca/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/mend/sca/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/mend/sca/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/okta.py b/cortexapps_cli/commands/integrations_commands/okta.py new file mode 100644 index 0000000..0aa5fa4 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/okta.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Okta commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + domain: str = typer.Option(None, "--domain", "-d", help="Domain"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or domain: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "domain": domain, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/okta/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/okta/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + domain: str = typer.Option(None, "--domain", "-d", help="Domain"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or domain: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "domain": domain, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/okta/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/okta/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/okta/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/opsgenie.py b/cortexapps_cli/commands/integrations_commands/opsgenie.py new file mode 100644 index 0000000..51d9acb --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/opsgenie.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Opsgenie commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + is_eu: bool = typer.Option(False, "--is-eu", "-eu", help="If the Opsgenie instance is in the EU region"), + subdomain: str = typer.Option(None, "--subdomain", "-s", help="Subdomain"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or subdomain: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "isEu": is_eu, + "subdomain": subdomain, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/opsgenie/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/opsgenie/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_token: str = typer.Option(None, "--api-token", "-at", help="API token"), + is_eu: bool = typer.Option(False, "--is-eu", "-eu", help="If the Opsgenie instance is in the EU region"), + subdomain: str = typer.Option(None, "--subdomain", "-s", help="Subdomain"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_token or subdomain: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiToken": api_token, + "isEu": is_eu, + "subdomain": subdomain, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/opsgenie/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/opsgenie/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/opsgenie/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/rollbar.py b/cortexapps_cli/commands/integrations_commands/rollbar.py new file mode 100644 index 0000000..1a98f31 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/rollbar.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Rollbar commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + access_token: str = typer.Option(None, "--access-token", "-at", help="Access token"), + organization_slug: str = typer.Option(None, "--organization-slug", "-o", help="Organization slug"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if access_token or organization_slug: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "accessToken": access_token, + "organizationSlug": organization_slug, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/rollbar/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/rollbar/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + access_token: str = typer.Option(None, "--access-token", "-at", help="Access token"), + organization_slug: str = typer.Option(None, "--organization-slug", "-o", help="Organization slug"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if access_token or organization_slug: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "accessToken": access_token, + "organizationSlug": organization_slug, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/rollbar/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/rollbar/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/rollbar/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/rootly.py b/cortexapps_cli/commands/integrations_commands/rootly.py new file mode 100644 index 0000000..84426ac --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/rootly.py @@ -0,0 +1,167 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Rootly commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="Alias for this configuration"), + api_key: str = typer.Option(..., "--api-key", "-api", help="API key"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a single configuration + """ + + client = ctx.obj["client"] + + if file_input: + if alias or api_key or is_default: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "alias": alias, + "apiKey": api_key, + "isDefault": is_default, + } + + # remove any data elements that are None + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/rootly/configuration", data=data) + print_json(data=r) + +@app.command() +def add_multiple( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add multiple configurations + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.put("api/v1/rootly/configurations", data=data) + print_json(data=r) + +@app.command() +def delete( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Delete a configuration + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/rootly/configuration/" + alias) + print_json(data=r) + +@app.command() +def delete_all( + ctx: typer.Context, +): + """ + Delete all configurations + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/rootly/configurations") + print_json(data=r) + +@app.command() +def get( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Get a configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/rootly/configuration/" + alias) + print_json(data=r) + +@app.command() +def list( + ctx: typer.Context, +): + """ + Get all configurations + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/rootly/configurations") + print_json(data=r) + +@app.command() +def get_default( + ctx: typer.Context, +): + """ + Get default configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/rootly/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), +): + """ + Update a configuration + """ + + client = ctx.obj["client"] + + data = { + "alias": alias, + "isDefault": is_default + } + + r = client.put("api/v1/rootly/configuration/" + alias, data=data) + print_json(data=r) + +@app.command() +def validate( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Validate a configuration + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/rootly/configuration/validate/" + alias) + print_json(data=r) + +@app.command() +def validate_all( + ctx: typer.Context, +): + """ + Validate all configurations + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/rootly/configuration/validate") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/semgrep.py b/cortexapps_cli/commands/integrations_commands/semgrep.py new file mode 100644 index 0000000..5186f75 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/semgrep.py @@ -0,0 +1,171 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Semgrep commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="Alias for this configuration"), + api_key: str = typer.Option(..., "--api-key", "-api", help="API key"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), + organization_id: str = typer.Option(..., "--organization-id", "-oi", help="Organization ID"), + organization_slug: str = typer.Option(..., "--organization-slug", "-os", help="Organization slug"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a single configuration + """ + + client = ctx.obj["client"] + + if file_input: + if alias or api_key or is_default or organization_id or organization_slug: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "alias": alias, + "apiKey": api_key, + "isDefault": is_default, + "organizationId": organization_id, + "organizationSlug": organization_slug, + } + + # remove any data elements that are None + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/semgrep/configuration", data=data) + print_json(data=r) + +@app.command() +def add_multiple( + ctx: typer.Context, + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configurations; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add multiple configurations + """ + + client = ctx.obj["client"] + + data = json.loads("".join([line for line in file_input])) + + r = client.put("api/v1/semgrep/configurations", data=data) + print_json(data=r) + +@app.command() +def delete( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Delete a configuration + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/semgrep/configuration/" + alias) + print_json(data=r) + +@app.command() +def delete_all( + ctx: typer.Context, +): + """ + Delete all configurations + """ + + client = ctx.obj["client"] + + r = client.delete("api/v1/semgrep/configurations") + print_json(data=r) + +@app.command() +def get( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Get a configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/semgrep/configuration/" + alias) + print_json(data=r) + +@app.command() +def list( + ctx: typer.Context, +): + """ + Get all configurations + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/semgrep/configurations") + print_json(data=r) + +@app.command() +def get_default( + ctx: typer.Context, +): + """ + Get default configuration + """ + + client = ctx.obj["client"] + + r = client.get("api/v1/semgrep/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), + is_default: bool = typer.Option(False, "--is-default", "-i", help="If this is the default configuration"), +): + """ + Update a configuration + """ + + client = ctx.obj["client"] + + data = { + "alias": alias, + "isDefault": is_default + } + + r = client.put("api/v1/semgrep/configuration/" + alias, data=data) + print_json(data=r) + +@app.command() +def validate( + ctx: typer.Context, + alias: str = typer.Option(..., "--alias", "-a", help="The alias of the configuration"), +): + """ + Validate a configuration + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/semgrep/configuration/validate/" + alias) + print_json(data=r) + +@app.command() +def validate_all( + ctx: typer.Context, +): + """ + Validate all configurations + """ + + client = ctx.obj["client"] + + r = client.post("api/v1/semgrep/configuration/validate") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/sentry.py b/cortexapps_cli/commands/integrations_commands/sentry.py new file mode 100644 index 0000000..0f9b4f4 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/sentry.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Sentry commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + auth_token: str = typer.Option(None, "--auth-token", "-at", help="Auth token"), + organization_slug: str = typer.Option(None, "--organization-slug", "-o", help="Organization slug"), + host: str = typer.Option(None, "--host", "-h", help="Host"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if auth_token or organization_slug or host: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "authToken": auth_token, + "organizationSlug": organization_slug, + "host": host, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/sentry/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/sentry/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + auth_token: str = typer.Option(None, "--auth-token", "-at", help="Auth token"), + organization_slug: str = typer.Option(None, "--organization-slug", "-o", help="Organization slug"), + host: str = typer.Option(None, "--host", "-h", help="Host"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if auth_token or organization_slug or host: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "authToken": auth_token, + "organizationSlug": organization_slug, + "host": host, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/sentry/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/sentry/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/sentry/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/servicenow.py b/cortexapps_cli/commands/integrations_commands/servicenow.py new file mode 100644 index 0000000..f7f978a --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/servicenow.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="ServiceNow commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + instance_name: str = typer.Option(None, "--instance-name", "-in", help="Instance name"), + password: str = typer.Option(None, "--password", "-p", help="Password"), + username: str = typer.Option(None, "--username", "-u", help="Username"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if instance_name or password or username: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "instanceName": instance_name, + "password": password, + "username": username, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/servicenow/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/servicenow/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + instance_name: str = typer.Option(None, "--instance-name", "-in", help="Instance name"), + password: str = typer.Option(None, "--password", "-p", help="Password"), + username: str = typer.Option(None, "--username", "-u", help="Username"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if instance_name or password or username: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "instanceName": instance_name, + "password": password, + "username": username, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/servicenow/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/servicenow/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/servicenow/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/servicenow_cloud_observability.py b/cortexapps_cli/commands/integrations_commands/servicenow_cloud_observability.py new file mode 100644 index 0000000..d40b962 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/servicenow_cloud_observability.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="ServiceNow Cloud Observability commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + auth_token: str = typer.Option(None, "--auth-token", "-at", help="Auth token"), + organization_id: str = typer.Option(None, "--organization-id", "-o", help="Organization ID"), + project_id: str = typer.Option(None, "--project-id", "-pi", help="Project ID"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if auth_token or organization_id or project_id: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "authToken": auth_token, + "organizationId": organization_id, + "projectId": project_id, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/lightstep/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/lightstep/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + auth_token: str = typer.Option(None, "--auth-token", "-at", help="Auth token"), + organization_id: str = typer.Option(None, "--organization-id", "-o", help="Organization ID"), + project_id: str = typer.Option(None, "--project-id", "-pi", help="Project ID"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if auth_token or organization_id or project_id: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "authToken": auth_token, + "organizationId": organization_id, + "projectId": project_id, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/lightstep/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/lightstep/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/lightstep/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/snyk.py b/cortexapps_cli/commands/integrations_commands/snyk.py new file mode 100644 index 0000000..bf2f06f --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/snyk.py @@ -0,0 +1,92 @@ +import json +from enum import Enum +from rich import print_json +import typer +from typing_extensions import Annotated + +class Region(str, Enum): + USA = "USA" + US2 = "US2" + EU = "EU" + AUS = "AUS" + +app = typer.Typer(help="Snyk commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + auth_token: str = typer.Option(None, "--auth-token", "-at", help="Auth token"), + region: Region = typer.Option(None, "--region", "-r", help="Region"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if auth_token or region: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "authToken": auth_token, + "region": region.value if region else None, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/snyk/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/snyk/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + auth_token: str = typer.Option(None, "--auth-token", "-at", help="Auth token"), + region: Region = typer.Option(None, "--region", "-r", help="Region"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if auth_token or region: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "authToken": auth_token, + "region": region.value if region else None, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/snyk/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/snyk/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/snyk/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/splunk_observability_cloud.py b/cortexapps_cli/commands/integrations_commands/splunk_observability_cloud.py new file mode 100644 index 0000000..62372b9 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/splunk_observability_cloud.py @@ -0,0 +1,85 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Splunk Observability Cloud commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + access_token: str = typer.Option(None, "--access-token", "-at", help="Access token"), + realm: str = typer.Option(None, "--realm", "-r", help="Realm"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if access_token or realm: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "accessToken": access_token, + "realm": realm, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/signalfx/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/signalfx/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + access_token: str = typer.Option(None, "--access-token", "-at", help="Access token"), + realm: str = typer.Option(None, "--realm", "-r", help="Realm"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if access_token or realm: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "accessToken": access_token, + "realm": realm, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/signalfx/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/signalfx/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/signalfx/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/splunk_on_call.py b/cortexapps_cli/commands/integrations_commands/splunk_on_call.py new file mode 100644 index 0000000..5fff998 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/splunk_on_call.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Splunk On Call commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_id: str = typer.Option(None, "--api-id", "-ai", help="API ID"), + api_key: str = typer.Option(None, "--api-key", "-api", help="API key"), + organization: str = typer.Option(None, "--organization", "-o", help="Organization"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_id or api_key or organization: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiId": api_id, + "apiKey": api_key, + "organization": organization, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/victorops/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/victorops/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_id: str = typer.Option(None, "--api-id", "-ai", help="API ID"), + api_key: str = typer.Option(None, "--api-key", "-api", help="API key"), + organization: str = typer.Option(None, "--organization", "-o", help="Organization"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_id or api_key or organization: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiId": api_id, + "apiKey": api_key, + "organization": organization, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/victorops/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/victorops/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/victorops/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/sumologic.py b/cortexapps_cli/commands/integrations_commands/sumologic.py new file mode 100644 index 0000000..53d15a8 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/sumologic.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Sumo Logic commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + access_id: str = typer.Option(None, "--access-id", "-ai", help="Access ID"), + access_key: str = typer.Option(None, "--access-key", "-ak", help="Access key"), + deployment: str = typer.Option(None, "--deployment", "-d", help="Deployment"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if access_id or access_key or deployment: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "accessId": access_id, + "accessKey": access_key, + "deployment": deployment, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/sumologic/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/sumologic/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + access_id: str = typer.Option(None, "--access-id", "-ai", help="Access ID"), + access_key: str = typer.Option(None, "--access-key", "-ak", help="Access key"), + deployment: str = typer.Option(None, "--deployment", "-d", help="Deployment"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if access_id or access_key or deployment: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "accessId": access_id, + "accessKey": access_key, + "deployment": deployment, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/sumologic/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/sumologic/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/sumologic/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/veracode.py b/cortexapps_cli/commands/integrations_commands/veracode.py new file mode 100644 index 0000000..f2c55f8 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/veracode.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Veracode commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + api_key: str = typer.Option(None, "--api-key", "-api", help="API key"), + key_id: str = typer.Option(None, "--key-id", "-ki", help="Key ID"), + region: str = typer.Option(None, "--region", "-r", help="Region"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_key or key_id or region: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiKey": api_key, + "keyId": key_id, + "region": region, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/veracode/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/veracode/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + api_key: str = typer.Option(None, "--api-key", "-api", help="API key"), + key_id: str = typer.Option(None, "--key-id", "-ki", help="Key ID"), + region: str = typer.Option(None, "--region", "-r", help="Region"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if api_key or key_id or region: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "apiKey": api_key, + "keyId": key_id, + "region": region, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/veracode/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/veracode/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/veracode/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/wiz.py b/cortexapps_cli/commands/integrations_commands/wiz.py new file mode 100644 index 0000000..3dc4a91 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/wiz.py @@ -0,0 +1,93 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Wiz commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + client_id: str = typer.Option(None, "--client-id", "-ci", help="Client ID"), + client_secret: str = typer.Option(None, "--client-secret", "-cs", help="Client secret"), + data_center: str = typer.Option(None, "--data-center", "-dc", help="Data center"), + identity_provider: str = typer.Option(None, "--identity-provider", "-ip", help="Identity provider"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if client_id or client_secret or data_center or identity_provider: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "clientId": client_id, + "clientSecret": client_secret, + "dataCenter": data_center, + "identityProvider": identity_provider, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/wiz/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/wiz/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + client_id: str = typer.Option(None, "--client-id", "-ci", help="Client ID"), + client_secret: str = typer.Option(None, "--client-secret", "-cs", help="Client secret"), + data_center: str = typer.Option(None, "--data-center", "-dc", help="Data center"), + identity_provider: str = typer.Option(None, "--identity-provider", "-ip", help="Identity provider"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if client_id or client_secret or data_center or identity_provider: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "clientId": client_id, + "clientSecret": client_secret, + "dataCenter": data_center, + "identityProvider": identity_provider, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/wiz/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/wiz/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/wiz/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/workday.py b/cortexapps_cli/commands/integrations_commands/workday.py new file mode 100644 index 0000000..bc9d28e --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/workday.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="Workday commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + username: str = typer.Option(None, "--username", "-u", help="Username"), + password: str = typer.Option(None, "--password", "-p", help="Password"), + ownership_report_url: str = typer.Option(None, "--ownership-report-url", "-url", help="Ownership report URL"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if username or password or ownership_report_url: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "username": username, + "password": password, + "ownershipReportUrl": ownership_report_url, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/workday/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/workday/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + username: str = typer.Option(None, "--username", "-u", help="Username"), + password: str = typer.Option(None, "--password", "-p", help="Password"), + ownership_report_url: str = typer.Option(None, "--ownership-report-url", "-url", help="Ownership report URL"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if username or password or ownership_report_url: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "username": username, + "password": password, + "ownershipReportUrl": ownership_report_url, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/workday/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/workday/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/workday/configurations") + print_json(data=r) diff --git a/cortexapps_cli/commands/integrations_commands/xmatters.py b/cortexapps_cli/commands/integrations_commands/xmatters.py new file mode 100644 index 0000000..f37b5a1 --- /dev/null +++ b/cortexapps_cli/commands/integrations_commands/xmatters.py @@ -0,0 +1,89 @@ +import json +from rich import print_json +import typer +from typing_extensions import Annotated + +app = typer.Typer(help="xMatters commands", no_args_is_help=True) + +@app.command() +def add( + ctx: typer.Context, + organization_slug: str = typer.Option(None, "--organization-slug", "-o", help="Organization slug"), + password: str = typer.Option(None, "--password", "-p", help="Password"), + username: str = typer.Option(None, "--username", "-u", help="Username"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Add a configuration + """ + client = ctx.obj["client"] + + if file_input: + if organization_slug or password or username: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "organizationSlug": organization_slug, + "password": password, + "username": username, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.post("api/v1/xmatters/configuration", data=data) + print_json(data=r) + +@app.command() +def get(ctx: typer.Context): + """ + Get the configuration + """ + client = ctx.obj["client"] + r = client.get("api/v1/xmatters/default-configuration") + print_json(data=r) + +@app.command() +def update( + ctx: typer.Context, + organization_slug: str = typer.Option(None, "--organization-slug", "-o", help="Organization slug"), + password: str = typer.Option(None, "--password", "-p", help="Password"), + username: str = typer.Option(None, "--username", "-u", help="Username"), + file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help="JSON file containing configuration, if command line options not used; can be passed as stdin with -, example: -f-")] = None, +): + """ + Update the configuration + """ + client = ctx.obj["client"] + + if file_input: + if organization_slug or password or username: + raise typer.BadParameter("When providing a configuration file, do not specify any other attributes") + data = json.loads("".join([line for line in file_input])) + else: + data = { + "organizationSlug": organization_slug, + "password": password, + "username": username, + } + data = {k: v for k, v in data.items() if v is not None} + + r = client.put("api/v1/xmatters/configuration", data=data) + print_json(data=r) + +@app.command() +def validate(ctx: typer.Context): + """ + Validate the configuration + """ + client = ctx.obj["client"] + r = client.post("api/v1/xmatters/configuration/validate") + print_json(data=r) + +@app.command() +def delete(ctx: typer.Context): + """ + Delete the configuration + """ + client = ctx.obj["client"] + r = client.delete("api/v1/xmatters/configurations") + print_json(data=r) diff --git a/tests/test_integrations_apiiro.py b/tests/test_integrations_apiiro.py new file mode 100644 index 0000000..91d148f --- /dev/null +++ b/tests/test_integrations_apiiro.py @@ -0,0 +1,59 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_apiiro_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_apiiro_add(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configuration", json={}, status=200) + cli(["integrations", "apiiro", "add", "-a", "myAlias", "--api-key", "123456", "-i"]) + +@responses.activate +def test_integrations_apiiro_add_multiple(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configurations", json={}, status=200) + cli(["integrations", "apiiro", "add-multiple", "-f", str(f)]) + +@responses.activate +def test_integrations_apiiro_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configuration/test", status=200) + cli(["integrations", "apiiro", "delete", "-a", "test"]) + +@responses.activate +def test_integrations_apiiro_delete_all(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configurations", status=200) + cli(["integrations", "apiiro", "delete-all"]) + +@responses.activate +def test_integrations_apiiro_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configuration/test", json={}, status=200) + cli(["integrations", "apiiro", "get", "-a", "test"]) + +@responses.activate +def test_integrations_apiiro_list(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configurations", json={}, status=200) + cli(["integrations", "apiiro", "list"]) + +@responses.activate +def test_integrations_apiiro_get_default(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/default-configuration", json={}, status=200) + cli(["integrations", "apiiro", "get-default"]) + +@responses.activate +def test_integrations_apiiro_update(): + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configuration/test", json={}, status=200) + cli(["integrations", "apiiro", "update", "-a", "test", "-i"]) + +@responses.activate +def test_integrations_apiiro_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configuration/validate/test", json={}, status=200) + cli(["integrations", "apiiro", "validate", "-a", "test"]) + +@responses.activate +def test_integrations_apiiro_validate_all(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configuration/validate", json={}, status=200) + cli(["integrations", "apiiro", "validate-all"]) diff --git a/tests/test_integrations_argocd.py b/tests/test_integrations_argocd.py new file mode 100644 index 0000000..511f9ad --- /dev/null +++ b/tests/test_integrations_argocd.py @@ -0,0 +1,59 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_argocd_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_argocd_add(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configuration", json={}, status=200) + cli(["integrations", "argocd", "add", "-a", "myAlias", "-h", "https://argocd.example.com", "-p", "pass123", "-u", "admin", "-i"]) + +@responses.activate +def test_integrations_argocd_add_multiple(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configurations", json={}, status=200) + cli(["integrations", "argocd", "add-multiple", "-f", str(f)]) + +@responses.activate +def test_integrations_argocd_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configuration/test", status=200) + cli(["integrations", "argocd", "delete", "-a", "test"]) + +@responses.activate +def test_integrations_argocd_delete_all(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configurations", status=200) + cli(["integrations", "argocd", "delete-all"]) + +@responses.activate +def test_integrations_argocd_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configuration/test", json={}, status=200) + cli(["integrations", "argocd", "get", "-a", "test"]) + +@responses.activate +def test_integrations_argocd_list(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configurations", json={}, status=200) + cli(["integrations", "argocd", "list"]) + +@responses.activate +def test_integrations_argocd_get_default(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/default-configuration", json={}, status=200) + cli(["integrations", "argocd", "get-default"]) + +@responses.activate +def test_integrations_argocd_update(): + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configuration/test", json={}, status=200) + cli(["integrations", "argocd", "update", "-a", "test", "-i"]) + +@responses.activate +def test_integrations_argocd_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configuration/validate/test", json={}, status=200) + cli(["integrations", "argocd", "validate", "-a", "test"]) + +@responses.activate +def test_integrations_argocd_validate_all(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configuration/validate", json={}, status=200) + cli(["integrations", "argocd", "validate-all"]) diff --git a/tests/test_integrations_azure_active_directory.py b/tests/test_integrations_azure_active_directory.py new file mode 100644 index 0000000..24e8c00 --- /dev/null +++ b/tests/test_integrations_azure_active_directory.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_azure_active_directory_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_azure_active_directory_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/configuration", json={}, status=200) + cli(["integrations", "azure-active-directory", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_azure_active_directory_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/default-configuration", json={}, status=200) + cli(["integrations", "azure-active-directory", "get"]) + +@responses.activate +def test_integrations_azure_active_directory_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/configuration", json={}, status=200) + cli(["integrations", "azure-active-directory", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_azure_active_directory_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/configuration/validate", json={}, status=200) + cli(["integrations", "azure-active-directory", "validate"]) + +@responses.activate +def test_integrations_azure_active_directory_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/configurations", json={}, status=200) + cli(["integrations", "azure-active-directory", "delete"]) diff --git a/tests/test_integrations_bamboohr.py b/tests/test_integrations_bamboohr.py new file mode 100644 index 0000000..d04291f --- /dev/null +++ b/tests/test_integrations_bamboohr.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_bamboohr_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_bamboohr_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/configuration", json={}, status=200) + cli(["integrations", "bamboohr", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_bamboohr_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/default-configuration", json={}, status=200) + cli(["integrations", "bamboohr", "get"]) + +@responses.activate +def test_integrations_bamboohr_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/configuration", json={}, status=200) + cli(["integrations", "bamboohr", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_bamboohr_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/configuration/validate", json={}, status=200) + cli(["integrations", "bamboohr", "validate"]) + +@responses.activate +def test_integrations_bamboohr_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/configurations", json={}, status=200) + cli(["integrations", "bamboohr", "delete"]) diff --git a/tests/test_integrations_bitbucket.py b/tests/test_integrations_bitbucket.py new file mode 100644 index 0000000..c014858 --- /dev/null +++ b/tests/test_integrations_bitbucket.py @@ -0,0 +1,60 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_bitbucket_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_bitbucket_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configuration", json={}, status=200) + cli(["integrations", "bitbucket", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_bitbucket_add_multiple(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configurations", json={}, status=200) + cli(["integrations", "bitbucket", "add-multiple", "-f", str(f)]) + +@responses.activate +def test_integrations_bitbucket_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configuration/test", status=200) + cli(["integrations", "bitbucket", "delete", "-a", "test"]) + +@responses.activate +def test_integrations_bitbucket_delete_all(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configurations", status=200) + cli(["integrations", "bitbucket", "delete-all"]) + +@responses.activate +def test_integrations_bitbucket_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configuration/test", json={}, status=200) + cli(["integrations", "bitbucket", "get", "-a", "test"]) + +@responses.activate +def test_integrations_bitbucket_list(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configurations", json={}, status=200) + cli(["integrations", "bitbucket", "list"]) + +@responses.activate +def test_integrations_bitbucket_get_default(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/default-configuration", json={}, status=200) + cli(["integrations", "bitbucket", "get-default"]) + +@responses.activate +def test_integrations_bitbucket_update(): + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configuration/test", json={}, status=200) + cli(["integrations", "bitbucket", "update", "-a", "test", "-i"]) + +@responses.activate +def test_integrations_bitbucket_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configuration/validate/test", json={}, status=200) + cli(["integrations", "bitbucket", "validate", "-a", "test"]) + +@responses.activate +def test_integrations_bitbucket_validate_all(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configuration/validate", json={}, status=200) + cli(["integrations", "bitbucket", "validate-all"]) diff --git a/tests/test_integrations_bugsnag.py b/tests/test_integrations_bugsnag.py new file mode 100644 index 0000000..5c753ef --- /dev/null +++ b/tests/test_integrations_bugsnag.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_bugsnag_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_bugsnag_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/configuration", json={}, status=200) + cli(["integrations", "bugsnag", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_bugsnag_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/default-configuration", json={}, status=200) + cli(["integrations", "bugsnag", "get"]) + +@responses.activate +def test_integrations_bugsnag_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/configuration", json={}, status=200) + cli(["integrations", "bugsnag", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_bugsnag_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/configuration/validate", json={}, status=200) + cli(["integrations", "bugsnag", "validate"]) + +@responses.activate +def test_integrations_bugsnag_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/configurations", json={}, status=200) + cli(["integrations", "bugsnag", "delete"]) diff --git a/tests/test_integrations_buildkite.py b/tests/test_integrations_buildkite.py new file mode 100644 index 0000000..1d97c5c --- /dev/null +++ b/tests/test_integrations_buildkite.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_buildkite_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_buildkite_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/configuration", json={}, status=200) + cli(["integrations", "buildkite", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_buildkite_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/default-configuration", json={}, status=200) + cli(["integrations", "buildkite", "get"]) + +@responses.activate +def test_integrations_buildkite_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/configuration", json={}, status=200) + cli(["integrations", "buildkite", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_buildkite_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/configuration/validate", json={}, status=200) + cli(["integrations", "buildkite", "validate"]) + +@responses.activate +def test_integrations_buildkite_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/configurations", json={}, status=200) + cli(["integrations", "buildkite", "delete"]) diff --git a/tests/test_integrations_checkmarx_sast.py b/tests/test_integrations_checkmarx_sast.py new file mode 100644 index 0000000..a86ad18 --- /dev/null +++ b/tests/test_integrations_checkmarx_sast.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_checkmarx_sast_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_checkmarx_sast_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/configuration", json={}, status=200) + cli(["integrations", "checkmarx-sast", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_checkmarx_sast_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/default-configuration", json={}, status=200) + cli(["integrations", "checkmarx-sast", "get"]) + +@responses.activate +def test_integrations_checkmarx_sast_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/configuration", json={}, status=200) + cli(["integrations", "checkmarx-sast", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_checkmarx_sast_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/configuration/validate", json={}, status=200) + cli(["integrations", "checkmarx-sast", "validate"]) + +@responses.activate +def test_integrations_checkmarx_sast_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/configurations", json={}, status=200) + cli(["integrations", "checkmarx-sast", "delete"]) diff --git a/tests/test_integrations_clickup.py b/tests/test_integrations_clickup.py new file mode 100644 index 0000000..8318c90 --- /dev/null +++ b/tests/test_integrations_clickup.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_clickup_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_clickup_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/configuration", json={}, status=200) + cli(["integrations", "clickup", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_clickup_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/default-configuration", json={}, status=200) + cli(["integrations", "clickup", "get"]) + +@responses.activate +def test_integrations_clickup_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/configuration", json={}, status=200) + cli(["integrations", "clickup", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_clickup_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/configuration/validate", json={}, status=200) + cli(["integrations", "clickup", "validate"]) + +@responses.activate +def test_integrations_clickup_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/configurations", json={}, status=200) + cli(["integrations", "clickup", "delete"]) diff --git a/tests/test_integrations_codecov.py b/tests/test_integrations_codecov.py new file mode 100644 index 0000000..4eb06a5 --- /dev/null +++ b/tests/test_integrations_codecov.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_codecov_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_codecov_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/configuration", json={}, status=200) + cli(["integrations", "codecov", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_codecov_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/default-configuration", json={}, status=200) + cli(["integrations", "codecov", "get"]) + +@responses.activate +def test_integrations_codecov_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/configuration", json={}, status=200) + cli(["integrations", "codecov", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_codecov_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/configuration/validate", json={}, status=200) + cli(["integrations", "codecov", "validate"]) + +@responses.activate +def test_integrations_codecov_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/configurations", json={}, status=200) + cli(["integrations", "codecov", "delete"]) diff --git a/tests/test_integrations_dynatrace.py b/tests/test_integrations_dynatrace.py new file mode 100644 index 0000000..5201cea --- /dev/null +++ b/tests/test_integrations_dynatrace.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_dynatrace_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_dynatrace_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/configuration", json={}, status=200) + cli(["integrations", "dynatrace", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_dynatrace_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/default-configuration", json={}, status=200) + cli(["integrations", "dynatrace", "get"]) + +@responses.activate +def test_integrations_dynatrace_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/configuration", json={}, status=200) + cli(["integrations", "dynatrace", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_dynatrace_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/configuration/validate", json={}, status=200) + cli(["integrations", "dynatrace", "validate"]) + +@responses.activate +def test_integrations_dynatrace_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/configurations", json={}, status=200) + cli(["integrations", "dynatrace", "delete"]) diff --git a/tests/test_integrations_firehydrant.py b/tests/test_integrations_firehydrant.py new file mode 100644 index 0000000..3597a57 --- /dev/null +++ b/tests/test_integrations_firehydrant.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_firehydrant_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_firehydrant_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/configuration", json={}, status=200) + cli(["integrations", "firehydrant", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_firehydrant_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/default-configuration", json={}, status=200) + cli(["integrations", "firehydrant", "get"]) + +@responses.activate +def test_integrations_firehydrant_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/configuration", json={}, status=200) + cli(["integrations", "firehydrant", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_firehydrant_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/configuration/validate", json={}, status=200) + cli(["integrations", "firehydrant", "validate"]) + +@responses.activate +def test_integrations_firehydrant_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/configurations", json={}, status=200) + cli(["integrations", "firehydrant", "delete"]) diff --git a/tests/test_integrations_instana.py b/tests/test_integrations_instana.py new file mode 100644 index 0000000..d03471d --- /dev/null +++ b/tests/test_integrations_instana.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_instana_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_instana_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/configuration", json={}, status=200) + cli(["integrations", "instana", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_instana_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/default-configuration", json={}, status=200) + cli(["integrations", "instana", "get"]) + +@responses.activate +def test_integrations_instana_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/configuration", json={}, status=200) + cli(["integrations", "instana", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_instana_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/configuration/validate", json={}, status=200) + cli(["integrations", "instana", "validate"]) + +@responses.activate +def test_integrations_instana_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/configurations", json={}, status=200) + cli(["integrations", "instana", "delete"]) diff --git a/tests/test_integrations_jenkins.py b/tests/test_integrations_jenkins.py new file mode 100644 index 0000000..dd92cad --- /dev/null +++ b/tests/test_integrations_jenkins.py @@ -0,0 +1,59 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_jenkins_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_jenkins_add(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configuration", json={}, status=200) + cli(["integrations", "jenkins", "add", "-a", "myAlias", "--api-key", "123456", "-h", "https://jenkins.example.com", "-u", "admin", "-i"]) + +@responses.activate +def test_integrations_jenkins_add_multiple(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configurations", json={}, status=200) + cli(["integrations", "jenkins", "add-multiple", "-f", str(f)]) + +@responses.activate +def test_integrations_jenkins_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configuration/test", status=200) + cli(["integrations", "jenkins", "delete", "-a", "test"]) + +@responses.activate +def test_integrations_jenkins_delete_all(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configurations", status=200) + cli(["integrations", "jenkins", "delete-all"]) + +@responses.activate +def test_integrations_jenkins_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configuration/test", json={}, status=200) + cli(["integrations", "jenkins", "get", "-a", "test"]) + +@responses.activate +def test_integrations_jenkins_list(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configurations", json={}, status=200) + cli(["integrations", "jenkins", "list"]) + +@responses.activate +def test_integrations_jenkins_get_default(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/default-configuration", json={}, status=200) + cli(["integrations", "jenkins", "get-default"]) + +@responses.activate +def test_integrations_jenkins_update(): + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configuration/test", json={}, status=200) + cli(["integrations", "jenkins", "update", "-a", "test", "-i"]) + +@responses.activate +def test_integrations_jenkins_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configuration/validate/test", json={}, status=200) + cli(["integrations", "jenkins", "validate", "-a", "test"]) + +@responses.activate +def test_integrations_jenkins_validate_all(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configuration/validate", json={}, status=200) + cli(["integrations", "jenkins", "validate-all"]) diff --git a/tests/test_integrations_jira.py b/tests/test_integrations_jira.py new file mode 100644 index 0000000..fd10595 --- /dev/null +++ b/tests/test_integrations_jira.py @@ -0,0 +1,60 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_jira_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_jira_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configuration", json={}, status=200) + cli(["integrations", "jira", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_jira_add_multiple(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configurations", json={}, status=200) + cli(["integrations", "jira", "add-multiple", "-f", str(f)]) + +@responses.activate +def test_integrations_jira_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configuration/test", status=200) + cli(["integrations", "jira", "delete", "-a", "test"]) + +@responses.activate +def test_integrations_jira_delete_all(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configurations", status=200) + cli(["integrations", "jira", "delete-all"]) + +@responses.activate +def test_integrations_jira_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configuration/test", json={}, status=200) + cli(["integrations", "jira", "get", "-a", "test"]) + +@responses.activate +def test_integrations_jira_list(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configurations", json={}, status=200) + cli(["integrations", "jira", "list"]) + +@responses.activate +def test_integrations_jira_get_default(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/default-configuration", json={}, status=200) + cli(["integrations", "jira", "get-default"]) + +@responses.activate +def test_integrations_jira_update(): + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configuration/test", json={}, status=200) + cli(["integrations", "jira", "update", "-a", "test", "-i"]) + +@responses.activate +def test_integrations_jira_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configuration/validate/test", json={}, status=200) + cli(["integrations", "jira", "validate", "-a", "test"]) + +@responses.activate +def test_integrations_jira_validate_all(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configuration/validate", json={}, status=200) + cli(["integrations", "jira", "validate-all"]) diff --git a/tests/test_integrations_mend_sast.py b/tests/test_integrations_mend_sast.py new file mode 100644 index 0000000..0087ea8 --- /dev/null +++ b/tests/test_integrations_mend_sast.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_mend_sast_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_mend_sast_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/configuration", json={}, status=200) + cli(["integrations", "mend-sast", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_mend_sast_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/default-configuration", json={}, status=200) + cli(["integrations", "mend-sast", "get"]) + +@responses.activate +def test_integrations_mend_sast_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/configuration", json={}, status=200) + cli(["integrations", "mend-sast", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_mend_sast_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/configuration/validate", json={}, status=200) + cli(["integrations", "mend-sast", "validate"]) + +@responses.activate +def test_integrations_mend_sast_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/configurations", json={}, status=200) + cli(["integrations", "mend-sast", "delete"]) diff --git a/tests/test_integrations_mend_sca.py b/tests/test_integrations_mend_sca.py new file mode 100644 index 0000000..44f1e0e --- /dev/null +++ b/tests/test_integrations_mend_sca.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_mend_sca_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_mend_sca_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/configuration", json={}, status=200) + cli(["integrations", "mend-sca", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_mend_sca_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/default-configuration", json={}, status=200) + cli(["integrations", "mend-sca", "get"]) + +@responses.activate +def test_integrations_mend_sca_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/configuration", json={}, status=200) + cli(["integrations", "mend-sca", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_mend_sca_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/configuration/validate", json={}, status=200) + cli(["integrations", "mend-sca", "validate"]) + +@responses.activate +def test_integrations_mend_sca_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/configurations", json={}, status=200) + cli(["integrations", "mend-sca", "delete"]) diff --git a/tests/test_integrations_okta.py b/tests/test_integrations_okta.py new file mode 100644 index 0000000..2633233 --- /dev/null +++ b/tests/test_integrations_okta.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_okta_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_okta_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/configuration", json={}, status=200) + cli(["integrations", "okta", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_okta_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/default-configuration", json={}, status=200) + cli(["integrations", "okta", "get"]) + +@responses.activate +def test_integrations_okta_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/configuration", json={}, status=200) + cli(["integrations", "okta", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_okta_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/configuration/validate", json={}, status=200) + cli(["integrations", "okta", "validate"]) + +@responses.activate +def test_integrations_okta_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/configurations", json={}, status=200) + cli(["integrations", "okta", "delete"]) diff --git a/tests/test_integrations_opsgenie.py b/tests/test_integrations_opsgenie.py new file mode 100644 index 0000000..9165c50 --- /dev/null +++ b/tests/test_integrations_opsgenie.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_opsgenie_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_opsgenie_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/configuration", json={}, status=200) + cli(["integrations", "opsgenie", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_opsgenie_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/default-configuration", json={}, status=200) + cli(["integrations", "opsgenie", "get"]) + +@responses.activate +def test_integrations_opsgenie_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/configuration", json={}, status=200) + cli(["integrations", "opsgenie", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_opsgenie_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/configuration/validate", json={}, status=200) + cli(["integrations", "opsgenie", "validate"]) + +@responses.activate +def test_integrations_opsgenie_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/configurations", json={}, status=200) + cli(["integrations", "opsgenie", "delete"]) diff --git a/tests/test_integrations_rollbar.py b/tests/test_integrations_rollbar.py new file mode 100644 index 0000000..9e06ac6 --- /dev/null +++ b/tests/test_integrations_rollbar.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_rollbar_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_rollbar_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/configuration", json={}, status=200) + cli(["integrations", "rollbar", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_rollbar_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/default-configuration", json={}, status=200) + cli(["integrations", "rollbar", "get"]) + +@responses.activate +def test_integrations_rollbar_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/configuration", json={}, status=200) + cli(["integrations", "rollbar", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_rollbar_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/configuration/validate", json={}, status=200) + cli(["integrations", "rollbar", "validate"]) + +@responses.activate +def test_integrations_rollbar_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/configurations", json={}, status=200) + cli(["integrations", "rollbar", "delete"]) diff --git a/tests/test_integrations_rootly.py b/tests/test_integrations_rootly.py new file mode 100644 index 0000000..5cf8f5a --- /dev/null +++ b/tests/test_integrations_rootly.py @@ -0,0 +1,59 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_rootly_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_rootly_add(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configuration", json={}, status=200) + cli(["integrations", "rootly", "add", "-a", "myAlias", "--api-key", "123456", "-i"]) + +@responses.activate +def test_integrations_rootly_add_multiple(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configurations", json={}, status=200) + cli(["integrations", "rootly", "add-multiple", "-f", str(f)]) + +@responses.activate +def test_integrations_rootly_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configuration/test", status=200) + cli(["integrations", "rootly", "delete", "-a", "test"]) + +@responses.activate +def test_integrations_rootly_delete_all(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configurations", status=200) + cli(["integrations", "rootly", "delete-all"]) + +@responses.activate +def test_integrations_rootly_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configuration/test", json={}, status=200) + cli(["integrations", "rootly", "get", "-a", "test"]) + +@responses.activate +def test_integrations_rootly_list(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configurations", json={}, status=200) + cli(["integrations", "rootly", "list"]) + +@responses.activate +def test_integrations_rootly_get_default(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/default-configuration", json={}, status=200) + cli(["integrations", "rootly", "get-default"]) + +@responses.activate +def test_integrations_rootly_update(): + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configuration/test", json={}, status=200) + cli(["integrations", "rootly", "update", "-a", "test", "-i"]) + +@responses.activate +def test_integrations_rootly_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configuration/validate/test", json={}, status=200) + cli(["integrations", "rootly", "validate", "-a", "test"]) + +@responses.activate +def test_integrations_rootly_validate_all(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configuration/validate", json={}, status=200) + cli(["integrations", "rootly", "validate-all"]) diff --git a/tests/test_integrations_semgrep.py b/tests/test_integrations_semgrep.py new file mode 100644 index 0000000..bd49158 --- /dev/null +++ b/tests/test_integrations_semgrep.py @@ -0,0 +1,59 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_semgrep_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_semgrep_add(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configuration", json={}, status=200) + cli(["integrations", "semgrep", "add", "-a", "myAlias", "--api-key", "123456", "-oi", "org123", "-os", "my-org", "-i"]) + +@responses.activate +def test_integrations_semgrep_add_multiple(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configurations", json={}, status=200) + cli(["integrations", "semgrep", "add-multiple", "-f", str(f)]) + +@responses.activate +def test_integrations_semgrep_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configuration/test", status=200) + cli(["integrations", "semgrep", "delete", "-a", "test"]) + +@responses.activate +def test_integrations_semgrep_delete_all(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configurations", status=200) + cli(["integrations", "semgrep", "delete-all"]) + +@responses.activate +def test_integrations_semgrep_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configuration/test", json={}, status=200) + cli(["integrations", "semgrep", "get", "-a", "test"]) + +@responses.activate +def test_integrations_semgrep_list(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configurations", json={}, status=200) + cli(["integrations", "semgrep", "list"]) + +@responses.activate +def test_integrations_semgrep_get_default(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/default-configuration", json={}, status=200) + cli(["integrations", "semgrep", "get-default"]) + +@responses.activate +def test_integrations_semgrep_update(): + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configuration/test", json={}, status=200) + cli(["integrations", "semgrep", "update", "-a", "test", "-i"]) + +@responses.activate +def test_integrations_semgrep_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configuration/validate/test", json={}, status=200) + cli(["integrations", "semgrep", "validate", "-a", "test"]) + +@responses.activate +def test_integrations_semgrep_validate_all(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configuration/validate", json={}, status=200) + cli(["integrations", "semgrep", "validate-all"]) diff --git a/tests/test_integrations_sentry.py b/tests/test_integrations_sentry.py new file mode 100644 index 0000000..d9e0fb1 --- /dev/null +++ b/tests/test_integrations_sentry.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_sentry_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_sentry_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/configuration", json={}, status=200) + cli(["integrations", "sentry", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_sentry_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/default-configuration", json={}, status=200) + cli(["integrations", "sentry", "get"]) + +@responses.activate +def test_integrations_sentry_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/configuration", json={}, status=200) + cli(["integrations", "sentry", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_sentry_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/configuration/validate", json={}, status=200) + cli(["integrations", "sentry", "validate"]) + +@responses.activate +def test_integrations_sentry_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/configurations", json={}, status=200) + cli(["integrations", "sentry", "delete"]) diff --git a/tests/test_integrations_servicenow.py b/tests/test_integrations_servicenow.py new file mode 100644 index 0000000..bf94756 --- /dev/null +++ b/tests/test_integrations_servicenow.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_servicenow_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_servicenow_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/configuration", json={}, status=200) + cli(["integrations", "servicenow", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_servicenow_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/default-configuration", json={}, status=200) + cli(["integrations", "servicenow", "get"]) + +@responses.activate +def test_integrations_servicenow_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/configuration", json={}, status=200) + cli(["integrations", "servicenow", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_servicenow_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/configuration/validate", json={}, status=200) + cli(["integrations", "servicenow", "validate"]) + +@responses.activate +def test_integrations_servicenow_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/configurations", json={}, status=200) + cli(["integrations", "servicenow", "delete"]) diff --git a/tests/test_integrations_servicenow_cloud_observability.py b/tests/test_integrations_servicenow_cloud_observability.py new file mode 100644 index 0000000..59f9b1b --- /dev/null +++ b/tests/test_integrations_servicenow_cloud_observability.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_servicenow_cloud_observability_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_servicenow_cloud_observability_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/configuration", json={}, status=200) + cli(["integrations", "servicenow-cloud-observability", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_servicenow_cloud_observability_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/default-configuration", json={}, status=200) + cli(["integrations", "servicenow-cloud-observability", "get"]) + +@responses.activate +def test_integrations_servicenow_cloud_observability_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/configuration", json={}, status=200) + cli(["integrations", "servicenow-cloud-observability", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_servicenow_cloud_observability_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/configuration/validate", json={}, status=200) + cli(["integrations", "servicenow-cloud-observability", "validate"]) + +@responses.activate +def test_integrations_servicenow_cloud_observability_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/configurations", json={}, status=200) + cli(["integrations", "servicenow-cloud-observability", "delete"]) diff --git a/tests/test_integrations_snyk.py b/tests/test_integrations_snyk.py new file mode 100644 index 0000000..9eeff7d --- /dev/null +++ b/tests/test_integrations_snyk.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_snyk_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_snyk_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/configuration", json={}, status=200) + cli(["integrations", "snyk", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_snyk_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/default-configuration", json={}, status=200) + cli(["integrations", "snyk", "get"]) + +@responses.activate +def test_integrations_snyk_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/configuration", json={}, status=200) + cli(["integrations", "snyk", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_snyk_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/configuration/validate", json={}, status=200) + cli(["integrations", "snyk", "validate"]) + +@responses.activate +def test_integrations_snyk_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/configurations", json={}, status=200) + cli(["integrations", "snyk", "delete"]) diff --git a/tests/test_integrations_splunk_observability_cloud.py b/tests/test_integrations_splunk_observability_cloud.py new file mode 100644 index 0000000..8260a72 --- /dev/null +++ b/tests/test_integrations_splunk_observability_cloud.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_splunk_observability_cloud_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_splunk_observability_cloud_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/configuration", json={}, status=200) + cli(["integrations", "splunk-observability-cloud", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_splunk_observability_cloud_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/default-configuration", json={}, status=200) + cli(["integrations", "splunk-observability-cloud", "get"]) + +@responses.activate +def test_integrations_splunk_observability_cloud_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/configuration", json={}, status=200) + cli(["integrations", "splunk-observability-cloud", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_splunk_observability_cloud_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/configuration/validate", json={}, status=200) + cli(["integrations", "splunk-observability-cloud", "validate"]) + +@responses.activate +def test_integrations_splunk_observability_cloud_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/configurations", json={}, status=200) + cli(["integrations", "splunk-observability-cloud", "delete"]) diff --git a/tests/test_integrations_splunk_on_call.py b/tests/test_integrations_splunk_on_call.py new file mode 100644 index 0000000..2a421c1 --- /dev/null +++ b/tests/test_integrations_splunk_on_call.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_splunk_on_call_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_splunk_on_call_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/configuration", json={}, status=200) + cli(["integrations", "splunk-on-call", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_splunk_on_call_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/default-configuration", json={}, status=200) + cli(["integrations", "splunk-on-call", "get"]) + +@responses.activate +def test_integrations_splunk_on_call_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/configuration", json={}, status=200) + cli(["integrations", "splunk-on-call", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_splunk_on_call_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/configuration/validate", json={}, status=200) + cli(["integrations", "splunk-on-call", "validate"]) + +@responses.activate +def test_integrations_splunk_on_call_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/configurations", json={}, status=200) + cli(["integrations", "splunk-on-call", "delete"]) diff --git a/tests/test_integrations_sumologic.py b/tests/test_integrations_sumologic.py new file mode 100644 index 0000000..2f7db9c --- /dev/null +++ b/tests/test_integrations_sumologic.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_sumologic_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_sumologic_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/configuration", json={}, status=200) + cli(["integrations", "sumo-logic", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_sumologic_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/default-configuration", json={}, status=200) + cli(["integrations", "sumo-logic", "get"]) + +@responses.activate +def test_integrations_sumologic_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/configuration", json={}, status=200) + cli(["integrations", "sumo-logic", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_sumologic_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/configuration/validate", json={}, status=200) + cli(["integrations", "sumo-logic", "validate"]) + +@responses.activate +def test_integrations_sumologic_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/configurations", json={}, status=200) + cli(["integrations", "sumo-logic", "delete"]) diff --git a/tests/test_integrations_veracode.py b/tests/test_integrations_veracode.py new file mode 100644 index 0000000..8b5a731 --- /dev/null +++ b/tests/test_integrations_veracode.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_veracode_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_veracode_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/configuration", json={}, status=200) + cli(["integrations", "veracode", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_veracode_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/default-configuration", json={}, status=200) + cli(["integrations", "veracode", "get"]) + +@responses.activate +def test_integrations_veracode_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/configuration", json={}, status=200) + cli(["integrations", "veracode", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_veracode_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/configuration/validate", json={}, status=200) + cli(["integrations", "veracode", "validate"]) + +@responses.activate +def test_integrations_veracode_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/configurations", json={}, status=200) + cli(["integrations", "veracode", "delete"]) diff --git a/tests/test_integrations_wiz.py b/tests/test_integrations_wiz.py new file mode 100644 index 0000000..886568b --- /dev/null +++ b/tests/test_integrations_wiz.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_wiz_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_wiz_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/configuration", json={}, status=200) + cli(["integrations", "wiz", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_wiz_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/default-configuration", json={}, status=200) + cli(["integrations", "wiz", "get"]) + +@responses.activate +def test_integrations_wiz_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/configuration", json={}, status=200) + cli(["integrations", "wiz", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_wiz_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/configuration/validate", json={}, status=200) + cli(["integrations", "wiz", "validate"]) + +@responses.activate +def test_integrations_wiz_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/configurations", json={}, status=200) + cli(["integrations", "wiz", "delete"]) diff --git a/tests/test_integrations_workday.py b/tests/test_integrations_workday.py new file mode 100644 index 0000000..c9957ee --- /dev/null +++ b/tests/test_integrations_workday.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_workday_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_workday_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/configuration", json={}, status=200) + cli(["integrations", "workday", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_workday_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/default-configuration", json={}, status=200) + cli(["integrations", "workday", "get"]) + +@responses.activate +def test_integrations_workday_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/configuration", json={}, status=200) + cli(["integrations", "workday", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_workday_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/configuration/validate", json={}, status=200) + cli(["integrations", "workday", "validate"]) + +@responses.activate +def test_integrations_workday_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/configurations", json={}, status=200) + cli(["integrations", "workday", "delete"]) diff --git a/tests/test_integrations_xmatters.py b/tests/test_integrations_xmatters.py new file mode 100644 index 0000000..12e23a5 --- /dev/null +++ b/tests/test_integrations_xmatters.py @@ -0,0 +1,35 @@ +from tests.helpers.utils import * + +# Since responses are all mocked and no data validation is done by the CLI -- +# we let the API handle validation -- we don't need valid input files. +def _dummy_file(tmp_path): + f = tmp_path / "test_integrations_xmatters_add.json" + f.write_text("foobar") + return f + +@responses.activate +def test_integrations_xmatters_add(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/configuration", json={}, status=200) + cli(["integrations", "xmatters", "add", "-f", str(f)]) + +@responses.activate +def test_integrations_xmatters_get(): + responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/default-configuration", json={}, status=200) + cli(["integrations", "xmatters", "get"]) + +@responses.activate +def test_integrations_xmatters_update(tmp_path): + f = _dummy_file(tmp_path) + responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/configuration", json={}, status=200) + cli(["integrations", "xmatters", "update", "-f", str(f)]) + +@responses.activate +def test_integrations_xmatters_validate(): + responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/configuration/validate", json={}, status=200) + cli(["integrations", "xmatters", "validate"]) + +@responses.activate +def test_integrations_xmatters_delete(): + responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/configurations", json={}, status=200) + cli(["integrations", "xmatters", "delete"])