honeycomb-api 0.1.0__py3-none-any.whl → 0.5.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- honeycomb/__init__.py +9 -0
- honeycomb/_generated/api/auth/get_auth.py +2 -5
- honeycomb/_generated/api/auth/get_v2_auth.py +2 -5
- honeycomb/_generated/api/boards/create_board.py +6 -5
- honeycomb/_generated/api/boards/create_board_view.py +14 -9
- honeycomb/_generated/api/boards/delete_board.py +2 -5
- honeycomb/_generated/api/boards/delete_board_view.py +2 -5
- honeycomb/_generated/api/boards/get_board.py +2 -5
- honeycomb/_generated/api/boards/get_board_view.py +2 -5
- honeycomb/_generated/api/boards/list_board_views.py +10 -9
- honeycomb/_generated/api/boards/list_boards.py +2 -5
- honeycomb/_generated/api/boards/update_board.py +10 -5
- honeycomb/_generated/api/boards/update_board_view.py +2 -5
- honeycomb/_generated/api/burn_alerts/create_burn_alert.py +6 -8
- honeycomb/_generated/api/burn_alerts/delete_burn_alert.py +2 -5
- honeycomb/_generated/api/burn_alerts/get_burn_alert.py +2 -5
- honeycomb/_generated/api/burn_alerts/list_burn_alerts_by_slo.py +2 -5
- honeycomb/_generated/api/calculated_fields/create_calculated_field.py +2 -5
- honeycomb/_generated/api/calculated_fields/delete_calculated_field.py +2 -5
- honeycomb/_generated/api/calculated_fields/get_calculated_field.py +2 -5
- honeycomb/_generated/api/calculated_fields/list_calculated_fields.py +2 -8
- honeycomb/_generated/api/calculated_fields/update_calculated_field.py +2 -5
- honeycomb/_generated/api/columns/create_column.py +2 -5
- honeycomb/_generated/api/columns/delete_column.py +2 -5
- honeycomb/_generated/api/columns/get_column.py +2 -5
- honeycomb/_generated/api/columns/list_columns.py +2 -8
- honeycomb/_generated/api/columns/update_column.py +2 -5
- honeycomb/_generated/api/dataset_definitions/list_dataset_definitions.py +2 -5
- honeycomb/_generated/api/dataset_definitions/patch_dataset_definitions.py +2 -5
- honeycomb/_generated/api/datasets/create_dataset.py +2 -5
- honeycomb/_generated/api/datasets/delete_dataset.py +2 -5
- honeycomb/_generated/api/datasets/get_dataset.py +2 -5
- honeycomb/_generated/api/datasets/list_datasets.py +2 -5
- honeycomb/_generated/api/datasets/update_dataset.py +2 -5
- honeycomb/_generated/api/enhance/record_enhance_indexer_usage.py +4 -6
- honeycomb/_generated/api/environments/create_environment.py +2 -5
- honeycomb/_generated/api/environments/delete_environment.py +2 -5
- honeycomb/_generated/api/environments/get_environment.py +2 -5
- honeycomb/_generated/api/environments/list_environments.py +2 -7
- honeycomb/_generated/api/environments/update_environment.py +2 -5
- honeycomb/_generated/api/events/create_event.py +2 -7
- honeycomb/_generated/api/events/create_events.py +7 -11
- honeycomb/_generated/api/key_management/create_api_key.py +2 -5
- honeycomb/_generated/api/key_management/delete_api_key.py +2 -5
- honeycomb/_generated/api/key_management/get_api_key.py +2 -5
- honeycomb/_generated/api/key_management/list_api_keys.py +2 -7
- honeycomb/_generated/api/key_management/update_api_key.py +2 -5
- honeycomb/_generated/api/kinesis_events/create_kinesis_events.py +2 -5
- honeycomb/_generated/api/marker_settings/create_marker_setting.py +2 -5
- honeycomb/_generated/api/marker_settings/delete_marker_settings.py +2 -5
- honeycomb/_generated/api/marker_settings/list_marker_settings.py +2 -5
- honeycomb/_generated/api/marker_settings/update_marker_settings.py +2 -5
- honeycomb/_generated/api/markers/create_marker.py +2 -5
- honeycomb/_generated/api/markers/create_marker_v2.py +2 -5
- honeycomb/_generated/api/markers/delete_marker.py +2 -5
- honeycomb/_generated/api/markers/get_marker.py +2 -5
- honeycomb/_generated/api/markers/update_marker.py +2 -5
- honeycomb/_generated/api/markers/update_marker_v2.py +2 -5
- honeycomb/_generated/api/pipelines/get_pipeline_configuration.py +4 -8
- honeycomb/_generated/api/pipelines/record_pipeline_usage.py +4 -6
- honeycomb/_generated/api/pipelines/update_pipeline_configuration_rollout.py +6 -7
- honeycomb/_generated/api/queries/create_query.py +2 -5
- honeycomb/_generated/api/queries/get_query.py +2 -5
- honeycomb/_generated/api/query_annotations/create_query_annotation.py +2 -5
- honeycomb/_generated/api/query_annotations/delete_query_annotation.py +2 -5
- honeycomb/_generated/api/query_annotations/get_query_annotation.py +2 -5
- honeycomb/_generated/api/query_annotations/list_query_annotations.py +2 -7
- honeycomb/_generated/api/query_annotations/update_query_annotation.py +2 -5
- honeycomb/_generated/api/query_data/create_query_result.py +2 -5
- honeycomb/_generated/api/query_data/get_query_result.py +2 -5
- honeycomb/_generated/api/recipients/create_recipient.py +2 -6
- honeycomb/_generated/api/recipients/delete_recipient.py +2 -5
- honeycomb/_generated/api/recipients/get_recipient.py +2 -6
- honeycomb/_generated/api/recipients/list_recipients.py +2 -6
- honeycomb/_generated/api/recipients/update_recipient.py +2 -6
- honeycomb/_generated/api/reporting/get_slo_history.py +2 -5
- honeycomb/_generated/api/service_maps/create_map_dependency_request.py +6 -9
- honeycomb/_generated/api/service_maps/get_map_dependencies.py +2 -7
- honeycomb/_generated/api/sl_os/create_slo.py +2 -5
- honeycomb/_generated/api/sl_os/delete_slo.py +2 -5
- honeycomb/_generated/api/sl_os/get_slo.py +2 -8
- honeycomb/_generated/api/sl_os/list_slos.py +2 -5
- honeycomb/_generated/api/sl_os/update_slo.py +2 -5
- honeycomb/_generated/api/triggers/create_trigger.py +2 -6
- honeycomb/_generated/api/triggers/delete_trigger.py +2 -5
- honeycomb/_generated/api/triggers/get_trigger.py +2 -5
- honeycomb/_generated/api/triggers/list_triggers.py +2 -5
- honeycomb/_generated/api/triggers/list_triggers_with_recipient.py +2 -5
- honeycomb/_generated/api/triggers/update_trigger.py +2 -5
- honeycomb/_generated/client.py +2 -5
- honeycomb/_generated/models/__init__.py +195 -88
- honeycomb/_generated/models/api_key_create_request.py +6 -5
- honeycomb/_generated/models/api_key_create_request_data.py +15 -11
- honeycomb/_generated/models/api_key_create_request_data_relationships.py +2 -3
- honeycomb/_generated/models/api_key_create_request_data_type.py +1 -0
- honeycomb/_generated/models/api_key_list_response.py +2 -5
- honeycomb/_generated/models/api_key_object.py +14 -14
- honeycomb/_generated/models/api_key_object_links.py +2 -9
- honeycomb/_generated/models/api_key_object_relationships.py +5 -9
- honeycomb/_generated/models/api_key_object_type.py +1 -0
- honeycomb/_generated/models/api_key_response.py +2 -3
- honeycomb/_generated/models/api_key_update_request.py +15 -17
- honeycomb/_generated/models/auth.py +2 -5
- honeycomb/_generated/models/auth_api_key_access.py +2 -9
- honeycomb/_generated/models/auth_environment.py +2 -9
- honeycomb/_generated/models/auth_team.py +2 -9
- honeycomb/_generated/models/auth_type.py +1 -0
- honeycomb/_generated/models/auth_v2_response.py +5 -8
- honeycomb/_generated/models/auth_v2_response_data.py +14 -11
- honeycomb/_generated/models/auth_v2_response_data_attributes.py +10 -9
- honeycomb/_generated/models/auth_v2_response_data_attributes_key_type.py +1 -0
- honeycomb/_generated/models/auth_v2_response_data_attributes_timestamps.py +4 -12
- honeycomb/_generated/models/auth_v2_response_data_relationships.py +1 -7
- honeycomb/_generated/models/auth_v2_response_data_type.py +1 -0
- honeycomb/_generated/models/base_trigger.py +21 -18
- honeycomb/_generated/models/base_trigger_alert_type.py +1 -0
- honeycomb/_generated/models/base_trigger_baseline_details_type_0.py +5 -9
- honeycomb/_generated/models/base_trigger_baseline_details_type_0_offset_minutes.py +1 -0
- honeycomb/_generated/models/base_trigger_baseline_details_type_0_type.py +1 -0
- honeycomb/_generated/models/base_trigger_evaluation_schedule.py +8 -6
- honeycomb/_generated/models/base_trigger_evaluation_schedule_type.py +1 -0
- honeycomb/_generated/models/base_trigger_evaluation_schedule_window.py +4 -9
- honeycomb/_generated/models/base_trigger_evaluation_schedule_window_days_of_week_item.py +1 -0
- honeycomb/_generated/models/base_trigger_threshold.py +2 -9
- honeycomb/_generated/models/base_trigger_threshold_op.py +1 -0
- honeycomb/_generated/models/batch_event.py +2 -5
- honeycomb/_generated/models/board.py +15 -17
- honeycomb/_generated/models/board_layout_generation.py +1 -0
- honeycomb/_generated/models/board_links.py +2 -9
- honeycomb/_generated/models/board_panel_position.py +2 -9
- honeycomb/_generated/models/board_query_visualization_settings.py +8 -8
- honeycomb/_generated/models/board_query_visualization_settings_charts_item.py +4 -10
- honeycomb/_generated/models/board_query_visualization_settings_charts_item_chart_type.py +1 -0
- honeycomb/_generated/models/board_type.py +1 -0
- honeycomb/_generated/models/board_view_filter.py +2 -9
- honeycomb/_generated/models/board_view_filter_operation.py +1 -0
- honeycomb/_generated/models/board_view_response.py +2 -5
- honeycomb/_generated/models/budget_rate.py +4 -12
- honeycomb/_generated/models/budget_rate_alert_type.py +1 -0
- honeycomb/_generated/models/burn_alert_shared_params.py +4 -12
- honeycomb/_generated/models/calculated_field.py +2 -9
- honeycomb/_generated/models/configuration_key_attributes.py +16 -12
- honeycomb/_generated/models/configuration_key_attributes_key_type.py +1 -0
- honeycomb/_generated/models/configuration_key_attributes_permissions.py +172 -0
- honeycomb/_generated/models/configuration_key_attributes_timestamps.py +4 -12
- honeycomb/_generated/models/configuration_key_request.py +101 -0
- honeycomb/_generated/models/configuration_key_request_attributes.py +107 -0
- honeycomb/_generated/models/configuration_key_request_attributes_permissions.py +168 -0
- honeycomb/_generated/models/configuration_key_request_type.py +8 -0
- honeycomb/_generated/models/create_board_view_request.py +2 -3
- honeycomb/_generated/models/create_budget_rate_burn_alert_request.py +10 -10
- honeycomb/_generated/models/create_budget_rate_burn_alert_request_slo.py +1 -7
- honeycomb/_generated/models/create_column.py +2 -9
- honeycomb/_generated/models/create_column_type.py +1 -0
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request.py +8 -6
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data.py +10 -7
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes.py +8 -8
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data.py +8 -6
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data_resource_metrics_item.py +8 -6
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item.py +8 -6
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item.py +8 -8
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum.py +10 -7
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum_aggregation_temporality.py +1 -0
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum_datapoints_item.py +8 -6
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum_datapoints_item_attributes_item.py +8 -6
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum_datapoints_item_attributes_item_value.py +2 -9
- honeycomb/_generated/models/create_enhance_indexer_usage_record_request_data_type.py +1 -0
- honeycomb/_generated/models/create_environment_request.py +8 -6
- honeycomb/_generated/models/create_environment_request_data.py +10 -7
- honeycomb/_generated/models/create_environment_request_data_attributes.py +2 -9
- honeycomb/_generated/models/create_environment_request_data_type.py +1 -0
- honeycomb/_generated/models/create_events_content_encoding.py +1 -0
- honeycomb/_generated/models/create_events_response_200_item.py +2 -9
- honeycomb/_generated/models/create_exhaustion_time_burn_alert_request.py +10 -10
- honeycomb/_generated/models/create_exhaustion_time_burn_alert_request_slo.py +1 -7
- honeycomb/_generated/models/create_map_dependencies_request.py +2 -5
- honeycomb/_generated/models/create_map_dependencies_response.py +4 -10
- honeycomb/_generated/models/create_map_dependencies_response_status.py +1 -0
- honeycomb/_generated/models/create_pipeline_health_record_request.py +8 -6
- honeycomb/_generated/models/create_pipeline_health_record_request_data.py +10 -7
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes.py +8 -8
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data.py +8 -6
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data_resource_metrics_item.py +8 -6
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item.py +8 -6
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item.py +8 -8
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum.py +10 -7
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum_aggregation_temporality.py +1 -0
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum_datapoints_item.py +8 -6
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum_datapoints_item_attributes_item.py +8 -6
- honeycomb/_generated/models/create_pipeline_health_record_request_data_attributes_usage_data_resource_metrics_item_scope_metrics_item_metrics_item_sum_datapoints_item_attributes_item_value.py +2 -9
- honeycomb/_generated/models/create_pipeline_health_record_request_data_type.py +1 -0
- honeycomb/_generated/models/create_query_result_request.py +2 -9
- honeycomb/_generated/models/dataset.py +2 -6
- honeycomb/_generated/models/dataset_creation_payload.py +2 -9
- honeycomb/_generated/models/dataset_definition_type_1.py +4 -10
- honeycomb/_generated/models/dataset_definition_type_1_column_type.py +1 -0
- honeycomb/_generated/models/dataset_definitions.py +2 -6
- honeycomb/_generated/models/dataset_relationship.py +2 -3
- honeycomb/_generated/models/dataset_relationship_data.py +2 -8
- honeycomb/_generated/models/dataset_relationship_data_type.py +1 -0
- honeycomb/_generated/models/dataset_settings.py +2 -9
- honeycomb/_generated/models/dataset_update_payload.py +8 -8
- honeycomb/_generated/models/dataset_update_payload_settings.py +2 -9
- honeycomb/_generated/models/detailed_error.py +2 -9
- honeycomb/_generated/models/email_recipient.py +4 -7
- honeycomb/_generated/models/email_recipient_details.py +1 -7
- honeycomb/_generated/models/email_recipient_type.py +1 -0
- honeycomb/_generated/models/environment.py +6 -7
- honeycomb/_generated/models/environment_attributes.py +11 -9
- honeycomb/_generated/models/environment_attributes_color_type_1.py +1 -0
- honeycomb/_generated/models/environment_attributes_settings.py +1 -7
- honeycomb/_generated/models/environment_color.py +1 -0
- honeycomb/_generated/models/environment_links.py +1 -7
- honeycomb/_generated/models/environment_list_response.py +2 -5
- honeycomb/_generated/models/environment_relationship.py +8 -6
- honeycomb/_generated/models/environment_relationship_data.py +3 -8
- honeycomb/_generated/models/environment_relationship_data_type.py +1 -0
- honeycomb/_generated/models/environment_response.py +2 -3
- honeycomb/_generated/models/environment_type.py +1 -0
- honeycomb/_generated/models/error.py +2 -9
- honeycomb/_generated/models/event.py +2 -8
- honeycomb/_generated/models/exhaustion_time.py +4 -12
- honeycomb/_generated/models/exhaustion_time_alert_type.py +1 -0
- honeycomb/_generated/models/exhaustion_time_burn_alert_list_response.py +10 -10
- honeycomb/_generated/models/exhaustion_time_burn_alert_list_response_slo.py +2 -9
- honeycomb/_generated/models/filter_op.py +1 -0
- honeycomb/_generated/models/get_map_dependencies_response.py +7 -10
- honeycomb/_generated/models/get_map_dependencies_response_status.py +1 -0
- honeycomb/_generated/models/having_calculate_op.py +1 -0
- honeycomb/_generated/models/having_op.py +1 -0
- honeycomb/_generated/models/included_resource.py +6 -7
- honeycomb/_generated/models/included_resource_attributes.py +1 -7
- honeycomb/_generated/models/ingest_key_attributes.py +14 -11
- honeycomb/_generated/models/ingest_key_attributes_key_type.py +1 -0
- honeycomb/_generated/models/ingest_key_attributes_permissions.py +2 -9
- honeycomb/_generated/models/ingest_key_attributes_timestamps.py +4 -12
- honeycomb/_generated/models/ingest_key_request.py +100 -0
- honeycomb/_generated/models/ingest_key_request_attributes.py +75 -0
- honeycomb/_generated/models/ingest_key_request_type.py +8 -0
- honeycomb/_generated/models/ingest_key_type.py +2 -9
- honeycomb/_generated/models/ingest_key_type_key_type.py +1 -0
- honeycomb/_generated/models/jsonapi_error_source.py +2 -9
- honeycomb/_generated/models/kinesis_event.py +2 -5
- honeycomb/_generated/models/kinesis_event_record.py +2 -9
- honeycomb/_generated/models/kinesis_response.py +2 -9
- honeycomb/_generated/models/list_api_keys_filtertype.py +1 -0
- honeycomb/_generated/models/map_dependency.py +2 -5
- honeycomb/_generated/models/map_node.py +2 -9
- honeycomb/_generated/models/map_node_type.py +1 -0
- honeycomb/_generated/models/marker.py +2 -9
- honeycomb/_generated/models/marker_create_request.py +2 -3
- honeycomb/_generated/models/marker_create_request_data.py +16 -10
- honeycomb/_generated/models/marker_create_request_data_attributes.py +2 -9
- honeycomb/_generated/models/marker_create_request_data_relationships.py +2 -3
- honeycomb/_generated/models/marker_create_request_data_type.py +1 -0
- honeycomb/_generated/models/marker_object.py +7 -8
- honeycomb/_generated/models/marker_object_attributes.py +8 -8
- honeycomb/_generated/models/marker_object_attributes_timestamps.py +4 -12
- honeycomb/_generated/models/marker_object_links.py +2 -9
- honeycomb/_generated/models/marker_object_relationships.py +8 -8
- honeycomb/_generated/models/marker_object_relationships_dataset.py +8 -9
- honeycomb/_generated/models/marker_object_relationships_dataset_data_type_0.py +4 -10
- honeycomb/_generated/models/marker_object_relationships_dataset_data_type_0_type.py +1 -0
- honeycomb/_generated/models/marker_object_type.py +1 -0
- honeycomb/_generated/models/marker_response.py +2 -3
- honeycomb/_generated/models/marker_setting.py +2 -10
- honeycomb/_generated/models/marker_update_request.py +2 -3
- honeycomb/_generated/models/marker_update_request_data.py +16 -10
- honeycomb/_generated/models/marker_update_request_data_attributes.py +2 -9
- honeycomb/_generated/models/marker_update_request_data_relationships.py +2 -3
- honeycomb/_generated/models/marker_update_request_data_type.py +1 -0
- honeycomb/_generated/models/ms_teams_recipient.py +4 -7
- honeycomb/_generated/models/ms_teams_recipient_details.py +1 -7
- honeycomb/_generated/models/ms_teams_recipient_type.py +1 -0
- honeycomb/_generated/models/ms_teams_workflow_recipient.py +12 -11
- honeycomb/_generated/models/ms_teams_workflow_recipient_details.py +1 -7
- honeycomb/_generated/models/ms_teams_workflow_recipient_type.py +1 -0
- honeycomb/_generated/models/notification_recipient.py +8 -8
- honeycomb/_generated/models/notification_recipient_details.py +10 -9
- honeycomb/_generated/models/notification_recipient_details_pagerduty_severity.py +1 -0
- honeycomb/_generated/models/notification_recipient_details_variables_item.py +2 -9
- honeycomb/_generated/models/pager_duty_recipient.py +8 -9
- honeycomb/_generated/models/pager_duty_recipient_details.py +1 -7
- honeycomb/_generated/models/pager_duty_recipient_type.py +1 -0
- honeycomb/_generated/models/pagination_links.py +2 -8
- honeycomb/_generated/models/payload_template.py +2 -9
- honeycomb/_generated/models/pipeline_configuration_response.py +16 -10
- honeycomb/_generated/models/pipeline_configuration_response_attributes.py +8 -8
- honeycomb/_generated/models/pipeline_configuration_response_attributes_configs_item.py +1 -7
- honeycomb/_generated/models/pipeline_configuration_response_links.py +2 -9
- honeycomb/_generated/models/pipeline_configuration_response_type.py +1 -0
- honeycomb/_generated/models/pipeline_configuration_rollout.py +16 -10
- honeycomb/_generated/models/pipeline_configuration_rollout_attributes.py +3 -8
- honeycomb/_generated/models/pipeline_configuration_rollout_attributes_status.py +1 -0
- honeycomb/_generated/models/pipeline_configuration_rollout_links.py +2 -9
- honeycomb/_generated/models/pipeline_configuration_rollout_type.py +1 -0
- honeycomb/_generated/models/preset_filter.py +1 -7
- honeycomb/_generated/models/query.py +11 -11
- honeycomb/_generated/models/query_annotation.py +4 -12
- honeycomb/_generated/models/query_annotation_source.py +1 -0
- honeycomb/_generated/models/query_calculated_fields_item.py +1 -7
- honeycomb/_generated/models/query_calculations_item.py +2 -10
- honeycomb/_generated/models/query_compare_time_offset_seconds.py +1 -0
- honeycomb/_generated/models/query_filter_combination.py +1 -0
- honeycomb/_generated/models/query_filters_item.py +2 -11
- honeycomb/_generated/models/query_havings_item.py +2 -10
- honeycomb/_generated/models/query_op.py +1 -0
- honeycomb/_generated/models/query_orders_item.py +2 -9
- honeycomb/_generated/models/query_orders_item_order.py +1 -0
- honeycomb/_generated/models/query_panel.py +2 -6
- honeycomb/_generated/models/query_panel_query_panel.py +10 -9
- honeycomb/_generated/models/query_panel_query_panel_query_style.py +1 -0
- honeycomb/_generated/models/query_result.py +2 -5
- honeycomb/_generated/models/query_result_details.py +5 -8
- honeycomb/_generated/models/query_result_details_data.py +5 -8
- honeycomb/_generated/models/query_result_details_links.py +2 -9
- honeycomb/_generated/models/query_result_links.py +2 -9
- honeycomb/_generated/models/query_results_data.py +2 -5
- honeycomb/_generated/models/query_results_data_data.py +2 -8
- honeycomb/_generated/models/query_results_series.py +2 -5
- honeycomb/_generated/models/recipient_properties.py +4 -12
- honeycomb/_generated/models/recipient_type.py +1 -0
- honeycomb/_generated/models/slack_recipient.py +4 -7
- honeycomb/_generated/models/slack_recipient_details.py +1 -7
- honeycomb/_generated/models/slack_recipient_type.py +1 -0
- honeycomb/_generated/models/slo.py +4 -8
- honeycomb/_generated/models/slo_create.py +4 -8
- honeycomb/_generated/models/slo_create_sli.py +1 -7
- honeycomb/_generated/models/slo_detailed_response.py +4 -8
- honeycomb/_generated/models/slo_detailed_response_status.py +1 -0
- honeycomb/_generated/models/slo_history.py +2 -9
- honeycomb/_generated/models/slo_history_request.py +2 -8
- honeycomb/_generated/models/slo_history_response.py +2 -3
- honeycomb/_generated/models/slo_panel.py +2 -6
- honeycomb/_generated/models/slo_panel_slo_panel.py +2 -9
- honeycomb/_generated/models/slo_sli.py +1 -7
- honeycomb/_generated/models/tag.py +1 -7
- honeycomb/_generated/models/team_relationship.py +2 -3
- honeycomb/_generated/models/team_relationship_team.py +6 -5
- honeycomb/_generated/models/team_relationship_team_data.py +3 -8
- honeycomb/_generated/models/team_relationship_team_data_type.py +1 -0
- honeycomb/_generated/models/template_variable_definition.py +2 -9
- honeycomb/_generated/models/text_panel.py +2 -6
- honeycomb/_generated/models/text_panel_text_panel.py +1 -7
- honeycomb/_generated/models/trigger_response.py +27 -21
- honeycomb/_generated/models/trigger_with_inline_query.py +27 -21
- honeycomb/_generated/models/trigger_with_inline_query_query.py +1 -7
- honeycomb/_generated/models/trigger_with_query_reference.py +21 -18
- honeycomb/_generated/models/update_board_view_request.py +2 -5
- honeycomb/_generated/models/update_environment_request.py +8 -6
- honeycomb/_generated/models/update_environment_request_data.py +10 -7
- honeycomb/_generated/models/update_environment_request_data_attributes.py +8 -8
- honeycomb/_generated/models/update_environment_request_data_attributes_settings.py +2 -9
- honeycomb/_generated/models/update_environment_request_data_type.py +1 -0
- honeycomb/_generated/models/update_exhaustion_time_burn_alert_request.py +4 -7
- honeycomb/_generated/models/update_pipeline_configuration_rollout.py +10 -7
- honeycomb/_generated/models/update_pipeline_configuration_rollout_attributes.py +3 -8
- honeycomb/_generated/models/update_pipeline_configuration_rollout_attributes_status.py +1 -0
- honeycomb/_generated/models/update_pipeline_configuration_rollout_request.py +8 -6
- honeycomb/_generated/models/update_pipeline_configuration_rollout_request_data.py +10 -7
- honeycomb/_generated/models/update_pipeline_configuration_rollout_request_data_attributes.py +3 -8
- honeycomb/_generated/models/update_pipeline_configuration_rollout_request_data_attributes_status.py +1 -0
- honeycomb/_generated/models/update_pipeline_configuration_rollout_request_data_type.py +1 -0
- honeycomb/_generated/models/update_pipeline_configuration_rollout_response.py +8 -6
- honeycomb/_generated/models/update_pipeline_configuration_rollout_type.py +1 -0
- honeycomb/_generated/models/user_relationship.py +2 -3
- honeycomb/_generated/models/user_relationship_data.py +2 -8
- honeycomb/_generated/models/user_relationship_data_type.py +1 -0
- honeycomb/_generated/models/validation_error.py +8 -8
- honeycomb/_generated/models/validation_error_type_detail_item.py +4 -10
- honeycomb/_generated/models/validation_error_type_detail_item_code.py +1 -0
- honeycomb/_generated/models/webhook_header.py +2 -9
- honeycomb/_generated/models/webhook_recipient.py +4 -7
- honeycomb/_generated/models/webhook_recipient_details.py +8 -8
- honeycomb/_generated/models/webhook_recipient_details_webhook_payloads.py +12 -10
- honeycomb/_generated/models/webhook_recipient_details_webhook_payloads_payload_templates.py +2 -5
- honeycomb/_generated/models/webhook_recipient_type.py +1 -0
- honeycomb/_generated/types.py +1 -1
- honeycomb/cli/__init__.py +85 -0
- honeycomb/cli/api_keys.py +205 -0
- honeycomb/cli/auth.py +70 -0
- honeycomb/cli/boards.py +203 -0
- honeycomb/cli/columns.py +256 -0
- honeycomb/cli/config.py +259 -0
- honeycomb/cli/datasets.py +186 -0
- honeycomb/cli/derived_columns.py +222 -0
- honeycomb/cli/environments.py +240 -0
- honeycomb/cli/formatters.py +242 -0
- honeycomb/cli/markers.py +151 -0
- honeycomb/cli/queries.py +380 -0
- honeycomb/cli/recipients.py +211 -0
- honeycomb/cli/slos.py +338 -0
- honeycomb/cli/triggers.py +271 -0
- honeycomb/client.py +13 -7
- honeycomb/models/__init__.py +18 -3
- honeycomb/models/api_keys.py +49 -5
- honeycomb/models/auth.py +71 -0
- honeycomb/models/board_builder.py +97 -25
- honeycomb/models/boards.py +95 -1
- honeycomb/models/datasets.py +42 -0
- honeycomb/models/queries.py +21 -0
- honeycomb/models/query_builder.py +119 -59
- honeycomb/models/recipient_builder.py +51 -4
- honeycomb/models/slo_builder.py +24 -19
- honeycomb/models/slos.py +50 -3
- honeycomb/models/tool_inputs.py +630 -0
- honeycomb/models/triggers.py +39 -9
- honeycomb/resources/__init__.py +2 -0
- honeycomb/resources/api_keys.py +66 -29
- honeycomb/resources/auth.py +97 -0
- honeycomb/resources/boards.py +272 -18
- honeycomb/resources/datasets.py +49 -11
- honeycomb/resources/environments.py +54 -31
- honeycomb/resources/slos.py +87 -61
- honeycomb/tools/__main__.py +40 -4
- honeycomb/tools/builders.py +235 -150
- honeycomb/tools/descriptions.py +175 -7
- honeycomb/tools/executor.py +212 -25
- honeycomb/tools/generator.py +61 -2264
- honeycomb/tools/resources/__init__.py +42 -0
- honeycomb/tools/resources/api_keys.py +249 -0
- honeycomb/tools/resources/auth.py +98 -0
- honeycomb/tools/resources/boards.py +313 -0
- honeycomb/tools/resources/burn_alerts.py +278 -0
- honeycomb/tools/resources/columns.py +257 -0
- honeycomb/tools/resources/datasets.py +215 -0
- honeycomb/tools/resources/derived_columns.py +311 -0
- honeycomb/tools/resources/environments.py +211 -0
- honeycomb/tools/resources/events.py +158 -0
- honeycomb/tools/resources/marker_settings.py +216 -0
- honeycomb/tools/resources/markers.py +197 -0
- honeycomb/tools/resources/queries.py +220 -0
- honeycomb/tools/resources/recipients.py +297 -0
- honeycomb/tools/resources/service_map.py +110 -0
- honeycomb/tools/resources/slos.py +276 -0
- honeycomb/tools/resources/triggers.py +328 -0
- honeycomb/tools/schemas.py +81 -0
- {honeycomb_api-0.1.0.dist-info → honeycomb_api-0.5.3.dist-info}/METADATA +33 -8
- honeycomb_api-0.5.3.dist-info/RECORD +497 -0
- honeycomb_api-0.5.3.dist-info/entry_points.txt +5 -0
- honeycomb_api-0.1.0.dist-info/RECORD +0 -453
- {honeycomb_api-0.1.0.dist-info → honeycomb_api-0.5.3.dist-info}/WHEEL +0 -0
- {honeycomb_api-0.1.0.dist-info → honeycomb_api-0.5.3.dist-info}/licenses/LICENSE +0 -0
honeycomb/cli/slos.py
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SLO management commands.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from honeycomb.cli.config import get_client
|
|
12
|
+
from honeycomb.cli.formatters import DEFAULT_OUTPUT_FORMAT, OutputFormat, output_result
|
|
13
|
+
from honeycomb.models.slos import SLOCreate
|
|
14
|
+
|
|
15
|
+
app = typer.Typer(help="Manage SLOs (Service Level Objectives)")
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.command("list")
|
|
20
|
+
def list_slos(
|
|
21
|
+
dataset: str = typer.Option(
|
|
22
|
+
"__all__", "--dataset", "-d", help="Dataset slug (default: __all__ for environment-wide)"
|
|
23
|
+
),
|
|
24
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
25
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
26
|
+
output: OutputFormat = typer.Option(DEFAULT_OUTPUT_FORMAT, "--output", "-o"),
|
|
27
|
+
quiet: bool = typer.Option(False, "--quiet", "-q", help="Only output SLO IDs"),
|
|
28
|
+
) -> None:
|
|
29
|
+
"""List all SLOs (environment-wide by default, or in a specific dataset)."""
|
|
30
|
+
try:
|
|
31
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
32
|
+
slos = client.slos.list(dataset=dataset)
|
|
33
|
+
|
|
34
|
+
# Add computed columns for table display
|
|
35
|
+
slos_with_computed = []
|
|
36
|
+
for slo in slos:
|
|
37
|
+
slo_dict = slo.model_dump(mode="json")
|
|
38
|
+
# Show "environment-wide" or comma-separated dataset slugs
|
|
39
|
+
if slo.dataset_slugs and len(slo.dataset_slugs) > 0:
|
|
40
|
+
if slo.dataset_slugs == ["__all__"]:
|
|
41
|
+
slo_dict["datasets"] = "environment-wide"
|
|
42
|
+
else:
|
|
43
|
+
slo_dict["datasets"] = ", ".join(slo.dataset_slugs)
|
|
44
|
+
else:
|
|
45
|
+
slo_dict["datasets"] = "environment-wide"
|
|
46
|
+
# Add target_percentage for user-friendly display
|
|
47
|
+
slo_dict["target_percentage"] = slo.target_percentage
|
|
48
|
+
slos_with_computed.append(slo_dict)
|
|
49
|
+
|
|
50
|
+
output_result(
|
|
51
|
+
slos_with_computed if output == OutputFormat.table else slos,
|
|
52
|
+
output,
|
|
53
|
+
columns=[
|
|
54
|
+
"id",
|
|
55
|
+
"name",
|
|
56
|
+
"datasets",
|
|
57
|
+
"target_percentage",
|
|
58
|
+
"time_period_days",
|
|
59
|
+
"created_at",
|
|
60
|
+
],
|
|
61
|
+
quiet=quiet,
|
|
62
|
+
)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
65
|
+
raise typer.Exit(1)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@app.command("get")
|
|
69
|
+
def get_slo(
|
|
70
|
+
slo_id: str = typer.Argument(..., help="SLO ID"),
|
|
71
|
+
dataset: str | None = typer.Option(
|
|
72
|
+
None, "--dataset", "-d", help="Dataset slug (auto-detected if not provided)"
|
|
73
|
+
),
|
|
74
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
75
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
76
|
+
output: OutputFormat = typer.Option(DEFAULT_OUTPUT_FORMAT, "--output", "-o"),
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Get a specific SLO."""
|
|
79
|
+
try:
|
|
80
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
81
|
+
|
|
82
|
+
# If dataset not provided, find it by listing all SLOs
|
|
83
|
+
if dataset is None:
|
|
84
|
+
all_slos = client.slos.list(dataset="__all__")
|
|
85
|
+
matching = [s for s in all_slos if s.id == slo_id]
|
|
86
|
+
if not matching:
|
|
87
|
+
console.print(f"[red]Error:[/red] SLO {slo_id} not found", style="bold")
|
|
88
|
+
raise typer.Exit(1)
|
|
89
|
+
|
|
90
|
+
slo = matching[0]
|
|
91
|
+
# Check if SLO spans multiple datasets
|
|
92
|
+
if slo.dataset_slugs and len(slo.dataset_slugs) > 1:
|
|
93
|
+
datasets_str = ", ".join(slo.dataset_slugs)
|
|
94
|
+
console.print(
|
|
95
|
+
f"[yellow]Note:[/yellow] SLO {slo_id} spans multiple datasets: {datasets_str}"
|
|
96
|
+
)
|
|
97
|
+
console.print(
|
|
98
|
+
"[yellow]Fetching from first dataset. Use --dataset to specify a different one.[/yellow]"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
found_dataset = slo.dataset
|
|
102
|
+
if not found_dataset:
|
|
103
|
+
console.print(f"[red]Error:[/red] SLO {slo_id} has no dataset", style="bold")
|
|
104
|
+
raise typer.Exit(1)
|
|
105
|
+
dataset = found_dataset
|
|
106
|
+
console.print(f"[dim]Found SLO in dataset: {dataset}[/dim]")
|
|
107
|
+
# We already have the SLO from the list, just output it
|
|
108
|
+
output_result(slo, output)
|
|
109
|
+
else:
|
|
110
|
+
# Dataset provided, fetch directly
|
|
111
|
+
slo = client.slos.get(dataset=dataset, slo_id=slo_id)
|
|
112
|
+
output_result(slo, output)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
115
|
+
raise typer.Exit(1)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@app.command("create")
|
|
119
|
+
def create_slo(
|
|
120
|
+
dataset: str = typer.Option(..., "--dataset", "-d", help="Dataset slug"),
|
|
121
|
+
from_file: Path = typer.Option(..., "--from-file", "-f", help="JSON file with SLO config"),
|
|
122
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
123
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
124
|
+
output: OutputFormat = typer.Option(DEFAULT_OUTPUT_FORMAT, "--output", "-o"),
|
|
125
|
+
) -> None:
|
|
126
|
+
"""Create an SLO from a JSON file."""
|
|
127
|
+
try:
|
|
128
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
129
|
+
|
|
130
|
+
# Load and parse JSON file
|
|
131
|
+
data = json.loads(from_file.read_text())
|
|
132
|
+
|
|
133
|
+
# Strip fields that shouldn't be in create request
|
|
134
|
+
data.pop("id", None)
|
|
135
|
+
data.pop("created_at", None)
|
|
136
|
+
data.pop("updated_at", None)
|
|
137
|
+
|
|
138
|
+
slo_create = SLOCreate.model_validate(data)
|
|
139
|
+
slo = client.slos.create(dataset=dataset, slo=slo_create)
|
|
140
|
+
|
|
141
|
+
console.print(f"[green]Created SLO '{slo.name}' with ID: {slo.id}[/green]")
|
|
142
|
+
output_result(slo, output)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
145
|
+
raise typer.Exit(1)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@app.command("update")
|
|
149
|
+
def update_slo(
|
|
150
|
+
slo_id: str = typer.Argument(..., help="SLO ID"),
|
|
151
|
+
dataset: str = typer.Option(..., "--dataset", "-d", help="Dataset slug"),
|
|
152
|
+
from_file: Path = typer.Option(..., "--from-file", "-f", help="JSON file with SLO config"),
|
|
153
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
154
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
155
|
+
output: OutputFormat = typer.Option(DEFAULT_OUTPUT_FORMAT, "--output", "-o"),
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Update an existing SLO."""
|
|
158
|
+
try:
|
|
159
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
160
|
+
|
|
161
|
+
# Load and parse JSON file
|
|
162
|
+
data = json.loads(from_file.read_text())
|
|
163
|
+
|
|
164
|
+
# Strip fields that shouldn't be in update request
|
|
165
|
+
data.pop("id", None)
|
|
166
|
+
data.pop("created_at", None)
|
|
167
|
+
data.pop("updated_at", None)
|
|
168
|
+
|
|
169
|
+
slo_update = SLOCreate.model_validate(data)
|
|
170
|
+
slo = client.slos.update(dataset=dataset, slo_id=slo_id, slo=slo_update)
|
|
171
|
+
|
|
172
|
+
console.print(f"[green]Updated SLO '{slo.name}'[/green]")
|
|
173
|
+
output_result(slo, output)
|
|
174
|
+
except Exception as e:
|
|
175
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
176
|
+
raise typer.Exit(1)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@app.command("delete")
|
|
180
|
+
def delete_slo(
|
|
181
|
+
slo_id: str = typer.Argument(..., help="SLO ID"),
|
|
182
|
+
dataset: str | None = typer.Option(
|
|
183
|
+
None, "--dataset", "-d", help="Dataset slug (auto-detected if not provided)"
|
|
184
|
+
),
|
|
185
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
186
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
187
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
|
|
188
|
+
) -> None:
|
|
189
|
+
"""Delete an SLO."""
|
|
190
|
+
try:
|
|
191
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
192
|
+
|
|
193
|
+
# If dataset not provided, find it by listing all SLOs
|
|
194
|
+
if dataset is None:
|
|
195
|
+
all_slos = client.slos.list(dataset="__all__")
|
|
196
|
+
matching = [s for s in all_slos if s.id == slo_id]
|
|
197
|
+
if not matching:
|
|
198
|
+
console.print(f"[red]Error:[/red] SLO {slo_id} not found", style="bold")
|
|
199
|
+
raise typer.Exit(1)
|
|
200
|
+
|
|
201
|
+
slo = matching[0]
|
|
202
|
+
# Check if SLO spans multiple datasets - must use __all__ to delete
|
|
203
|
+
if slo.dataset_slugs and len(slo.dataset_slugs) > 1:
|
|
204
|
+
datasets_str = ", ".join(slo.dataset_slugs)
|
|
205
|
+
console.print(f"[dim]SLO {slo_id} spans multiple datasets: {datasets_str}[/dim]")
|
|
206
|
+
dataset = "__all__"
|
|
207
|
+
console.print("[dim]Using dataset=__all__ for deletion[/dim]")
|
|
208
|
+
else:
|
|
209
|
+
found_dataset = slo.dataset
|
|
210
|
+
if not found_dataset:
|
|
211
|
+
console.print(f"[red]Error:[/red] SLO {slo_id} has no dataset", style="bold")
|
|
212
|
+
raise typer.Exit(1)
|
|
213
|
+
dataset = found_dataset
|
|
214
|
+
console.print(f"[dim]Found SLO in dataset: {dataset}[/dim]")
|
|
215
|
+
else:
|
|
216
|
+
# If dataset is explicitly provided for a multi-dataset SLO and it's not __all__, error
|
|
217
|
+
all_slos = client.slos.list(dataset="__all__")
|
|
218
|
+
matching = [s for s in all_slos if s.id == slo_id]
|
|
219
|
+
if matching:
|
|
220
|
+
slo = matching[0]
|
|
221
|
+
if slo.dataset_slugs and len(slo.dataset_slugs) > 1 and dataset != "__all__":
|
|
222
|
+
datasets_str = ", ".join(slo.dataset_slugs)
|
|
223
|
+
console.print(
|
|
224
|
+
f"[red]Error:[/red] SLO {slo_id} spans multiple datasets: {datasets_str}",
|
|
225
|
+
style="bold",
|
|
226
|
+
)
|
|
227
|
+
console.print(
|
|
228
|
+
"[yellow]Multi-dataset SLOs can only be deleted with --dataset __all__[/yellow]"
|
|
229
|
+
)
|
|
230
|
+
raise typer.Exit(1)
|
|
231
|
+
|
|
232
|
+
if not yes:
|
|
233
|
+
confirm = typer.confirm(f"Delete SLO {slo_id} from dataset {dataset}?")
|
|
234
|
+
if not confirm:
|
|
235
|
+
console.print("[yellow]Cancelled[/yellow]")
|
|
236
|
+
raise typer.Exit(0)
|
|
237
|
+
|
|
238
|
+
client.slos.delete(dataset=dataset, slo_id=slo_id)
|
|
239
|
+
console.print(f"[green]Deleted SLO {slo_id}[/green]")
|
|
240
|
+
except Exception as e:
|
|
241
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
242
|
+
raise typer.Exit(1)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@app.command("export")
|
|
246
|
+
def export_slo(
|
|
247
|
+
slo_id: str = typer.Argument(..., help="SLO ID"),
|
|
248
|
+
dataset: str | None = typer.Option(
|
|
249
|
+
None, "--dataset", "-d", help="Dataset slug (auto-detected if not provided)"
|
|
250
|
+
),
|
|
251
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
252
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
253
|
+
output_file: Path | None = typer.Option(
|
|
254
|
+
None, "--output-file", "-o", help="Output file (default: stdout)"
|
|
255
|
+
),
|
|
256
|
+
) -> None:
|
|
257
|
+
"""
|
|
258
|
+
Export an SLO as JSON.
|
|
259
|
+
|
|
260
|
+
Output is suitable for importing to another environment via the 'create' command.
|
|
261
|
+
"""
|
|
262
|
+
try:
|
|
263
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
264
|
+
|
|
265
|
+
# If dataset not provided, find it by listing all SLOs
|
|
266
|
+
if dataset is None:
|
|
267
|
+
all_slos = client.slos.list(dataset="__all__")
|
|
268
|
+
matching = [s for s in all_slos if s.id == slo_id]
|
|
269
|
+
if not matching:
|
|
270
|
+
console.print(f"[red]Error:[/red] SLO {slo_id} not found", style="bold")
|
|
271
|
+
raise typer.Exit(1)
|
|
272
|
+
|
|
273
|
+
slo = matching[0]
|
|
274
|
+
# Check if SLO spans multiple datasets
|
|
275
|
+
if slo.dataset_slugs and len(slo.dataset_slugs) > 1:
|
|
276
|
+
datasets_str = ", ".join(slo.dataset_slugs)
|
|
277
|
+
console.print(
|
|
278
|
+
f"[yellow]Warning:[/yellow] SLO {slo_id} spans multiple datasets: {datasets_str}"
|
|
279
|
+
)
|
|
280
|
+
console.print(
|
|
281
|
+
"[yellow]Exporting from first dataset. Use --dataset to specify a different one.[/yellow]"
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
found_dataset = slo.dataset
|
|
285
|
+
if not found_dataset:
|
|
286
|
+
console.print(f"[red]Error:[/red] SLO {slo_id} has no dataset", style="bold")
|
|
287
|
+
raise typer.Exit(1)
|
|
288
|
+
dataset = found_dataset
|
|
289
|
+
console.print(f"[dim]Exporting SLO from dataset: {dataset}[/dim]")
|
|
290
|
+
else:
|
|
291
|
+
# Dataset provided, fetch directly
|
|
292
|
+
slo = client.slos.get(dataset=dataset, slo_id=slo_id)
|
|
293
|
+
|
|
294
|
+
# Export without IDs/timestamps for portability
|
|
295
|
+
data = slo.model_dump(exclude={"id", "created_at", "updated_at"}, mode="json")
|
|
296
|
+
json_str = json.dumps(data, indent=2, default=str)
|
|
297
|
+
|
|
298
|
+
if output_file:
|
|
299
|
+
output_file.write_text(json_str)
|
|
300
|
+
console.print(f"[green]Exported SLO to {output_file}[/green]")
|
|
301
|
+
else:
|
|
302
|
+
console.print(json_str)
|
|
303
|
+
except Exception as e:
|
|
304
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
305
|
+
raise typer.Exit(1)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@app.command("export-all")
|
|
309
|
+
def export_all_slos(
|
|
310
|
+
dataset: str = typer.Option(..., "--dataset", "-d", help="Dataset slug"),
|
|
311
|
+
output_dir: Path = typer.Option(..., "--output-dir", help="Output directory"),
|
|
312
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
313
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
314
|
+
) -> None:
|
|
315
|
+
"""Export all SLOs from a dataset to individual JSON files."""
|
|
316
|
+
try:
|
|
317
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
318
|
+
|
|
319
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
320
|
+
slos = client.slos.list(dataset=dataset)
|
|
321
|
+
|
|
322
|
+
for slo in slos:
|
|
323
|
+
# Export without IDs/timestamps
|
|
324
|
+
data = slo.model_dump(exclude={"id", "created_at", "updated_at"}, mode="json")
|
|
325
|
+
|
|
326
|
+
# Sanitize filename (replace special chars with dash)
|
|
327
|
+
filename = f"{slo.name}.json".replace("/", "-").replace(" ", "-").lower()
|
|
328
|
+
file_path = output_dir / filename
|
|
329
|
+
|
|
330
|
+
with open(file_path, "w") as f:
|
|
331
|
+
json.dump(data, f, indent=2, default=str)
|
|
332
|
+
|
|
333
|
+
console.print(f"[green]Exported '{slo.name}' to {file_path}[/green]")
|
|
334
|
+
|
|
335
|
+
console.print(f"\n[bold green]Exported {len(slos)} SLOs to {output_dir}[/bold green]")
|
|
336
|
+
except Exception as e:
|
|
337
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
338
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Trigger management commands.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from honeycomb.cli.config import get_client
|
|
12
|
+
from honeycomb.cli.formatters import DEFAULT_OUTPUT_FORMAT, OutputFormat, output_result
|
|
13
|
+
from honeycomb.models.triggers import TriggerCreate
|
|
14
|
+
|
|
15
|
+
app = typer.Typer(help="Manage triggers (alerts)")
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.command("list")
|
|
20
|
+
def list_triggers(
|
|
21
|
+
dataset: str = typer.Option(
|
|
22
|
+
"__all__", "--dataset", "-d", help="Dataset slug (default: __all__ for environment-wide)"
|
|
23
|
+
),
|
|
24
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
25
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
26
|
+
output: OutputFormat = typer.Option(DEFAULT_OUTPUT_FORMAT, "--output", "-o"),
|
|
27
|
+
quiet: bool = typer.Option(False, "--quiet", "-q", help="Only output trigger IDs"),
|
|
28
|
+
) -> None:
|
|
29
|
+
"""List all triggers (environment-wide by default, or in a specific dataset)."""
|
|
30
|
+
try:
|
|
31
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
32
|
+
triggers = client.triggers.list(dataset=dataset)
|
|
33
|
+
|
|
34
|
+
# Add computed dataset column for table display
|
|
35
|
+
triggers_with_dataset = []
|
|
36
|
+
for trigger in triggers:
|
|
37
|
+
trigger_dict = trigger.model_dump(mode="json")
|
|
38
|
+
# Show "environment-wide" for environment-wide triggers, otherwise show dataset slug
|
|
39
|
+
if trigger.dataset_slug == "__all__":
|
|
40
|
+
trigger_dict["dataset"] = "environment-wide"
|
|
41
|
+
else:
|
|
42
|
+
trigger_dict["dataset"] = trigger.dataset_slug
|
|
43
|
+
triggers_with_dataset.append(trigger_dict)
|
|
44
|
+
|
|
45
|
+
output_result(
|
|
46
|
+
triggers_with_dataset if output == OutputFormat.table else triggers,
|
|
47
|
+
output,
|
|
48
|
+
columns=["id", "name", "dataset", "disabled", "frequency", "created_at"],
|
|
49
|
+
quiet=quiet,
|
|
50
|
+
)
|
|
51
|
+
except Exception as e:
|
|
52
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
53
|
+
raise typer.Exit(1)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@app.command("get")
|
|
57
|
+
def get_trigger(
|
|
58
|
+
trigger_id: str = typer.Argument(..., help="Trigger ID"),
|
|
59
|
+
dataset: str | None = typer.Option(
|
|
60
|
+
None, "--dataset", "-d", help="Dataset slug (auto-detected if not provided)"
|
|
61
|
+
),
|
|
62
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
63
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
64
|
+
output: OutputFormat = typer.Option(DEFAULT_OUTPUT_FORMAT, "--output", "-o"),
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Get a specific trigger."""
|
|
67
|
+
try:
|
|
68
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
69
|
+
|
|
70
|
+
# If dataset not provided, find it by listing all triggers
|
|
71
|
+
if dataset is None:
|
|
72
|
+
all_triggers = client.triggers.list(dataset="__all__")
|
|
73
|
+
matching = [t for t in all_triggers if t.id == trigger_id]
|
|
74
|
+
if not matching:
|
|
75
|
+
console.print(f"[red]Error:[/red] Trigger {trigger_id} not found", style="bold")
|
|
76
|
+
raise typer.Exit(1)
|
|
77
|
+
trigger = matching[0]
|
|
78
|
+
dataset = trigger.dataset
|
|
79
|
+
console.print(f"[dim]Found trigger in dataset: {dataset}[/dim]")
|
|
80
|
+
# We already have the trigger from the list, just output it
|
|
81
|
+
output_result(trigger, output)
|
|
82
|
+
else:
|
|
83
|
+
# Dataset provided, fetch directly
|
|
84
|
+
trigger = client.triggers.get(dataset=dataset, trigger_id=trigger_id)
|
|
85
|
+
output_result(trigger, output)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
88
|
+
raise typer.Exit(1)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@app.command("create")
|
|
92
|
+
def create_trigger(
|
|
93
|
+
dataset: str = typer.Option(..., "--dataset", "-d", help="Dataset slug"),
|
|
94
|
+
from_file: Path = typer.Option(..., "--from-file", "-f", help="JSON file with trigger config"),
|
|
95
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
96
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
97
|
+
output: OutputFormat = typer.Option(DEFAULT_OUTPUT_FORMAT, "--output", "-o"),
|
|
98
|
+
) -> None:
|
|
99
|
+
"""Create a trigger from a JSON file."""
|
|
100
|
+
try:
|
|
101
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
102
|
+
|
|
103
|
+
# Load and parse JSON file
|
|
104
|
+
data = json.loads(from_file.read_text())
|
|
105
|
+
|
|
106
|
+
# Strip fields that shouldn't be in create request
|
|
107
|
+
data.pop("id", None)
|
|
108
|
+
data.pop("created_at", None)
|
|
109
|
+
data.pop("updated_at", None)
|
|
110
|
+
|
|
111
|
+
trigger_create = TriggerCreate.model_validate(data)
|
|
112
|
+
trigger = client.triggers.create(dataset=dataset, trigger=trigger_create)
|
|
113
|
+
|
|
114
|
+
console.print(f"[green]Created trigger '{trigger.name}' with ID: {trigger.id}[/green]")
|
|
115
|
+
output_result(trigger, output)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
118
|
+
raise typer.Exit(1)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@app.command("update")
|
|
122
|
+
def update_trigger(
|
|
123
|
+
trigger_id: str = typer.Argument(..., help="Trigger ID"),
|
|
124
|
+
dataset: str = typer.Option(..., "--dataset", "-d", help="Dataset slug"),
|
|
125
|
+
from_file: Path = typer.Option(..., "--from-file", "-f", help="JSON file with trigger config"),
|
|
126
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
127
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
128
|
+
output: OutputFormat = typer.Option(DEFAULT_OUTPUT_FORMAT, "--output", "-o"),
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Update an existing trigger."""
|
|
131
|
+
try:
|
|
132
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
133
|
+
|
|
134
|
+
# Load and parse JSON file
|
|
135
|
+
data = json.loads(from_file.read_text())
|
|
136
|
+
|
|
137
|
+
# Strip fields that shouldn't be in update request
|
|
138
|
+
data.pop("id", None)
|
|
139
|
+
data.pop("created_at", None)
|
|
140
|
+
data.pop("updated_at", None)
|
|
141
|
+
|
|
142
|
+
trigger_update = TriggerCreate.model_validate(data)
|
|
143
|
+
trigger = client.triggers.update(
|
|
144
|
+
dataset=dataset, trigger_id=trigger_id, trigger=trigger_update
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
console.print(f"[green]Updated trigger '{trigger.name}'[/green]")
|
|
148
|
+
output_result(trigger, output)
|
|
149
|
+
except Exception as e:
|
|
150
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
151
|
+
raise typer.Exit(1)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@app.command("delete")
|
|
155
|
+
def delete_trigger(
|
|
156
|
+
trigger_id: str = typer.Argument(..., help="Trigger ID"),
|
|
157
|
+
dataset: str | None = typer.Option(
|
|
158
|
+
None, "--dataset", "-d", help="Dataset slug (auto-detected if not provided)"
|
|
159
|
+
),
|
|
160
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
161
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
162
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Delete a trigger."""
|
|
165
|
+
try:
|
|
166
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
167
|
+
|
|
168
|
+
# If dataset not provided, find it by listing all triggers
|
|
169
|
+
if dataset is None:
|
|
170
|
+
all_triggers = client.triggers.list(dataset="__all__")
|
|
171
|
+
matching = [t for t in all_triggers if t.id == trigger_id]
|
|
172
|
+
if not matching:
|
|
173
|
+
console.print(f"[red]Error:[/red] Trigger {trigger_id} not found", style="bold")
|
|
174
|
+
raise typer.Exit(1)
|
|
175
|
+
dataset = matching[0].dataset
|
|
176
|
+
console.print(f"[dim]Found trigger in dataset: {dataset}[/dim]")
|
|
177
|
+
|
|
178
|
+
if not yes:
|
|
179
|
+
confirm = typer.confirm(f"Delete trigger {trigger_id} from dataset {dataset}?")
|
|
180
|
+
if not confirm:
|
|
181
|
+
console.print("[yellow]Cancelled[/yellow]")
|
|
182
|
+
raise typer.Exit(0)
|
|
183
|
+
|
|
184
|
+
client.triggers.delete(dataset=dataset, trigger_id=trigger_id)
|
|
185
|
+
console.print(f"[green]Deleted trigger {trigger_id}[/green]")
|
|
186
|
+
except Exception as e:
|
|
187
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
188
|
+
raise typer.Exit(1)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@app.command("export")
|
|
192
|
+
def export_trigger(
|
|
193
|
+
trigger_id: str = typer.Argument(..., help="Trigger ID"),
|
|
194
|
+
dataset: str | None = typer.Option(
|
|
195
|
+
None, "--dataset", "-d", help="Dataset slug (auto-detected if not provided)"
|
|
196
|
+
),
|
|
197
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
198
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
199
|
+
output_file: Path | None = typer.Option(
|
|
200
|
+
None, "--output-file", "-o", help="Output file (default: stdout)"
|
|
201
|
+
),
|
|
202
|
+
) -> None:
|
|
203
|
+
"""
|
|
204
|
+
Export a trigger as JSON.
|
|
205
|
+
|
|
206
|
+
Output is suitable for importing to another environment via the 'create' command.
|
|
207
|
+
"""
|
|
208
|
+
try:
|
|
209
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
210
|
+
|
|
211
|
+
# If dataset not provided, find it by listing all triggers
|
|
212
|
+
if dataset is None:
|
|
213
|
+
all_triggers = client.triggers.list(dataset="__all__")
|
|
214
|
+
matching = [t for t in all_triggers if t.id == trigger_id]
|
|
215
|
+
if not matching:
|
|
216
|
+
console.print(f"[red]Error:[/red] Trigger {trigger_id} not found", style="bold")
|
|
217
|
+
raise typer.Exit(1)
|
|
218
|
+
trigger = matching[0]
|
|
219
|
+
dataset = trigger.dataset
|
|
220
|
+
console.print(f"[dim]Found trigger in dataset: {dataset}[/dim]")
|
|
221
|
+
else:
|
|
222
|
+
# Dataset provided, fetch directly
|
|
223
|
+
trigger = client.triggers.get(dataset=dataset, trigger_id=trigger_id)
|
|
224
|
+
|
|
225
|
+
# Export without IDs/timestamps for portability
|
|
226
|
+
data = trigger.model_dump(exclude={"id", "created_at", "updated_at"}, mode="json")
|
|
227
|
+
json_str = json.dumps(data, indent=2, default=str)
|
|
228
|
+
|
|
229
|
+
if output_file:
|
|
230
|
+
output_file.write_text(json_str)
|
|
231
|
+
console.print(f"[green]Exported trigger to {output_file}[/green]")
|
|
232
|
+
else:
|
|
233
|
+
console.print(json_str)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
236
|
+
raise typer.Exit(1)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@app.command("export-all")
|
|
240
|
+
def export_all_triggers(
|
|
241
|
+
dataset: str = typer.Option(..., "--dataset", "-d", help="Dataset slug"),
|
|
242
|
+
output_dir: Path = typer.Option(..., "--output-dir", help="Output directory"),
|
|
243
|
+
profile: str | None = typer.Option(None, "--profile", "-p", help="Config profile"),
|
|
244
|
+
api_key: str | None = typer.Option(None, "--api-key", envvar="HONEYCOMB_API_KEY"),
|
|
245
|
+
) -> None:
|
|
246
|
+
"""Export all triggers from a dataset to individual JSON files."""
|
|
247
|
+
try:
|
|
248
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
249
|
+
|
|
250
|
+
client = get_client(profile=profile, api_key=api_key)
|
|
251
|
+
triggers = client.triggers.list(dataset=dataset)
|
|
252
|
+
|
|
253
|
+
for trigger in triggers:
|
|
254
|
+
# Export without IDs/timestamps
|
|
255
|
+
data = trigger.model_dump(exclude={"id", "created_at", "updated_at"}, mode="json")
|
|
256
|
+
|
|
257
|
+
# Sanitize filename (replace special chars with dash)
|
|
258
|
+
filename = f"{trigger.name}.json".replace("/", "-").replace(" ", "-").lower()
|
|
259
|
+
file_path = output_dir / filename
|
|
260
|
+
|
|
261
|
+
with open(file_path, "w") as f:
|
|
262
|
+
json.dump(data, f, indent=2, default=str)
|
|
263
|
+
|
|
264
|
+
console.print(f"[green]Exported '{trigger.name}' to {file_path}[/green]")
|
|
265
|
+
|
|
266
|
+
console.print(
|
|
267
|
+
f"\n[bold green]Exported {len(triggers)} triggers to {output_dir}[/bold green]"
|
|
268
|
+
)
|
|
269
|
+
except Exception as e:
|
|
270
|
+
console.print(f"[red]Error:[/red] {e}", style="bold")
|
|
271
|
+
raise typer.Exit(1)
|
honeycomb/client.py
CHANGED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
from contextlib import suppress
|
|
7
|
-
from dataclasses import dataclass
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from email.utils import parsedate_to_datetime
|
|
10
10
|
from typing import TYPE_CHECKING, Any
|
|
@@ -26,6 +26,7 @@ from .exceptions import (
|
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
28
28
|
from .resources.api_keys import ApiKeysResource
|
|
29
|
+
from .resources.auth import AuthResource
|
|
29
30
|
from .resources.boards import BoardsResource
|
|
30
31
|
from .resources.burn_alerts import BurnAlertsResource
|
|
31
32
|
from .resources.columns import ColumnsResource
|
|
@@ -64,12 +65,7 @@ class RetryConfig:
|
|
|
64
65
|
base_delay: float = 1.0
|
|
65
66
|
max_delay: float = 30.0
|
|
66
67
|
exponential_base: float = 2.0
|
|
67
|
-
retry_statuses: set[int] =
|
|
68
|
-
|
|
69
|
-
def __post_init__(self) -> None:
|
|
70
|
-
"""Initialize default retry statuses if not provided."""
|
|
71
|
-
if self.retry_statuses is None:
|
|
72
|
-
self.retry_statuses = {429, 500, 502, 503, 504}
|
|
68
|
+
retry_statuses: set[int] = field(default_factory=lambda: {429, 500, 502, 503, 504})
|
|
73
69
|
|
|
74
70
|
|
|
75
71
|
@dataclass
|
|
@@ -154,6 +150,7 @@ class HoneycombClient:
|
|
|
154
150
|
self._burn_alerts: BurnAlertsResource | None = None
|
|
155
151
|
self._events: EventsResource | None = None
|
|
156
152
|
self._api_keys: ApiKeysResource | None = None
|
|
153
|
+
self._auth_resource: AuthResource | None = None
|
|
157
154
|
self._environments: EnvironmentsResource | None = None
|
|
158
155
|
self._service_map_dependencies: ServiceMapDependenciesResource | None = None
|
|
159
156
|
|
|
@@ -287,6 +284,15 @@ class HoneycombClient:
|
|
|
287
284
|
self._api_keys = ApiKeysResource(self)
|
|
288
285
|
return self._api_keys
|
|
289
286
|
|
|
287
|
+
@property
|
|
288
|
+
def auth(self) -> AuthResource:
|
|
289
|
+
"""Access the Auth API."""
|
|
290
|
+
if self._auth_resource is None:
|
|
291
|
+
from .resources.auth import AuthResource
|
|
292
|
+
|
|
293
|
+
self._auth_resource = AuthResource(self)
|
|
294
|
+
return self._auth_resource
|
|
295
|
+
|
|
290
296
|
@property
|
|
291
297
|
def environments(self) -> EnvironmentsResource:
|
|
292
298
|
"""Access the Environments API (v2 team-scoped)."""
|