From a834ebe760687a4022837f01217bcb061dbe6702 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 11:57:37 +0200 Subject: [PATCH 1/9] spike: Go CLI with a client generated from the platform OpenAPI spec A proof of concept for rewriting the CLI in Go: oapi-codegen generates the client from openapi/platform-api.json, cobra provides the commands, and config, experiment get/apply/run (by key, file and template) and config profile add/list/select/remove are ported with the TypeScript CLI's messages, flags, exit codes and profile files. Experiment files pass through as order-preserving documents so that output stays byte-compatible with the TypeScript CLI where possible. --- Dockerfile.spike | 16 + api/oapi-codegen.yaml | 9 + api/platform.gen.go | 38786 ++++++++++++++++++++++++++++ cmd/steadybit/main.go | 14 + go.mod | 37 + go.sum | 191 + internal/cli/config.go | 169 + internal/cli/experiment.go | 112 + internal/cli/root.go | 95 + internal/config/config.go | 173 + internal/config/config_test.go | 56 + internal/experiment/experiment.go | 500 + internal/output/document.go | 257 + internal/output/document_test.go | 59 + internal/output/output.go | 112 + internal/platform/client.go | 212 + internal/prompt/prompt.go | 135 + 17 files changed, 40933 insertions(+) create mode 100644 Dockerfile.spike create mode 100644 api/oapi-codegen.yaml create mode 100644 api/platform.gen.go create mode 100644 cmd/steadybit/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/cli/config.go create mode 100644 internal/cli/experiment.go create mode 100644 internal/cli/root.go create mode 100644 internal/config/config.go create mode 100644 internal/config/config_test.go create mode 100644 internal/experiment/experiment.go create mode 100644 internal/output/document.go create mode 100644 internal/output/document_test.go create mode 100644 internal/output/output.go create mode 100644 internal/platform/client.go create mode 100644 internal/prompt/prompt.go diff --git a/Dockerfile.spike b/Dockerfile.spike new file mode 100644 index 0000000..0e1a9ad --- /dev/null +++ b/Dockerfile.spike @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2026 Steadybit GmbH + +# Spike: the Go CLI in the same shape as the published image, so e2e/run.sh runs unchanged. +FROM golang:1.26-alpine AS builder +WORKDIR /build +COPY go.mod go.sum ./ +RUN go mod download +COPY api ./api +COPY cmd ./cmd +COPY internal ./internal +RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X github.com/steadybit/cli/internal/platform.Version=spike" -o /steadybit ./cmd/steadybit + +FROM alpine:3 +COPY --from=builder /steadybit /usr/local/bin/steadybit +ENTRYPOINT ["steadybit"] diff --git a/api/oapi-codegen.yaml b/api/oapi-codegen.yaml new file mode 100644 index 0000000..d5efe2b --- /dev/null +++ b/api/oapi-codegen.yaml @@ -0,0 +1,9 @@ +package: api +output: api/platform.gen.go +generate: + client: true + models: true +compatibility: + circular-reference-limit: 11 +output-options: + skip-prune: false diff --git a/api/platform.gen.go b/api/platform.gen.go new file mode 100644 index 0000000..c0eee8c --- /dev/null +++ b/api/platform.gen.go @@ -0,0 +1,38786 @@ +// Package api provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.8.0 DO NOT EDIT. +package api + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "go.yaml.in/yaml/v3" + + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +// Defines values for AccessTokenPrincipalALPrincipalType. +const ( + AccessTokenPrincipalALPrincipalTypeACCESSTOKEN AccessTokenPrincipalALPrincipalType = "ACCESS_TOKEN" + AccessTokenPrincipalALPrincipalTypeBATCHJOB AccessTokenPrincipalALPrincipalType = "BATCH_JOB" + AccessTokenPrincipalALPrincipalTypeUSER AccessTokenPrincipalALPrincipalType = "USER" +) + +// Valid indicates whether the value is a known member of the AccessTokenPrincipalALPrincipalType enum. +func (e AccessTokenPrincipalALPrincipalType) Valid() bool { + switch e { + case AccessTokenPrincipalALPrincipalTypeACCESSTOKEN: + return true + case AccessTokenPrincipalALPrincipalTypeBATCHJOB: + return true + case AccessTokenPrincipalALPrincipalTypeUSER: + return true + default: + return false + } +} + +// Defines values for AccessTokenPrincipalALTokenType. +const ( + AccessTokenPrincipalALTokenTypeADMIN AccessTokenPrincipalALTokenType = "ADMIN" + AccessTokenPrincipalALTokenTypeTEAM AccessTokenPrincipalALTokenType = "TEAM" + AccessTokenPrincipalALTokenTypeWILDCARD AccessTokenPrincipalALTokenType = "WILDCARD" +) + +// Valid indicates whether the value is a known member of the AccessTokenPrincipalALTokenType enum. +func (e AccessTokenPrincipalALTokenType) Valid() bool { + switch e { + case AccessTokenPrincipalALTokenTypeADMIN: + return true + case AccessTokenPrincipalALTokenTypeTEAM: + return true + case AccessTokenPrincipalALTokenTypeWILDCARD: + return true + default: + return false + } +} + +// Defines values for AccessTokensPageItemAOType. +const ( + AccessTokensPageItemAOTypeADMIN AccessTokensPageItemAOType = "ADMIN" + AccessTokensPageItemAOTypeTEAM AccessTokensPageItemAOType = "TEAM" +) + +// Valid indicates whether the value is a known member of the AccessTokensPageItemAOType enum. +func (e AccessTokensPageItemAOType) Valid() bool { + switch e { + case AccessTokensPageItemAOTypeADMIN: + return true + case AccessTokensPageItemAOTypeTEAM: + return true + default: + return false + } +} + +// Defines values for AccessTokensPageItemV2AOType. +const ( + AccessTokensPageItemV2AOTypeADMIN AccessTokensPageItemV2AOType = "ADMIN" + AccessTokensPageItemV2AOTypeTEAM AccessTokensPageItemV2AOType = "TEAM" + AccessTokensPageItemV2AOTypeWILDCARD AccessTokensPageItemV2AOType = "WILDCARD" +) + +// Valid indicates whether the value is a known member of the AccessTokensPageItemV2AOType enum. +func (e AccessTokensPageItemV2AOType) Valid() bool { + switch e { + case AccessTokensPageItemV2AOTypeADMIN: + return true + case AccessTokensPageItemV2AOTypeTEAM: + return true + case AccessTokensPageItemV2AOTypeWILDCARD: + return true + default: + return false + } +} + +// Defines values for ActionAOKind. +const ( + ActionAOKindATTACK ActionAOKind = "ATTACK" + ActionAOKindBASIC ActionAOKind = "BASIC" + ActionAOKindCHECK ActionAOKind = "CHECK" + ActionAOKindLOADTEST ActionAOKind = "LOAD_TEST" + ActionAOKindOTHER ActionAOKind = "OTHER" +) + +// Valid indicates whether the value is a known member of the ActionAOKind enum. +func (e ActionAOKind) Valid() bool { + switch e { + case ActionAOKindATTACK: + return true + case ActionAOKindBASIC: + return true + case ActionAOKindCHECK: + return true + case ActionAOKindLOADTEST: + return true + case ActionAOKindOTHER: + return true + default: + return false + } +} + +// Defines values for ActionAOMissingQuerySelection. +const ( + INCLUDEALL ActionAOMissingQuerySelection = "INCLUDE_ALL" + INCLUDENONE ActionAOMissingQuerySelection = "INCLUDE_NONE" +) + +// Valid indicates whether the value is a known member of the ActionAOMissingQuerySelection enum. +func (e ActionAOMissingQuerySelection) Valid() bool { + switch e { + case INCLUDEALL: + return true + case INCLUDENONE: + return true + default: + return false + } +} + +// Defines values for ActionAOQuantityRestriction. +const ( + ActionAOQuantityRestrictionALL ActionAOQuantityRestriction = "ALL" + ActionAOQuantityRestrictionEXACTLYONE ActionAOQuantityRestriction = "EXACTLY_ONE" + ActionAOQuantityRestrictionNONE ActionAOQuantityRestriction = "NONE" +) + +// Valid indicates whether the value is a known member of the ActionAOQuantityRestriction enum. +func (e ActionAOQuantityRestriction) Valid() bool { + switch e { + case ActionAOQuantityRestrictionALL: + return true + case ActionAOQuantityRestrictionEXACTLYONE: + return true + case ActionAOQuantityRestrictionNONE: + return true + default: + return false + } +} + +// Defines values for BatchPrincipalALPrincipalType. +const ( + BatchPrincipalALPrincipalTypeACCESSTOKEN BatchPrincipalALPrincipalType = "ACCESS_TOKEN" + BatchPrincipalALPrincipalTypeBATCHJOB BatchPrincipalALPrincipalType = "BATCH_JOB" + BatchPrincipalALPrincipalTypeUSER BatchPrincipalALPrincipalType = "USER" +) + +// Valid indicates whether the value is a known member of the BatchPrincipalALPrincipalType enum. +func (e BatchPrincipalALPrincipalType) Valid() bool { + switch e { + case BatchPrincipalALPrincipalTypeACCESSTOKEN: + return true + case BatchPrincipalALPrincipalTypeBATCHJOB: + return true + case BatchPrincipalALPrincipalTypeUSER: + return true + default: + return false + } +} + +// Defines values for CreateAccessTokenRequestAOType. +const ( + CreateAccessTokenRequestAOTypeADMIN CreateAccessTokenRequestAOType = "ADMIN" + CreateAccessTokenRequestAOTypeTEAM CreateAccessTokenRequestAOType = "TEAM" +) + +// Valid indicates whether the value is a known member of the CreateAccessTokenRequestAOType enum. +func (e CreateAccessTokenRequestAOType) Valid() bool { + switch e { + case CreateAccessTokenRequestAOTypeADMIN: + return true + case CreateAccessTokenRequestAOTypeTEAM: + return true + default: + return false + } +} + +// Defines values for CreateAccessTokenRequestV2AOType. +const ( + CreateAccessTokenRequestV2AOTypeADMIN CreateAccessTokenRequestV2AOType = "ADMIN" + CreateAccessTokenRequestV2AOTypeTEAM CreateAccessTokenRequestV2AOType = "TEAM" + CreateAccessTokenRequestV2AOTypeWILDCARD CreateAccessTokenRequestV2AOType = "WILDCARD" +) + +// Valid indicates whether the value is a known member of the CreateAccessTokenRequestV2AOType enum. +func (e CreateAccessTokenRequestV2AOType) Valid() bool { + switch e { + case CreateAccessTokenRequestV2AOTypeADMIN: + return true + case CreateAccessTokenRequestV2AOTypeTEAM: + return true + case CreateAccessTokenRequestV2AOTypeWILDCARD: + return true + default: + return false + } +} + +// Defines values for CustomWebhookAOScope. +const ( + CustomWebhookAOScopeGLOBAL CustomWebhookAOScope = "GLOBAL" + CustomWebhookAOScopeTEAM CustomWebhookAOScope = "TEAM" +) + +// Valid indicates whether the value is a known member of the CustomWebhookAOScope enum. +func (e CustomWebhookAOScope) Valid() bool { + switch e { + case CustomWebhookAOScopeGLOBAL: + return true + case CustomWebhookAOScopeTEAM: + return true + default: + return false + } +} + +// Defines values for CustomWebhookUpsertAOScope. +const ( + CustomWebhookUpsertAOScopeGLOBAL CustomWebhookUpsertAOScope = "GLOBAL" + CustomWebhookUpsertAOScopeTEAM CustomWebhookUpsertAOScope = "TEAM" +) + +// Valid indicates whether the value is a known member of the CustomWebhookUpsertAOScope enum. +func (e CustomWebhookUpsertAOScope) Valid() bool { + switch e { + case CustomWebhookUpsertAOScopeGLOBAL: + return true + case CustomWebhookUpsertAOScopeTEAM: + return true + default: + return false + } +} + +// Defines values for EnvironmentAOState. +const ( + EnvironmentAOStateCREATED EnvironmentAOState = "CREATED" + EnvironmentAOStateERROR EnvironmentAOState = "ERROR" + EnvironmentAOStateREADY EnvironmentAOState = "READY" + EnvironmentAOStateUNKNOWN EnvironmentAOState = "UNKNOWN" + EnvironmentAOStateUPDATED EnvironmentAOState = "UPDATED" +) + +// Valid indicates whether the value is a known member of the EnvironmentAOState enum. +func (e EnvironmentAOState) Valid() bool { + switch e { + case EnvironmentAOStateCREATED: + return true + case EnvironmentAOStateERROR: + return true + case EnvironmentAOStateREADY: + return true + case EnvironmentAOStateUNKNOWN: + return true + case EnvironmentAOStateUPDATED: + return true + default: + return false + } +} + +// Defines values for ExperimentExecutionAOCreatedVia. +const ( + ExperimentExecutionAOCreatedViaAPI ExperimentExecutionAOCreatedVia = "API" + ExperimentExecutionAOCreatedViaCLI ExperimentExecutionAOCreatedVia = "CLI" + ExperimentExecutionAOCreatedViaMCP ExperimentExecutionAOCreatedVia = "MCP" + ExperimentExecutionAOCreatedViaSCHEDULE ExperimentExecutionAOCreatedVia = "SCHEDULE" + ExperimentExecutionAOCreatedViaSUITE ExperimentExecutionAOCreatedVia = "SUITE" + ExperimentExecutionAOCreatedViaUI ExperimentExecutionAOCreatedVia = "UI" +) + +// Valid indicates whether the value is a known member of the ExperimentExecutionAOCreatedVia enum. +func (e ExperimentExecutionAOCreatedVia) Valid() bool { + switch e { + case ExperimentExecutionAOCreatedViaAPI: + return true + case ExperimentExecutionAOCreatedViaCLI: + return true + case ExperimentExecutionAOCreatedViaMCP: + return true + case ExperimentExecutionAOCreatedViaSCHEDULE: + return true + case ExperimentExecutionAOCreatedViaSUITE: + return true + case ExperimentExecutionAOCreatedViaUI: + return true + default: + return false + } +} + +// Defines values for ExperimentExecutionReportFilterAORollup. +const ( + ExperimentExecutionReportFilterAORollupDAILY ExperimentExecutionReportFilterAORollup = "DAILY" + ExperimentExecutionReportFilterAORollupMONTHLY ExperimentExecutionReportFilterAORollup = "MONTHLY" +) + +// Valid indicates whether the value is a known member of the ExperimentExecutionReportFilterAORollup enum. +func (e ExperimentExecutionReportFilterAORollup) Valid() bool { + switch e { + case ExperimentExecutionReportFilterAORollupDAILY: + return true + case ExperimentExecutionReportFilterAORollupMONTHLY: + return true + default: + return false + } +} + +// Defines values for ExperimentExecutionStepActionAOActionKind. +const ( + ExperimentExecutionStepActionAOActionKindATTACK ExperimentExecutionStepActionAOActionKind = "ATTACK" + ExperimentExecutionStepActionAOActionKindBASIC ExperimentExecutionStepActionAOActionKind = "BASIC" + ExperimentExecutionStepActionAOActionKindCHECK ExperimentExecutionStepActionAOActionKind = "CHECK" + ExperimentExecutionStepActionAOActionKindLOADTEST ExperimentExecutionStepActionAOActionKind = "LOAD_TEST" + ExperimentExecutionStepActionAOActionKindOTHER ExperimentExecutionStepActionAOActionKind = "OTHER" +) + +// Valid indicates whether the value is a known member of the ExperimentExecutionStepActionAOActionKind enum. +func (e ExperimentExecutionStepActionAOActionKind) Valid() bool { + switch e { + case ExperimentExecutionStepActionAOActionKindATTACK: + return true + case ExperimentExecutionStepActionAOActionKindBASIC: + return true + case ExperimentExecutionStepActionAOActionKindCHECK: + return true + case ExperimentExecutionStepActionAOActionKindLOADTEST: + return true + case ExperimentExecutionStepActionAOActionKindOTHER: + return true + default: + return false + } +} + +// Defines values for ExperimentExecutionVariableAOOrigin. +const ( + ExperimentExecutionVariableAOOriginENVIRONMENT ExperimentExecutionVariableAOOrigin = "ENVIRONMENT" + ExperimentExecutionVariableAOOriginEXECUTION ExperimentExecutionVariableAOOrigin = "EXECUTION" + ExperimentExecutionVariableAOOriginEXPERIMENT ExperimentExecutionVariableAOOrigin = "EXPERIMENT" + ExperimentExecutionVariableAOOriginSCHEDULE ExperimentExecutionVariableAOOrigin = "SCHEDULE" + ExperimentExecutionVariableAOOriginSERVICE ExperimentExecutionVariableAOOrigin = "SERVICE" +) + +// Valid indicates whether the value is a known member of the ExperimentExecutionVariableAOOrigin enum. +func (e ExperimentExecutionVariableAOOrigin) Valid() bool { + switch e { + case ExperimentExecutionVariableAOOriginENVIRONMENT: + return true + case ExperimentExecutionVariableAOOriginEXECUTION: + return true + case ExperimentExecutionVariableAOOriginEXPERIMENT: + return true + case ExperimentExecutionVariableAOOriginSCHEDULE: + return true + case ExperimentExecutionVariableAOOriginSERVICE: + return true + default: + return false + } +} + +// Defines values for ExperimentReportFilterAORollup. +const ( + ExperimentReportFilterAORollupDAILY ExperimentReportFilterAORollup = "DAILY" + ExperimentReportFilterAORollupMONTHLY ExperimentReportFilterAORollup = "MONTHLY" +) + +// Valid indicates whether the value is a known member of the ExperimentReportFilterAORollup enum. +func (e ExperimentReportFilterAORollup) Valid() bool { + switch e { + case ExperimentReportFilterAORollupDAILY: + return true + case ExperimentReportFilterAORollupMONTHLY: + return true + default: + return false + } +} + +// Defines values for InvitationAORole. +const ( + InvitationAORoleADMIN InvitationAORole = "ADMIN" + InvitationAORoleUSER InvitationAORole = "USER" +) + +// Valid indicates whether the value is a known member of the InvitationAORole enum. +func (e InvitationAORole) Valid() bool { + switch e { + case InvitationAORoleADMIN: + return true + case InvitationAORoleUSER: + return true + default: + return false + } +} + +// Defines values for LandscapeViewColorByAOOverrides. +const ( + BERRIES LandscapeViewColorByAOOverrides = "BERRIES" + BLUE LandscapeViewColorByAOOverrides = "BLUE" + BLUELIGHT LandscapeViewColorByAOOverrides = "BLUE_LIGHT" + GREEN LandscapeViewColorByAOOverrides = "GREEN" + GREY LandscapeViewColorByAOOverrides = "GREY" + ORANGEDARK LandscapeViewColorByAOOverrides = "ORANGE_DARK" + ORANGELIGHT LandscapeViewColorByAOOverrides = "ORANGE_LIGHT" + PINK LandscapeViewColorByAOOverrides = "PINK" + PLUM LandscapeViewColorByAOOverrides = "PLUM" + RED LandscapeViewColorByAOOverrides = "RED" + ROSEPINK LandscapeViewColorByAOOverrides = "ROSE_PINK" + TEAL LandscapeViewColorByAOOverrides = "TEAL" + TIFFANY LandscapeViewColorByAOOverrides = "TIFFANY" + VIOLET LandscapeViewColorByAOOverrides = "VIOLET" + YELLOW LandscapeViewColorByAOOverrides = "YELLOW" +) + +// Valid indicates whether the value is a known member of the LandscapeViewColorByAOOverrides enum. +func (e LandscapeViewColorByAOOverrides) Valid() bool { + switch e { + case BERRIES: + return true + case BLUE: + return true + case BLUELIGHT: + return true + case GREEN: + return true + case GREY: + return true + case ORANGEDARK: + return true + case ORANGELIGHT: + return true + case PINK: + return true + case PLUM: + return true + case RED: + return true + case ROSEPINK: + return true + case TEAL: + return true + case TIFFANY: + return true + case VIOLET: + return true + case YELLOW: + return true + default: + return false + } +} + +// Defines values for LicenseFeatureSummaryAOType. +const ( + HARDLIMIT LicenseFeatureSummaryAOType = "HARD_LIMIT" + SIMPLE LicenseFeatureSummaryAOType = "SIMPLE" + SOFTLIMIT LicenseFeatureSummaryAOType = "SOFT_LIMIT" +) + +// Valid indicates whether the value is a known member of the LicenseFeatureSummaryAOType enum. +func (e LicenseFeatureSummaryAOType) Valid() bool { + switch e { + case HARDLIMIT: + return true + case SIMPLE: + return true + case SOFTLIMIT: + return true + default: + return false + } +} + +// Defines values for LicenseSummaryAOLicenseType. +const ( + LicenseSummaryAOLicenseTypeENTERPRISE LicenseSummaryAOLicenseType = "ENTERPRISE" + LicenseSummaryAOLicenseTypeNONE LicenseSummaryAOLicenseType = "NONE" + LicenseSummaryAOLicenseTypePROFESSIONAL LicenseSummaryAOLicenseType = "PROFESSIONAL" + LicenseSummaryAOLicenseTypeSTARTUP LicenseSummaryAOLicenseType = "STARTUP" + LicenseSummaryAOLicenseTypeTRIAL LicenseSummaryAOLicenseType = "TRIAL" +) + +// Valid indicates whether the value is a known member of the LicenseSummaryAOLicenseType enum. +func (e LicenseSummaryAOLicenseType) Valid() bool { + switch e { + case LicenseSummaryAOLicenseTypeENTERPRISE: + return true + case LicenseSummaryAOLicenseTypeNONE: + return true + case LicenseSummaryAOLicenseTypePROFESSIONAL: + return true + case LicenseSummaryAOLicenseTypeSTARTUP: + return true + case LicenseSummaryAOLicenseTypeTRIAL: + return true + default: + return false + } +} + +// Defines values for MemberAOManagedBy. +const ( + MemberAOManagedByLDAP MemberAOManagedBy = "LDAP" + MemberAOManagedByMANUAL MemberAOManagedBy = "MANUAL" + MemberAOManagedByOIDC MemberAOManagedBy = "OIDC" +) + +// Valid indicates whether the value is a known member of the MemberAOManagedBy enum. +func (e MemberAOManagedBy) Valid() bool { + switch e { + case MemberAOManagedByLDAP: + return true + case MemberAOManagedByMANUAL: + return true + case MemberAOManagedByOIDC: + return true + default: + return false + } +} + +// Defines values for MemberAORole. +const ( + MemberAORoleMEMBER MemberAORole = "MEMBER" + MemberAORoleOWNER MemberAORole = "OWNER" +) + +// Valid indicates whether the value is a known member of the MemberAORole enum. +func (e MemberAORole) Valid() bool { + switch e { + case MemberAORoleMEMBER: + return true + case MemberAORoleOWNER: + return true + default: + return false + } +} + +// Defines values for MemberUpdateAORole. +const ( + MemberUpdateAORoleMEMBER MemberUpdateAORole = "MEMBER" + MemberUpdateAORoleOWNER MemberUpdateAORole = "OWNER" +) + +// Valid indicates whether the value is a known member of the MemberUpdateAORole enum. +func (e MemberUpdateAORole) Valid() bool { + switch e { + case MemberUpdateAORoleMEMBER: + return true + case MemberUpdateAORoleOWNER: + return true + default: + return false + } +} + +// Defines values for MetricCheckAOCondition. +const ( + DATASERIESPRESENCE MetricCheckAOCondition = "DATA_SERIES_PRESENCE" + EQ MetricCheckAOCondition = "EQ" + GT MetricCheckAOCondition = "GT" + GTE MetricCheckAOCondition = "GTE" + LT MetricCheckAOCondition = "LT" + LTE MetricCheckAOCondition = "LTE" + NEQ MetricCheckAOCondition = "NEQ" +) + +// Valid indicates whether the value is a known member of the MetricCheckAOCondition enum. +func (e MetricCheckAOCondition) Valid() bool { + switch e { + case DATASERIESPRESENCE: + return true + case EQ: + return true + case GT: + return true + case GTE: + return true + case LT: + return true + case LTE: + return true + case NEQ: + return true + default: + return false + } +} + +// Defines values for PreflightActionIntegrationAOScope. +const ( + PreflightActionIntegrationAOScopeGLOBAL PreflightActionIntegrationAOScope = "GLOBAL" + PreflightActionIntegrationAOScopeTEAM PreflightActionIntegrationAOScope = "TEAM" +) + +// Valid indicates whether the value is a known member of the PreflightActionIntegrationAOScope enum. +func (e PreflightActionIntegrationAOScope) Valid() bool { + switch e { + case PreflightActionIntegrationAOScopeGLOBAL: + return true + case PreflightActionIntegrationAOScopeTEAM: + return true + default: + return false + } +} + +// Defines values for PreflightActionIntegrationUpsertAOScope. +const ( + PreflightActionIntegrationUpsertAOScopeGLOBAL PreflightActionIntegrationUpsertAOScope = "GLOBAL" + PreflightActionIntegrationUpsertAOScopeTEAM PreflightActionIntegrationUpsertAOScope = "TEAM" +) + +// Valid indicates whether the value is a known member of the PreflightActionIntegrationUpsertAOScope enum. +func (e PreflightActionIntegrationUpsertAOScope) Valid() bool { + switch e { + case PreflightActionIntegrationUpsertAOScopeGLOBAL: + return true + case PreflightActionIntegrationUpsertAOScopeTEAM: + return true + default: + return false + } +} + +// Defines values for PreflightWebhookAOScope. +const ( + PreflightWebhookAOScopeGLOBAL PreflightWebhookAOScope = "GLOBAL" + PreflightWebhookAOScopeTEAM PreflightWebhookAOScope = "TEAM" +) + +// Valid indicates whether the value is a known member of the PreflightWebhookAOScope enum. +func (e PreflightWebhookAOScope) Valid() bool { + switch e { + case PreflightWebhookAOScopeGLOBAL: + return true + case PreflightWebhookAOScopeTEAM: + return true + default: + return false + } +} + +// Defines values for PreflightWebhookUpsertAOScope. +const ( + PreflightWebhookUpsertAOScopeGLOBAL PreflightWebhookUpsertAOScope = "GLOBAL" + PreflightWebhookUpsertAOScopeTEAM PreflightWebhookUpsertAOScope = "TEAM" +) + +// Valid indicates whether the value is a known member of the PreflightWebhookUpsertAOScope enum. +func (e PreflightWebhookUpsertAOScope) Valid() bool { + switch e { + case PreflightWebhookUpsertAOScopeGLOBAL: + return true + case PreflightWebhookUpsertAOScopeTEAM: + return true + default: + return false + } +} + +// Defines values for PrincipalALPrincipalType. +const ( + PrincipalALPrincipalTypeACCESSTOKEN PrincipalALPrincipalType = "ACCESS_TOKEN" + PrincipalALPrincipalTypeBATCHJOB PrincipalALPrincipalType = "BATCH_JOB" + PrincipalALPrincipalTypeUSER PrincipalALPrincipalType = "USER" +) + +// Valid indicates whether the value is a known member of the PrincipalALPrincipalType enum. +func (e PrincipalALPrincipalType) Valid() bool { + switch e { + case PrincipalALPrincipalTypeACCESSTOKEN: + return true + case PrincipalALPrincipalTypeBATCHJOB: + return true + case PrincipalALPrincipalTypeUSER: + return true + default: + return false + } +} + +// Defines values for PropertyAssociationAOAssociationType. +const ( + PropertyAssociationAOAssociationTypeEXPERIMENT PropertyAssociationAOAssociationType = "EXPERIMENT" + PropertyAssociationAOAssociationTypeSERVICE PropertyAssociationAOAssociationType = "SERVICE" +) + +// Valid indicates whether the value is a known member of the PropertyAssociationAOAssociationType enum. +func (e PropertyAssociationAOAssociationType) Valid() bool { + switch e { + case PropertyAssociationAOAssociationTypeEXPERIMENT: + return true + case PropertyAssociationAOAssociationTypeSERVICE: + return true + default: + return false + } +} + +// Defines values for PropertyDefinitionAODataType. +const ( + PropertyDefinitionAODataTypeBOOLEAN PropertyDefinitionAODataType = "BOOLEAN" + PropertyDefinitionAODataTypeDATE PropertyDefinitionAODataType = "DATE" + PropertyDefinitionAODataTypeENUM PropertyDefinitionAODataType = "ENUM" + PropertyDefinitionAODataTypeENUMLIST PropertyDefinitionAODataType = "ENUM_LIST" + PropertyDefinitionAODataTypeLINK PropertyDefinitionAODataType = "LINK" + PropertyDefinitionAODataTypeLINKLIST PropertyDefinitionAODataType = "LINK_LIST" + PropertyDefinitionAODataTypeMARKDOWN PropertyDefinitionAODataType = "MARKDOWN" + PropertyDefinitionAODataTypeNUMBER PropertyDefinitionAODataType = "NUMBER" + PropertyDefinitionAODataTypeNUMBERLIST PropertyDefinitionAODataType = "NUMBER_LIST" + PropertyDefinitionAODataTypeSTRING PropertyDefinitionAODataType = "STRING" + PropertyDefinitionAODataTypeSTRINGLIST PropertyDefinitionAODataType = "STRING_LIST" +) + +// Valid indicates whether the value is a known member of the PropertyDefinitionAODataType enum. +func (e PropertyDefinitionAODataType) Valid() bool { + switch e { + case PropertyDefinitionAODataTypeBOOLEAN: + return true + case PropertyDefinitionAODataTypeDATE: + return true + case PropertyDefinitionAODataTypeENUM: + return true + case PropertyDefinitionAODataTypeENUMLIST: + return true + case PropertyDefinitionAODataTypeLINK: + return true + case PropertyDefinitionAODataTypeLINKLIST: + return true + case PropertyDefinitionAODataTypeMARKDOWN: + return true + case PropertyDefinitionAODataTypeNUMBER: + return true + case PropertyDefinitionAODataTypeNUMBERLIST: + return true + case PropertyDefinitionAODataTypeSTRING: + return true + case PropertyDefinitionAODataTypeSTRINGLIST: + return true + default: + return false + } +} + +// Defines values for ReportFilterAORollup. +const ( + ReportFilterAORollupDAILY ReportFilterAORollup = "DAILY" + ReportFilterAORollupMONTHLY ReportFilterAORollup = "MONTHLY" +) + +// Valid indicates whether the value is a known member of the ReportFilterAORollup enum. +func (e ReportFilterAORollup) Valid() bool { + switch e { + case ReportFilterAORollupDAILY: + return true + case ReportFilterAORollupMONTHLY: + return true + default: + return false + } +} + +// Defines values for SelectExpressionAOMode. +const ( + Fixed SelectExpressionAOMode = "fixed" + Percent SelectExpressionAOMode = "percent" +) + +// Valid indicates whether the value is a known member of the SelectExpressionAOMode enum. +func (e SelectExpressionAOMode) Valid() bool { + switch e { + case Fixed: + return true + case Percent: + return true + default: + return false + } +} + +// Defines values for SelectExpressionAOScope. +const ( + Environment SelectExpressionAOScope = "environment" + Service SelectExpressionAOScope = "service" +) + +// Valid indicates whether the value is a known member of the SelectExpressionAOScope enum. +func (e SelectExpressionAOScope) Valid() bool { + switch e { + case Environment: + return true + case Service: + return true + default: + return false + } +} + +// Defines values for ServiceExperimentAOAssociationType. +const ( + ServiceExperimentAOAssociationTypeCUSTOM ServiceExperimentAOAssociationType = "CUSTOM" + ServiceExperimentAOAssociationTypePROVIDED ServiceExperimentAOAssociationType = "PROVIDED" +) + +// Valid indicates whether the value is a known member of the ServiceExperimentAOAssociationType enum. +func (e ServiceExperimentAOAssociationType) Valid() bool { + switch e { + case ServiceExperimentAOAssociationTypeCUSTOM: + return true + case ServiceExperimentAOAssociationTypePROVIDED: + return true + default: + return false + } +} + +// Defines values for ServiceProfileAOOrigin. +const ( + ServiceProfileAOOriginCUSTOM ServiceProfileAOOrigin = "CUSTOM" + ServiceProfileAOOriginPROVIDED ServiceProfileAOOrigin = "PROVIDED" +) + +// Valid indicates whether the value is a known member of the ServiceProfileAOOrigin enum. +func (e ServiceProfileAOOrigin) Valid() bool { + switch e { + case ServiceProfileAOOriginCUSTOM: + return true + case ServiceProfileAOOriginPROVIDED: + return true + default: + return false + } +} + +// Defines values for ServiceRiskReportFilterAORollup. +const ( + ServiceRiskReportFilterAORollupDAILY ServiceRiskReportFilterAORollup = "DAILY" + ServiceRiskReportFilterAORollupMONTHLY ServiceRiskReportFilterAORollup = "MONTHLY" +) + +// Valid indicates whether the value is a known member of the ServiceRiskReportFilterAORollup enum. +func (e ServiceRiskReportFilterAORollup) Valid() bool { + switch e { + case ServiceRiskReportFilterAORollupDAILY: + return true + case ServiceRiskReportFilterAORollupMONTHLY: + return true + default: + return false + } +} + +// Defines values for SlackWebhookAOScope. +const ( + SlackWebhookAOScopeGLOBAL SlackWebhookAOScope = "GLOBAL" + SlackWebhookAOScopeTEAM SlackWebhookAOScope = "TEAM" +) + +// Valid indicates whether the value is a known member of the SlackWebhookAOScope enum. +func (e SlackWebhookAOScope) Valid() bool { + switch e { + case SlackWebhookAOScopeGLOBAL: + return true + case SlackWebhookAOScopeTEAM: + return true + default: + return false + } +} + +// Defines values for SlackWebhookUpsertAOScope. +const ( + SlackWebhookUpsertAOScopeGLOBAL SlackWebhookUpsertAOScope = "GLOBAL" + SlackWebhookUpsertAOScopeTEAM SlackWebhookUpsertAOScope = "TEAM" +) + +// Valid indicates whether the value is a known member of the SlackWebhookUpsertAOScope enum. +func (e SlackWebhookUpsertAOScope) Valid() bool { + switch e { + case SlackWebhookUpsertAOScopeGLOBAL: + return true + case SlackWebhookUpsertAOScopeTEAM: + return true + default: + return false + } +} + +// Defines values for TargetAttributeKeyCountPredicateAOValueCountOperator. +const ( + EQUAL TargetAttributeKeyCountPredicateAOValueCountOperator = "EQUAL" + GREATERTHAN TargetAttributeKeyCountPredicateAOValueCountOperator = "GREATER_THAN" + GREATERTHANOREQUAL TargetAttributeKeyCountPredicateAOValueCountOperator = "GREATER_THAN_OR_EQUAL" + LESSTHAN TargetAttributeKeyCountPredicateAOValueCountOperator = "LESS_THAN" + LESSTHANOREQUAL TargetAttributeKeyCountPredicateAOValueCountOperator = "LESS_THAN_OR_EQUAL" + NOTEQUAL TargetAttributeKeyCountPredicateAOValueCountOperator = "NOT_EQUAL" +) + +// Valid indicates whether the value is a known member of the TargetAttributeKeyCountPredicateAOValueCountOperator enum. +func (e TargetAttributeKeyCountPredicateAOValueCountOperator) Valid() bool { + switch e { + case EQUAL: + return true + case GREATERTHAN: + return true + case GREATERTHANOREQUAL: + return true + case LESSTHAN: + return true + case LESSTHANOREQUAL: + return true + case NOTEQUAL: + return true + default: + return false + } +} + +// Defines values for TargetAttributeKeyPresencePredicateAOPresenceOperator. +const ( + NOTPRESENT TargetAttributeKeyPresencePredicateAOPresenceOperator = "NOT_PRESENT" + PRESENT TargetAttributeKeyPresencePredicateAOPresenceOperator = "PRESENT" +) + +// Valid indicates whether the value is a known member of the TargetAttributeKeyPresencePredicateAOPresenceOperator enum. +func (e TargetAttributeKeyPresencePredicateAOPresenceOperator) Valid() bool { + switch e { + case NOTPRESENT: + return true + case PRESENT: + return true + default: + return false + } +} + +// Defines values for TeamAOManagedBy. +const ( + TeamAOManagedByLDAP TeamAOManagedBy = "LDAP" + TeamAOManagedByMANUAL TeamAOManagedBy = "MANUAL" + TeamAOManagedByOIDC TeamAOManagedBy = "OIDC" +) + +// Valid indicates whether the value is a known member of the TeamAOManagedBy enum. +func (e TeamAOManagedBy) Valid() bool { + switch e { + case TeamAOManagedByLDAP: + return true + case TeamAOManagedByMANUAL: + return true + case TeamAOManagedByOIDC: + return true + default: + return false + } +} + +// Defines values for TimeSeriesReportAOGroupBy. +const ( + TimeSeriesReportAOGroupByACTION TimeSeriesReportAOGroupBy = "ACTION" + TimeSeriesReportAOGroupByCATEGORY TimeSeriesReportAOGroupBy = "CATEGORY" + TimeSeriesReportAOGroupByCREATEDVIA TimeSeriesReportAOGroupBy = "CREATED_VIA" + TimeSeriesReportAOGroupByISSUESDISCOVERED TimeSeriesReportAOGroupBy = "ISSUES_DISCOVERED" + TimeSeriesReportAOGroupByISSUESFIXED TimeSeriesReportAOGroupBy = "ISSUES_FIXED" + TimeSeriesReportAOGroupByNONE TimeSeriesReportAOGroupBy = "NONE" + TimeSeriesReportAOGroupByORIGIN TimeSeriesReportAOGroupBy = "ORIGIN" + TimeSeriesReportAOGroupByRISKLEVEL TimeSeriesReportAOGroupBy = "RISK_LEVEL" + TimeSeriesReportAOGroupBySTATE TimeSeriesReportAOGroupBy = "STATE" + TimeSeriesReportAOGroupByTRIGGER TimeSeriesReportAOGroupBy = "TRIGGER" +) + +// Valid indicates whether the value is a known member of the TimeSeriesReportAOGroupBy enum. +func (e TimeSeriesReportAOGroupBy) Valid() bool { + switch e { + case TimeSeriesReportAOGroupByACTION: + return true + case TimeSeriesReportAOGroupByCATEGORY: + return true + case TimeSeriesReportAOGroupByCREATEDVIA: + return true + case TimeSeriesReportAOGroupByISSUESDISCOVERED: + return true + case TimeSeriesReportAOGroupByISSUESFIXED: + return true + case TimeSeriesReportAOGroupByNONE: + return true + case TimeSeriesReportAOGroupByORIGIN: + return true + case TimeSeriesReportAOGroupByRISKLEVEL: + return true + case TimeSeriesReportAOGroupBySTATE: + return true + case TimeSeriesReportAOGroupByTRIGGER: + return true + default: + return false + } +} + +// Defines values for TimeSeriesReportAORollup. +const ( + TimeSeriesReportAORollupDAILY TimeSeriesReportAORollup = "DAILY" + TimeSeriesReportAORollupMONTHLY TimeSeriesReportAORollup = "MONTHLY" +) + +// Valid indicates whether the value is a known member of the TimeSeriesReportAORollup enum. +func (e TimeSeriesReportAORollup) Valid() bool { + switch e { + case TimeSeriesReportAORollupDAILY: + return true + case TimeSeriesReportAORollupMONTHLY: + return true + default: + return false + } +} + +// Defines values for UpsertPropertyAssociationAOAssociationType. +const ( + UpsertPropertyAssociationAOAssociationTypeEXPERIMENT UpsertPropertyAssociationAOAssociationType = "EXPERIMENT" + UpsertPropertyAssociationAOAssociationTypeSERVICE UpsertPropertyAssociationAOAssociationType = "SERVICE" +) + +// Valid indicates whether the value is a known member of the UpsertPropertyAssociationAOAssociationType enum. +func (e UpsertPropertyAssociationAOAssociationType) Valid() bool { + switch e { + case UpsertPropertyAssociationAOAssociationTypeEXPERIMENT: + return true + case UpsertPropertyAssociationAOAssociationTypeSERVICE: + return true + default: + return false + } +} + +// Defines values for UpsertPropertyDefinitionAODataType. +const ( + UpsertPropertyDefinitionAODataTypeBOOLEAN UpsertPropertyDefinitionAODataType = "BOOLEAN" + UpsertPropertyDefinitionAODataTypeDATE UpsertPropertyDefinitionAODataType = "DATE" + UpsertPropertyDefinitionAODataTypeENUM UpsertPropertyDefinitionAODataType = "ENUM" + UpsertPropertyDefinitionAODataTypeENUMLIST UpsertPropertyDefinitionAODataType = "ENUM_LIST" + UpsertPropertyDefinitionAODataTypeLINK UpsertPropertyDefinitionAODataType = "LINK" + UpsertPropertyDefinitionAODataTypeLINKLIST UpsertPropertyDefinitionAODataType = "LINK_LIST" + UpsertPropertyDefinitionAODataTypeMARKDOWN UpsertPropertyDefinitionAODataType = "MARKDOWN" + UpsertPropertyDefinitionAODataTypeNUMBER UpsertPropertyDefinitionAODataType = "NUMBER" + UpsertPropertyDefinitionAODataTypeNUMBERLIST UpsertPropertyDefinitionAODataType = "NUMBER_LIST" + UpsertPropertyDefinitionAODataTypeSTRING UpsertPropertyDefinitionAODataType = "STRING" + UpsertPropertyDefinitionAODataTypeSTRINGLIST UpsertPropertyDefinitionAODataType = "STRING_LIST" +) + +// Valid indicates whether the value is a known member of the UpsertPropertyDefinitionAODataType enum. +func (e UpsertPropertyDefinitionAODataType) Valid() bool { + switch e { + case UpsertPropertyDefinitionAODataTypeBOOLEAN: + return true + case UpsertPropertyDefinitionAODataTypeDATE: + return true + case UpsertPropertyDefinitionAODataTypeENUM: + return true + case UpsertPropertyDefinitionAODataTypeENUMLIST: + return true + case UpsertPropertyDefinitionAODataTypeLINK: + return true + case UpsertPropertyDefinitionAODataTypeLINKLIST: + return true + case UpsertPropertyDefinitionAODataTypeMARKDOWN: + return true + case UpsertPropertyDefinitionAODataTypeNUMBER: + return true + case UpsertPropertyDefinitionAODataTypeNUMBERLIST: + return true + case UpsertPropertyDefinitionAODataTypeSTRING: + return true + case UpsertPropertyDefinitionAODataTypeSTRINGLIST: + return true + default: + return false + } +} + +// Defines values for UpsertServiceProfileAOOrigin. +const ( + UpsertServiceProfileAOOriginCUSTOM UpsertServiceProfileAOOrigin = "CUSTOM" + UpsertServiceProfileAOOriginPROVIDED UpsertServiceProfileAOOrigin = "PROVIDED" +) + +// Valid indicates whether the value is a known member of the UpsertServiceProfileAOOrigin enum. +func (e UpsertServiceProfileAOOrigin) Valid() bool { + switch e { + case UpsertServiceProfileAOOriginCUSTOM: + return true + case UpsertServiceProfileAOOriginPROVIDED: + return true + default: + return false + } +} + +// Defines values for UpsertTeamAOManagedBy. +const ( + UpsertTeamAOManagedByLDAP UpsertTeamAOManagedBy = "LDAP" + UpsertTeamAOManagedByMANUAL UpsertTeamAOManagedBy = "MANUAL" + UpsertTeamAOManagedByOIDC UpsertTeamAOManagedBy = "OIDC" +) + +// Valid indicates whether the value is a known member of the UpsertTeamAOManagedBy enum. +func (e UpsertTeamAOManagedBy) Valid() bool { + switch e { + case UpsertTeamAOManagedByLDAP: + return true + case UpsertTeamAOManagedByMANUAL: + return true + case UpsertTeamAOManagedByOIDC: + return true + default: + return false + } +} + +// Defines values for UserPrincipalALPrincipalType. +const ( + UserPrincipalALPrincipalTypeACCESSTOKEN UserPrincipalALPrincipalType = "ACCESS_TOKEN" + UserPrincipalALPrincipalTypeBATCHJOB UserPrincipalALPrincipalType = "BATCH_JOB" + UserPrincipalALPrincipalTypeUSER UserPrincipalALPrincipalType = "USER" +) + +// Valid indicates whether the value is a known member of the UserPrincipalALPrincipalType enum. +func (e UserPrincipalALPrincipalType) Valid() bool { + switch e { + case UserPrincipalALPrincipalTypeACCESSTOKEN: + return true + case UserPrincipalALPrincipalTypeBATCHJOB: + return true + case UserPrincipalALPrincipalTypeUSER: + return true + default: + return false + } +} + +// Defines values for UserPrincipalALRole. +const ( + UserPrincipalALRoleADMIN UserPrincipalALRole = "ADMIN" + UserPrincipalALRoleSUPPORT UserPrincipalALRole = "SUPPORT" + UserPrincipalALRoleUSER UserPrincipalALRole = "USER" +) + +// Valid indicates whether the value is a known member of the UserPrincipalALRole enum. +func (e UserPrincipalALRole) Valid() bool { + switch e { + case UserPrincipalALRoleADMIN: + return true + case UserPrincipalALRoleSUPPORT: + return true + case UserPrincipalALRoleUSER: + return true + default: + return false + } +} + +// Defines values for GetAccessTokensParamsType. +const ( + GetAccessTokensParamsTypeADMIN GetAccessTokensParamsType = "ADMIN" + GetAccessTokensParamsTypeTEAM GetAccessTokensParamsType = "TEAM" +) + +// Valid indicates whether the value is a known member of the GetAccessTokensParamsType enum. +func (e GetAccessTokensParamsType) Valid() bool { + switch e { + case GetAccessTokensParamsTypeADMIN: + return true + case GetAccessTokensParamsTypeTEAM: + return true + default: + return false + } +} + +// Defines values for GetAccessTokens1ParamsType. +const ( + GetAccessTokens1ParamsTypeADMIN GetAccessTokens1ParamsType = "ADMIN" + GetAccessTokens1ParamsTypeTEAM GetAccessTokens1ParamsType = "TEAM" + GetAccessTokens1ParamsTypeWILDCARD GetAccessTokens1ParamsType = "WILDCARD" +) + +// Valid indicates whether the value is a known member of the GetAccessTokens1ParamsType enum. +func (e GetAccessTokens1ParamsType) Valid() bool { + switch e { + case GetAccessTokens1ParamsTypeADMIN: + return true + case GetAccessTokens1ParamsTypeTEAM: + return true + case GetAccessTokens1ParamsTypeWILDCARD: + return true + default: + return false + } +} + +// Defines values for GetExperimentsParamsKind. +const ( + GetExperimentsParamsKindATTACK GetExperimentsParamsKind = "ATTACK" + GetExperimentsParamsKindBASIC GetExperimentsParamsKind = "BASIC" + GetExperimentsParamsKindCHECK GetExperimentsParamsKind = "CHECK" + GetExperimentsParamsKindLOADTEST GetExperimentsParamsKind = "LOAD_TEST" + GetExperimentsParamsKindOTHER GetExperimentsParamsKind = "OTHER" +) + +// Valid indicates whether the value is a known member of the GetExperimentsParamsKind enum. +func (e GetExperimentsParamsKind) Valid() bool { + switch e { + case GetExperimentsParamsKindATTACK: + return true + case GetExperimentsParamsKindBASIC: + return true + case GetExperimentsParamsKindCHECK: + return true + case GetExperimentsParamsKindLOADTEST: + return true + case GetExperimentsParamsKindOTHER: + return true + default: + return false + } +} + +// Defines values for GetExperimentExecutions1ParamsState. +const ( + GetExperimentExecutions1ParamsStateCANCELED GetExperimentExecutions1ParamsState = "CANCELED" + GetExperimentExecutions1ParamsStateCOMPLETED GetExperimentExecutions1ParamsState = "COMPLETED" + GetExperimentExecutions1ParamsStateCREATED GetExperimentExecutions1ParamsState = "CREATED" + GetExperimentExecutions1ParamsStateERRORED GetExperimentExecutions1ParamsState = "ERRORED" + GetExperimentExecutions1ParamsStateFAILED GetExperimentExecutions1ParamsState = "FAILED" + GetExperimentExecutions1ParamsStatePREPARED GetExperimentExecutions1ParamsState = "PREPARED" + GetExperimentExecutions1ParamsStateREQUESTED GetExperimentExecutions1ParamsState = "REQUESTED" + GetExperimentExecutions1ParamsStateRUNNING GetExperimentExecutions1ParamsState = "RUNNING" +) + +// Valid indicates whether the value is a known member of the GetExperimentExecutions1ParamsState enum. +func (e GetExperimentExecutions1ParamsState) Valid() bool { + switch e { + case GetExperimentExecutions1ParamsStateCANCELED: + return true + case GetExperimentExecutions1ParamsStateCOMPLETED: + return true + case GetExperimentExecutions1ParamsStateCREATED: + return true + case GetExperimentExecutions1ParamsStateERRORED: + return true + case GetExperimentExecutions1ParamsStateFAILED: + return true + case GetExperimentExecutions1ParamsStatePREPARED: + return true + case GetExperimentExecutions1ParamsStateREQUESTED: + return true + case GetExperimentExecutions1ParamsStateRUNNING: + return true + default: + return false + } +} + +// Defines values for GetExperimentExecutions3ParamsState. +const ( + GetExperimentExecutions3ParamsStateCANCELED GetExperimentExecutions3ParamsState = "CANCELED" + GetExperimentExecutions3ParamsStateCOMPLETED GetExperimentExecutions3ParamsState = "COMPLETED" + GetExperimentExecutions3ParamsStateCREATED GetExperimentExecutions3ParamsState = "CREATED" + GetExperimentExecutions3ParamsStateERRORED GetExperimentExecutions3ParamsState = "ERRORED" + GetExperimentExecutions3ParamsStateFAILED GetExperimentExecutions3ParamsState = "FAILED" + GetExperimentExecutions3ParamsStatePREPARED GetExperimentExecutions3ParamsState = "PREPARED" + GetExperimentExecutions3ParamsStateREQUESTED GetExperimentExecutions3ParamsState = "REQUESTED" + GetExperimentExecutions3ParamsStateRUNNING GetExperimentExecutions3ParamsState = "RUNNING" +) + +// Valid indicates whether the value is a known member of the GetExperimentExecutions3ParamsState enum. +func (e GetExperimentExecutions3ParamsState) Valid() bool { + switch e { + case GetExperimentExecutions3ParamsStateCANCELED: + return true + case GetExperimentExecutions3ParamsStateCOMPLETED: + return true + case GetExperimentExecutions3ParamsStateCREATED: + return true + case GetExperimentExecutions3ParamsStateERRORED: + return true + case GetExperimentExecutions3ParamsStateFAILED: + return true + case GetExperimentExecutions3ParamsStatePREPARED: + return true + case GetExperimentExecutions3ParamsStateREQUESTED: + return true + case GetExperimentExecutions3ParamsStateRUNNING: + return true + default: + return false + } +} + +// Defines values for GetAssociationsParamsAssociationTypeAO. +const ( + GetAssociationsParamsAssociationTypeAOEXPERIMENT GetAssociationsParamsAssociationTypeAO = "EXPERIMENT" + GetAssociationsParamsAssociationTypeAOSERVICE GetAssociationsParamsAssociationTypeAO = "SERVICE" +) + +// Valid indicates whether the value is a known member of the GetAssociationsParamsAssociationTypeAO enum. +func (e GetAssociationsParamsAssociationTypeAO) Valid() bool { + switch e { + case GetAssociationsParamsAssociationTypeAOEXPERIMENT: + return true + case GetAssociationsParamsAssociationTypeAOSERVICE: + return true + default: + return false + } +} + +// Defines values for GetExperimentCreationsParamsGroupBy. +const ( + GetExperimentCreationsParamsGroupByCREATEDVIA GetExperimentCreationsParamsGroupBy = "CREATED_VIA" + GetExperimentCreationsParamsGroupByNONE GetExperimentCreationsParamsGroupBy = "NONE" + GetExperimentCreationsParamsGroupByORIGIN GetExperimentCreationsParamsGroupBy = "ORIGIN" +) + +// Valid indicates whether the value is a known member of the GetExperimentCreationsParamsGroupBy enum. +func (e GetExperimentCreationsParamsGroupBy) Valid() bool { + switch e { + case GetExperimentCreationsParamsGroupByCREATEDVIA: + return true + case GetExperimentCreationsParamsGroupByNONE: + return true + case GetExperimentCreationsParamsGroupByORIGIN: + return true + default: + return false + } +} + +// Defines values for GetExperimentExecutionsParamsGroupBy. +const ( + GetExperimentExecutionsParamsGroupByACTION GetExperimentExecutionsParamsGroupBy = "ACTION" + GetExperimentExecutionsParamsGroupByISSUESDISCOVERED GetExperimentExecutionsParamsGroupBy = "ISSUES_DISCOVERED" + GetExperimentExecutionsParamsGroupByISSUESFIXED GetExperimentExecutionsParamsGroupBy = "ISSUES_FIXED" + GetExperimentExecutionsParamsGroupByNONE GetExperimentExecutionsParamsGroupBy = "NONE" + GetExperimentExecutionsParamsGroupBySTATE GetExperimentExecutionsParamsGroupBy = "STATE" + GetExperimentExecutionsParamsGroupByTRIGGER GetExperimentExecutionsParamsGroupBy = "TRIGGER" +) + +// Valid indicates whether the value is a known member of the GetExperimentExecutionsParamsGroupBy enum. +func (e GetExperimentExecutionsParamsGroupBy) Valid() bool { + switch e { + case GetExperimentExecutionsParamsGroupByACTION: + return true + case GetExperimentExecutionsParamsGroupByISSUESDISCOVERED: + return true + case GetExperimentExecutionsParamsGroupByISSUESFIXED: + return true + case GetExperimentExecutionsParamsGroupByNONE: + return true + case GetExperimentExecutionsParamsGroupBySTATE: + return true + case GetExperimentExecutionsParamsGroupByTRIGGER: + return true + default: + return false + } +} + +// Defines values for GetServiceExperimentsParamsType. +const ( + GetServiceExperimentsParamsTypeCUSTOM GetServiceExperimentsParamsType = "CUSTOM" + GetServiceExperimentsParamsTypePROVIDED GetServiceExperimentsParamsType = "PROVIDED" +) + +// Valid indicates whether the value is a known member of the GetServiceExperimentsParamsType enum. +func (e GetServiceExperimentsParamsType) Valid() bool { + switch e { + case GetServiceExperimentsParamsTypeCUSTOM: + return true + case GetServiceExperimentsParamsTypePROVIDED: + return true + default: + return false + } +} + +// AbstractExperimentExecutionStepAO A step that is executed as part of an experiment. +// +// Example: [{"id":"40b0f797-912d-4256-8887-1553561962a9","ignoreFailure":false,"parameters":{"duration":"10s"},"predecessorId":null}] +type AbstractExperimentExecutionStepAO struct { + // CustomLabel Custom label assigned during experiment design to express the intention of this step + // + // Example: Container 'xyz' can not be reached + CustomLabel *string `json:"customLabel,omitempty"` + + // Ended Timestamp when this experiment step ended + // + // Example: 2023-01-01T09:00:00Z + Ended *time.Time `json:"ended,omitempty"` + + // Id Unique identifier of this step execution + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + Id *openapi_types.UUID `json:"id,omitempty"` + + // IgnoreFailure Whether the experiment should fail/error immediately in case this step fails/errors. + // + // Example: false + IgnoreFailure *bool `json:"ignoreFailure,omitempty"` + + // Parameters Step-specific parameters of the experiment step configuration + // + // Example: {"duration":"10s"} + Parameters *map[string]interface{} `json:"parameters,omitempty"` + + // PredecessorId Unique identifier of the step execution that precedes this step, null if it is the first step of a lane + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + PredecessorId *openapi_types.UUID `json:"predecessorId,omitempty"` + + // Reason Reason in case this experiment step execution failed or errored + // + // Example: Couldn't read state of container... + Reason *string `json:"reason,omitempty"` + + // Started Timestamp when this experiment step was started + // + // Example: 2023-01-01T09:00:00Z + Started *time.Time `json:"started,omitempty"` + + // State Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) + // + // Example: RUNNING + State *string `json:"state,omitempty"` + + // StepType Type of this step execution (e.g. ACTION, WAIT) + // + // Example: ACTION + StepType string `json:"stepType"` + union json.RawMessage +} + +// AccessTokenPrincipalAL The logged event was performed via API authorized via access token +// +// Example: {"id":"VDKTEBLl","name":"CI/CD","principalType":"ACCESS_TOKEN","tokenType":"TEAM"} +type AccessTokenPrincipalAL struct { + // Id Unique identifier of this access token principal + // + // Example: VDKTEBLl + Id string `json:"id"` + + // Name Name of the access token that was used + // + // Example: CI/CD + Name string `json:"name"` + + // PrincipalType Principal type for access token based principal + // + // Example: ACCESS_TOKEN + PrincipalType AccessTokenPrincipalALPrincipalType `json:"principalType"` + + // TokenType Access token type that was used to perform the logged event + // + // Example: TEAM + TokenType AccessTokenPrincipalALTokenType `json:"tokenType"` +} + +// AccessTokenPrincipalALPrincipalType Principal type for access token based principal +// +// Example: ACCESS_TOKEN +type AccessTokenPrincipalALPrincipalType string + +// AccessTokenPrincipalALTokenType Access token type that was used to perform the logged event +// +// Example: TEAM +type AccessTokenPrincipalALTokenType string + +// AccessTokensPageItemAO A single access token of a team. The token itself can't be read again +// +// Example: {"id":"aP4cDVfA","name":"CI/CD"} +type AccessTokensPageItemAO struct { + // Id Unique identifier of the access token + // + // Example: CQer2Oar + Id *string `json:"id,omitempty"` + + // Name Name of the Access Token to document e.g. its purpose + // + // Example: CI/CD access token + Name *string `json:"name,omitempty"` + + // Team Team associated with this token or null if this is an admin token + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Type Type of the access token + // + // Example: ADMIN + Type *AccessTokensPageItemAOType `json:"type,omitempty"` +} + +// AccessTokensPageItemAOType Type of the access token +// +// Example: ADMIN +type AccessTokensPageItemAOType string + +// AccessTokensPageItemV2AO A single access token +type AccessTokensPageItemV2AO struct { + // ExpiresAt Expiration date of the token. Null means the token never expires. + ExpiresAt *time.Time `json:"expiresAt,omitempty"` + + // Id Unique identifier of this access token + Id *string `json:"id,omitempty"` + + // LastUsed Date of the last token usage. Null means the token was never used. + LastUsed *time.Time `json:"lastUsed,omitempty"` + + // Name Name of this access token + Name *string `json:"name,omitempty"` + + // Teams Teams associated with this token. + Teams *[]string `json:"teams,omitempty"` + + // Type Type of this token + Type *AccessTokensPageItemV2AOType `json:"type,omitempty"` +} + +// AccessTokensPageItemV2AOType Type of this token +type AccessTokensPageItemV2AOType string + +// ActionAO An action that is currently registered. +type ActionAO struct { + // Category Category grouping similar actions. + // + // Example: Resource + Category *string `json:"category,omitempty"` + + // DefaultBlastRadius Default blast radius configuration for an action. + DefaultBlastRadius DefaultBlastRadiusAO `json:"defaultBlastRadius"` + + // Description Description of the action. + Description string `json:"description"` + + // Hint An informational or warning hint displayed to the user. + Hint *HintAO `json:"hint,omitempty"` + HubSummary *string `json:"hubSummary,omitempty"` + + // Icon Icon of the action as a data URI (may be a large base64-encoded image). + Icon *string `json:"icon,omitempty"` + + // Id Unique identifier of the action. + // + // Example: com.steadybit.extension_container.stress_cpu + Id string `json:"id"` + + // Kind Kind of the action. + // + // Example: ATTACK + Kind ActionAOKind `json:"kind"` + + // MetricQueryParameters Parameters that describe how to fetch metrics for this action. + MetricQueryParameters []ParameterAO `json:"metricQueryParameters"` + + // MissingQuerySelection Behavior when the target query does not match any targets. + // + // Example: INCLUDE_NONE + MissingQuerySelection ActionAOMissingQuerySelection `json:"missingQuerySelection"` + + // Name Display name of the action. + // + // Example: Stress CPU + Name string `json:"name"` + Parameters *[]ParameterAO `json:"parameters,omitempty"` + + // QuantityRestriction Restriction on the number of targets this action may operate on. + // + // Example: NONE + QuantityRestriction ActionAOQuantityRestriction `json:"quantityRestriction"` + + // SupportsMetricQueries Whether this action supports metric queries. + SupportsMetricQueries *bool `json:"supportsMetricQueries,omitempty"` + Target *TargetSelectorAO `json:"target,omitempty"` + + // TargetPredicateTemplates Predefined target predicate templates offered to the user when configuring this action. + TargetPredicateTemplates []TargetPredicateTemplateAO `json:"targetPredicateTemplates"` + + // Technology Technology the action belongs to. + // + // Example: Container + Technology *string `json:"technology,omitempty"` + + // Version Version of the action. + // + // Example: 1.2.3 + Version *string `json:"version,omitempty"` +} + +// ActionAOKind Kind of the action. +// +// Example: ATTACK +type ActionAOKind string + +// ActionAOMissingQuerySelection Behavior when the target query does not match any targets. +// +// Example: INCLUDE_NONE +type ActionAOMissingQuerySelection string + +// ActionAOQuantityRestriction Restriction on the number of targets this action may operate on. +// +// Example: NONE +type ActionAOQuantityRestriction string + +// ActionSummariesAO List of actions. +type ActionSummariesAO struct { + // Actions List of actions. + Actions *[]ActionAO `json:"actions,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// AdvancedRadiusAO defines model for AdvancedRadiusAO. +type AdvancedRadiusAO struct { + // Attribute The target attribute that should be picked randomly + // + // Example: aws.zone + Attribute string `json:"attribute"` + + // PickedValues Only in execution - the values that has been picked by the randomizer for the given execution + // + // Example: ['us-east-1a','us-east-1b'] + PickedValues *[]string `json:"pickedValues,omitempty"` + + // Value The percentage (example: `50%`) or fixed amount (example: `15#`) + // + // Example: 50% + Value string `json:"value"` +} + +// AdviceSummaryAO A pageable list of pieces of advice. +// +// Example: {"items":[{"advice":{"label":"Limit CPU Resources","status":"Validation needed","summary":"You already took action and configured a CPU limit. Validate your configuration via an experiment.","tags":["kubernetes","limit","cpu"],"type":"com.steadybit.extension_kubernetes.advice.k8s-cpu-limit"},"target":{"label":"gateway","reference":"prod-demo/steadybit-demo/gateway","type":"com.steadybit.extension_kubernetes.kubernetes-deployment"},"url":"https://platform.steadybit.com/permalink/advice/eyAiZW52..."},{"advice":{"label":"Requesting Reasonable CPU Resources","status":"Validation needed","summary":"You specified a CPU request that informs Kubernetes decision where to schedule your pods of *activemq*.\nPlease confirm that your requested CPU share is reasonable for your type of application.","tags":["kubernetes","request","cpu"],"type":"com.steadybit.extension_kubernetes.advice.k8s-cpu-request"},"target":{"label":"gateway","reference":"prod-demo/steadybit-demo/gateway","type":"com.steadybit.extension_kubernetes.kubernetes-deployment"},"url":"https://platform.steadybit.com/permalink/advice/eyAiZW52..."}],"nextOffset":3,"totalItems":108} +type AdviceSummaryAO struct { + Items *[]TargetAdviceAO `json:"items,omitempty"` + + // NextOffset Next queryable offset to query for next batch of advice + // + // Example: 21 + NextOffset *int32 `json:"nextOffset,omitempty"` + + // TotalItems Total amount of advice matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// AttributeAO An attributes (key-value-pair) that is associated to a target +// +// Example: {"key":"container.port","value":"51152:2376"} +type AttributeAO struct { + // Key The key of the attribute, may be associated multiple times to the same target + // + // Example: container.engine + Key string `json:"key"` + + // Value The value of the attribute + // + // Example: docker + Value string `json:"value"` +} + +// AuditLogEntry Audit log entry. +// +// Example: {"eventName":"experiment.created","eventTime":"2023-01-03T09:13:00Z","experiment":{"key":"ADM-1"},"id":"14av1421-aol3-4159-8ae2-47f5a9ba119e","tenant":{"key":"Demo","name":"Demo Tenant"},"trigger":{"triggerType":"HTTP_REQUEST","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"}} +type AuditLogEntry struct { + // Environment The environment in which the event was triggered + // + // Example: {"id":"1avfd231-8322-42f2-bad9-307dc962ec37","name":"Global","predicate":{"operator":"AND","predicates":[]}} + Environment *EnvironmentAL `json:"environment,omitempty"` + + // EventName Event name that was audited + // + // Example: experiment.created + EventName string `json:"eventName"` + + // EventTime The time at which the event was audited + // + // Example: 2023-01-03T09:13:00Z + EventTime time.Time `json:"eventTime"` + + // Id Unique identifier of the audit log entry + Id openapi_types.UUID `json:"id"` + + // Principal The principal that has performed the logged event + // + // Example: {"email":"example@example.com","name":"Jane Doe","principalType":"USER","role":"ADMIN","username":"1ava2afg-xju33-4c6a-9451-2854584c15be"} + Principal *PrincipalAL `json:"principal,omitempty"` + + // Team The team in which the event was triggered + // + // Example: {"id":"a2167b29-e73b-4445-8468-4670a0b459b3","key":"ADMIN","name":"Administrators"} + Team *TeamAL `json:"team,omitempty"` + + // Tenant The tenant in which the event was performed. Only relevant in case you are using multiple tenants of the Steadybit platform. + // + // Example: {"key":"Demo","name":"Demo Tenant"} + Tenant TenantAL `json:"tenant"` + + // Trigger The trigger that caused the event to happen + // + // Example: {"triggerType":"HTTP_REQUEST","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"} + Trigger *AuditLogTrigger `json:"trigger,omitempty"` +} + +// AuditLogTrigger The trigger that caused the event to happen +// +// Example: {"triggerType":"HTTP_REQUEST","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"} +type AuditLogTrigger struct { + TriggerType string `json:"triggerType"` +} + +// BaseExperimentStepAO A single step in a lane. +// +// Example: {"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"} +type BaseExperimentStepAO struct { + CustomLabel *string `json:"customLabel,omitempty"` + + // IgnoreFailure Ignore any errors and failures of this single step and continue the execution of an experiment run + // + // Example: false + IgnoreFailure *bool `json:"ignoreFailure,omitempty"` + + // MetricChecks Optional metric checks used to define success or failure of this step + MetricChecks *[]MetricCheckAO `json:"metricChecks,omitempty"` + + // MetricQueries Optional metric queries used of this step to filter e.g. monitoring data + MetricQueries *[]MetricQueryAO `json:"metricQueries,omitempty"` + + // Parameters Configuration parameters of this step that are saved during experiment design and evaluated at execution time. + // + // Example: {"duration":"30s"} + Parameters *map[string]interface{} `json:"parameters,omitempty"` + Type string `json:"type"` + union json.RawMessage +} + +// BatchPrincipalAL A batch job has performed the logged event +// +// Example: {"principalType":"BATCH_JOB","username":"af1bw7kj-d299-47ab-998f-c2a53b433820"} +type BatchPrincipalAL struct { + // PrincipalType Principal type for batch based principal + // + // Example: BATCH_JOB + PrincipalType BatchPrincipalALPrincipalType `json:"principalType"` + + // Username Username of the user, internal identifier of Steadybit + // + // Example: 13av2737-b318-4048-a79d-4789d645bc31 + Username *string `json:"username,omitempty"` +} + +// BatchPrincipalALPrincipalType Principal type for batch based principal +// +// Example: BATCH_JOB +type BatchPrincipalALPrincipalType string + +// BlastRadiusAO Blast radius that is applied to define the set of targets as well as an optional random subset +// +// Example: {"percentage":50,"predicate":{"operator":"AND","predicates":[{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}]},"targetType":"com.steadybit.extension_container.container"} +type BlastRadiusAO struct { + // Maximum In case a fixed number of as subset of specified targets should be effected + // + // Example: 2 + Maximum *int32 `json:"maximum,omitempty"` + + // Percentage In case a percentage subset of the specified targets should be effected + // + // Example: 40 + Percentage *int32 `json:"percentage,omitempty"` + + // Predicate Query defining the overall superset of targets being effected + // + // Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] + Predicate *TargetPredicateAO `json:"predicate,omitempty"` + + // TargetType Target type that is effected by that action + // + // Example: container + TargetType *string `json:"targetType,omitempty"` +} + +// CategoryRiskAO The risk for a given category in a service +// +// Example: {"advice":50,"experiment":50,"total":50} +type CategoryRiskAO struct { + // Advice The advice risk for this category, or null when no advice could be found for the given target selection + Advice *int32 `json:"advice,omitempty"` + + // Experiment The experiment risk for this category + Experiment *int32 `json:"experiment,omitempty"` + + // Total The total risk for this category + Total *int32 `json:"total,omitempty"` +} + +// ComparableValueAO defines model for ComparableValueAO. +type ComparableValueAO struct { + Type *string `json:"type,omitempty"` +} + +// CreateAccessTokenRequestAO defines model for CreateAccessTokenRequestAO. +type CreateAccessTokenRequestAO struct { + // Name Name of the Access Token to document its purpose + // + // Example: CI/CD access token + Name string `json:"name"` + + // Team Team associated with this token or null if this is an admin token + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Type Type of this token. + // + // Example: TEAM + Type CreateAccessTokenRequestAOType `json:"type"` +} + +// CreateAccessTokenRequestAOType Type of this token. +// +// Example: TEAM +type CreateAccessTokenRequestAOType string + +// CreateAccessTokenRequestV2AO defines model for CreateAccessTokenRequestV2AO. +type CreateAccessTokenRequestV2AO struct { + // ExpiresAt Expiration date of the token. If not set, the token will never expire. + // + // Example: 2027-01-01T00:00:00Z + ExpiresAt *time.Time `json:"expiresAt,omitempty"` + + // Name Name of the Access Token to document its purpose + // + // Example: CI/CD access token + Name string `json:"name"` + + // Teams Keys of teams to associate with this token. Required when type TEAM, must be empty for type ADMIN. + // + // Example: ["ADM","DEV"] + Teams *[]string `json:"teams,omitempty"` + + // Type Type of this token. + // + // Example: TEAM + Type CreateAccessTokenRequestV2AOType `json:"type"` +} + +// CreateAccessTokenRequestV2AOType Type of this token. +// +// Example: TEAM +type CreateAccessTokenRequestV2AOType string + +// CreateAccessTokenResponseAO defines model for CreateAccessTokenResponseAO. +type CreateAccessTokenResponseAO struct { + // Id Unique identifier of the access token + // + // Example: CQer2Oar + Id *string `json:"id,omitempty"` + + // Token Token to be used to authenticate in the API.
Make sure to save the generated token as you can't read it again afterwards for security-reasons. + // + // Example: a1fDXcA0.P.2dFfGl3fAq126mnVCxyPZLoEmLwPi2 + Token *string `json:"token,omitempty"` +} + +// CreateAccessTokenResponseV2AO defines model for CreateAccessTokenResponseV2AO. +type CreateAccessTokenResponseV2AO struct { + // Id Unique identifier of this access token + Id *string `json:"id,omitempty"` + + // Token The access token. Make sure to save it as you can't read it again afterwards for security-reasons. + Token *string `json:"token,omitempty"` +} + +// CreateAndRunExperimentAO Create or update the experiment with the given experiment design. +// +// Example: {"environment":"Global","lanes":[{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}],"name":"Blackhole Hot-deals","properties":{"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"},"team":"ADM"} +type CreateAndRunExperimentAO struct { + // Environment The name of the environment to be used + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // ExecutionVariables Variables that will be merged for the single experiment execution with the variables defined of the experiment or environment that the experiment will be executed in. A `key` that exists already in the experiment or environment variables will be overridden for this execution, all others will be added solely in the context of the first experiment execution. Each value is either a constant string, an array of constant strings, or a select expression object. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal"} + ExecutionVariables *map[string]VariableExpressionAO `json:"executionVariables,omitempty"` + + // ExperimentVariables Variables that will be used when the experiment will be executed. Experiment variables will override existing environment variables. Each value is either a constant string, an array of constant strings, or a select expression object (`{"type":"select",...}`). + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal","httpEndpointZones":{"attribute":"aws.zone","count":2,"mode":"fixed","targetType":"com.steadybit.extension_container.container","type":"select"},"targetServices":["gateway","hot-deals","fashion-bestseller"]} + ExperimentVariables *map[string]VariableExpressionAO `json:"experimentVariables,omitempty"` + + // ExternalId An optional external identifier used for create-or-update semantics. + // + // Example: 1234567 + ExternalId *string `json:"externalId,omitempty"` + + // ExternalReference An optional external reference. Will be removed and is replaced by tags. If used with experiment creation, the value will be added as a tag. + // + // Example: INCIDENT-4711 + // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set + ExternalReference *string `json:"externalReference,omitempty"` + + // Hypothesis The hypothesis that is validated by the experiment + // + // Example: System is able to survive a latency in the network of 1500ms + Hypothesis *string `json:"hypothesis,omitempty"` + + // Lanes The lanes (steps executed in parallel) in the experiment. Each lane consists of multiple steps that are executed sequential per lane. + // + // Example: [{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}] + Lanes []ExperimentLaneAO `json:"lanes"` + + // Name Name of the experiment to easily identify the experiment + // + // Example: Shop survives unavailability of hot-deals products + Name string `json:"name"` + + // Properties The properties of the experiment + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // SharedTeams Team keys with which the experiment is shared with + // + // Example: [OPS, SHOP] + SharedTeams *[]string `json:"sharedTeams,omitempty"` + + // Tags An optional set of tags you can use to search for. + // + // Example: ["myTag","myOtherTag"] + Tags *[]string `json:"tags,omitempty"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` +} + +// CreateAndRunExperimentFromTemplateAO Create or update an experiment based on an experiment template and run it. +// +// Example: {"environment":"steadybit-demo","executionVariables":{"httpEndpoint":"http://dev.shop.products.internal"},"externalId":"1234567","placeholders":[{"key":"CLUSTER","value":"demo-cluster"},{"key":"BOOL","value":true},{"key":"NUMBER","value":15},{"key":"KEYVALUE","value":[{"key":"example-a","value":"abc"},{"key":"example-b","value":"123"}]},{"key":"LIST","value":["entry1","entry2","entry3"]},{"key":"FILE","value":{"data":"SGVsbG8gV29ybGQh","fileName":"example.txt"}}],"team":"DEMO"} +type CreateAndRunExperimentFromTemplateAO struct { + // Environment The name of the environment to be used + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // ExecutionVariables Variables that will be merged for the single experiment execution with the variables defined of the experiment or environment that the experiment will be executed in. A `key` that exists already in the experiment or environment variables will be overridden for this execution, all others will be added solely in the context of the first experiment execution. Each value is either a constant string, an array of constant strings, or a select expression object. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal"} + ExecutionVariables *map[string]VariableExpressionAO `json:"executionVariables,omitempty"` + + // ExperimentVariables Variables that will be added to the created experiment design. A `key` that exists already in the environment variables will be overridden. Each value is either a constant string, an array of constant strings, or a select expression object. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal"} + ExperimentVariables *map[string]VariableExpressionAO `json:"experimentVariables,omitempty"` + + // ExternalId An optional external identifier used for create-or-update semantics. + // + // Example: 1234567 + ExternalId *string `json:"externalId,omitempty"` + + // Placeholders List of template placeholder values + Placeholders *[]ExperimentTemplatePlaceholderValueAO `json:"placeholders,omitempty"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` +} + +// CreateExperimentAO Create or update the experiment with the given experiment design. +// +// Example: {"environment":"Global","lanes":[{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}],"name":"Blackhole Hot-deals","properties":{"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"},"sharedTeams":["SHOP"],"team":"ADM"} +type CreateExperimentAO struct { + // Environment The name of the environment to be used + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // ExperimentVariables Variables that will be used when the experiment will be executed. Experiment variables will override existing environment variables. Each value is either a constant string, an array of constant strings, or a select expression object (`{"type":"select",...}`). + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal","httpEndpointZones":{"attribute":"aws.zone","count":2,"mode":"fixed","targetType":"com.steadybit.extension_container.container","type":"select"},"targetServices":["gateway","hot-deals","fashion-bestseller"]} + ExperimentVariables *map[string]VariableExpressionAO `json:"experimentVariables,omitempty"` + + // ExternalId An optional external identifier used for create-or-update semantics. + // + // Example: 1234567 + ExternalId *string `json:"externalId,omitempty"` + + // ExternalReference An optional external reference. Will be removed and is replaced by tags. If used with experiment creation, the value will be added as a tag. + // + // Example: INCIDENT-4711 + // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set + ExternalReference *string `json:"externalReference,omitempty"` + + // Hypothesis The hypothesis that is validated by the experiment + // + // Example: System is able to survive a latency in the network of 1500ms + Hypothesis *string `json:"hypothesis,omitempty"` + + // Lanes The lanes (steps executed in parallel) in the experiment. Each lane consists of multiple steps that are executed sequential per lane. + // + // Example: [{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}] + Lanes []ExperimentLaneAO `json:"lanes"` + + // Name Name of the experiment to easily identify the experiment + // + // Example: Shop survives unavailability of hot-deals products + Name string `json:"name"` + + // Properties The properties of the experiment + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // SharedTeams Team keys with which the experiment is shared with + // + // Example: [OPS, SHOP] + SharedTeams *[]string `json:"sharedTeams,omitempty"` + + // Tags An optional set of tags you can use to search for. + // + // Example: ["myTag","myOtherTag"] + Tags *[]string `json:"tags,omitempty"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` +} + +// CreateExperimentFromTemplateAO Create or update an experiment based on an experiment template. +// +// Example: {"environment":"steadybit-demo","experimentVariables":{"httpEndpoint":"http://dev.shop.products.internal"},"externalId":"1234567","placeholders":[{"key":"CLUSTER","value":"demo-cluster"},{"key":"BOOL","value":true},{"key":"NUMBER","value":15},{"key":"KEYVALUE","value":[{"key":"example-a","value":"abc"},{"key":"example-b","value":"123"}]},{"key":"LIST","value":["entry1","entry2","entry3"]},{"key":"FILE","value":{"data":"SGVsbG8gV29ybGQh","fileName":"example.txt"}}],"team":"DEMO"} +type CreateExperimentFromTemplateAO struct { + // Environment The name of the environment to be used + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // ExperimentVariables Variables that will be added to the created experiment design. A `key` that exists already in the environment variables will be overridden. Each value is either a constant string, an array of constant strings, or a select expression object. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal"} + ExperimentVariables *map[string]VariableExpressionAO `json:"experimentVariables,omitempty"` + + // ExternalId An optional external identifier used for create-or-update semantics. + // + // Example: 1234567 + ExternalId *string `json:"externalId,omitempty"` + + // Placeholders List of template placeholder values + Placeholders *[]ExperimentTemplatePlaceholderValueAO `json:"placeholders,omitempty"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` +} + +// CursorSliceResponseAOTargetAO defines model for CursorSliceResponseAOTargetAO. +type CursorSliceResponseAOTargetAO struct { + // HasNext Are there more items that can be fetched with the given nextCursor? + // + // Example: true + HasNext *bool `json:"hasNext,omitempty"` + Items *[]TargetAO `json:"items,omitempty"` + + // NextCursor The cursor to use to fetch the next page + // + // Example: eyJhZ2VudElkIjogImFnZW50LTEyMyIsICJuYW1lIjogImRlcGxveW1lbnQtYSIsICJ0eXBlIjogImNvbS5zdGVhZHliaXQuZXh0ZW5zaW9uX2t1YmVybmV0ZXMua3ViZXJuZXRlcy1kZXBsb3ltZW50In0= + NextCursor *string `json:"nextCursor,omitempty"` +} + +// CustomWebhookAO defines model for CustomWebhookAO. +type CustomWebhookAO struct { + // Events The events that are being sent or a list containing a single `*` if all supported event types should be used. + // + // Supported Events: + // - "experiment.execution.requested" + // - "experiment.execution.created" + // - "experiment.execution.preflight" + // - "experiment.execution.completed" + // - "experiment.execution.failed" + // - "experiment.execution.errored" + // - "experiment.execution.canceled" + // - "experiment.execution.step-started" + // - "experiment.execution.step-completed" + // - "experiment.execution.step-failed" + // - "experiment.execution.step-errored" + // - "experiment.execution.step-canceled" + // - "experiment.execution.step-skipped" + // - "killswitch.engaged" + // - "killswitch.disengaged" + // + // + // Example: ["experiment.execution.created","experiment.execution.completed"] + Events []string `json:"events"` + + // Headers Additional headers to include in the webhook request. + // + // Example: {"X-Another-Header":"AnotherValue","X-Custom-Header":"CustomValue"} + Headers *map[string]string `json:"headers,omitempty"` + + // Id The id of the webhook + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id openapi_types.UUID `json:"id"` + + // Name The name of the webhook + // + // Example: Custom Webhook + Name string `json:"name"` + + // Scope The scope of the webhook / integration + // + // Example: TEAM + Scope CustomWebhookAOScope `json:"scope"` + + // Secret If a secret is provided a signature of the body is computed using `HMAC SHA-256` and sent as `X-SB-Signature` http header. You can use this header to verify the message. + // + // Example: secret123!! + Secret *string `json:"secret,omitempty"` + + // TargetAttributeIncludes The body size can get very large as we include all target attributes for each target of your experiments. When having experiments with many targets, it might be useful to filter the attributes to only include the ones you are interested in. You can use the wildcard character '*' to match all attributes or a comma-separated-list of attribute-names. If the field is empty, no attributes will be included. + // + // Example: ["k8s.cluster-name","k8s.deployment"] + TargetAttributeIncludes []string `json:"targetAttributeIncludes"` + + // Team The key of the team if the scope is `TEAM` + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Url The URL of the webhook + // + // Example: https://example.com/webhook + Url string `json:"url"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version int32 `json:"version"` +} + +// CustomWebhookAOScope The scope of the webhook / integration +// +// Example: TEAM +type CustomWebhookAOScope string + +// CustomWebhookUpsertAO defines model for CustomWebhookUpsertAO. +type CustomWebhookUpsertAO struct { + // Events The events that are being sent or a list containing a single `*` if all supported event types should be used. + // + // Supported Events: + // - "experiment.execution.requested" + // - "experiment.execution.created" + // - "experiment.execution.preflight" + // - "experiment.execution.completed" + // - "experiment.execution.failed" + // - "experiment.execution.errored" + // - "experiment.execution.canceled" + // - "experiment.execution.step-started" + // - "experiment.execution.step-completed" + // - "experiment.execution.step-failed" + // - "experiment.execution.step-errored" + // - "experiment.execution.step-canceled" + // - "experiment.execution.step-skipped" + // - "killswitch.engaged" + // - "killswitch.disengaged" + // + // + // Example: ["experiment.execution.created","experiment.execution.completed"] + Events []string `json:"events"` + + // Headers Additional headers to include in the webhook request. + // + // Example: {"X-Another-Header":"AnotherValue","X-Custom-Header":"CustomValue"} + Headers *map[string]string `json:"headers,omitempty"` + + // Id The id of the webhook or null if a new webhook should be created. + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id *openapi_types.UUID `json:"id,omitempty"` + + // Name The name of the webhook + // + // Example: Custom Webhook + Name string `json:"name"` + + // Scope The scope of the webhook / integration + // + // Example: TEAM + Scope CustomWebhookUpsertAOScope `json:"scope"` + + // Secret If a secret is provided a signature of the body is computed using `HMAC SHA-256` and sent as `X-SB-Signature` http header. You can use this header to verify the message. + // + // Example: secret123!! + Secret *string `json:"secret,omitempty"` + + // TargetAttributeIncludes The body size can get very large as we include all target attributes for each target of your experiments. When having experiments with many targets, it might be useful to filter the attributes to only include the ones you are interested in. You can use the wildcard character '*' to match all attributes or a comma-separated-list of attribute-names. If the field is empty, no attributes will be included. + // + // Example: ["k8s.cluster-name","k8s.deployment"] + TargetAttributeIncludes []string `json:"targetAttributeIncludes"` + + // Team The key of the team if the scope is `TEAM` + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Url The URL of the webhook + // + // Example: https://example.com/webhook + Url string `json:"url"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// CustomWebhookUpsertAOScope The scope of the webhook / integration +// +// Example: TEAM +type CustomWebhookUpsertAOScope string + +// DefaultBlastRadiusAO Default blast radius configuration for an action. +type DefaultBlastRadiusAO struct { + // Mode Mode of the default blast radius. + // + // Example: PERCENTAGE + Mode string `json:"mode"` + + // Value Value for the mode. Percentage (0-100) when mode is PERCENTAGE, max target count when mode is MAXIMUM. + // + // Example: 100 + Value *int32 `json:"value,omitempty"` +} + +// EnvironmentAL The environment in which the event was triggered +// +// Example: {"id":"1avfd231-8322-42f2-bad9-307dc962ec37","name":"Global","predicate":{"operator":"AND","predicates":[]}} +type EnvironmentAL struct { + Id openapi_types.UUID `json:"id"` + Name string `json:"name"` + + // Predicate Query defining the overall superset of targets being effected + // + // Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] + Predicate TargetPredicateAO `json:"predicate"` +} + +// EnvironmentAO An environment for limiting the access to discovered systems for a team. +// +// Example: {"id":"2v1av42-e525-4c00-a13a-1ac32d170724","name":"Global","query":"aws.account=\"123\" OR aws.account=\"456\"","state":"READY","version":0} +type EnvironmentAO struct { + // Id Unique identifier of a environment + Id *openapi_types.UUID `json:"id,omitempty"` + + // Name Name of the environment. + // + // Example: Global + Name string `json:"name"` + + // Predicate Query defining the overall superset of targets being effected + // + // Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] + Predicate TargetPredicateAO `json:"predicate"` + + // Query Alternative to `predicate`. If both `query` and `predicate` will be provided, `query` will override the `predicate`. + // + // Example: (aws.account="123" OR aws.account="456" + Query *string `json:"query,omitempty"` + + // State State of the environment to indicate current background tasks. + // + // Example: "READY" + State EnvironmentAOState `json:"state"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// EnvironmentAOState State of the environment to indicate current background tasks. +// +// Example: "READY" +type EnvironmentAOState string + +// EnvironmentSummariesAO List of environments. +// +// Example: {"environments":[{"id":"2v1av42-e525-4c00-a13a-1ac32d170724","name":"Global","query":"aws.account=\"123\" OR aws.account=\"456\"","state":"READY","version":0}]} +type EnvironmentSummariesAO struct { + Environments *[]EnvironmentAO `json:"environments,omitempty"` +} + +// ExecuteExperimentRequestAO Experiment execution data that should be used only for that specific experiment execution and will not update the experiment design. +// +// Example: {} +type ExecuteExperimentRequestAO struct { + // Environment The name of the environment with which the experiment execution should be overridden once and executed in + // + // Example: Shop Stage + Environment *string `json:"environment,omitempty"` + + // Variables Variables that will be merged for the single experiment execution with the variables defined of the experiment or environment that the experiment will be executed in. A `key` that exists already in the experiment or environment variables will be overridden for this execution, all others will be added solely in the context of this experiment execution. Existing variables don't have to be repeated in this parameter. Each value is either a constant string, an array of constant strings, or a select expression object. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal"} + Variables *map[string]VariableExpressionAO `json:"variables,omitempty"` +} + +// ExecuteExperimentResponseAO A single experiment execution that was triggered from a single experiment. +// +// Example: { +// "id": 58070, +// "key": "SHOP-1" +// "apiLocation": "https://api.steadybit.com/experiments/execute/SHOP-1", +// "uiLocation": "https://platform.steadybit.com/experiments/edit/SHOP-1/executions/1234" +// } +type ExecuteExperimentResponseAO struct { + // ApiLocation A link to the API for the experiment execution + // + // Example: https://api.steadybit.com/experiments/execute/SHOP-1 + ApiLocation string `json:"apiLocation"` + + // ExecutionId Unique experiment execution id that identifies this specific experiment execution + // + // Example: 1234 + ExecutionId *int64 `json:"executionId,omitempty"` + + // Key Unique experiment key that identifies the experiment. Combination of `team key` and increasing number + // + // Example: SHOP-1 + Key string `json:"key"` + + // UiLocation A link to the UI for the experiment execution + // + // Example: https://platform.steadybit.com/experiments/edit/SHOP-1/executions/1234 + UiLocation string `json:"uiLocation"` +} + +// ExperimentAO Example: {"created":"2023-05-03T08:24:30.183237Z","createdBy":{"name":"Manuel","pictureUrl":"https://.../picture.png","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"},"edited":"2023-05-03T13:31:12.533664Z","editedBy":{"name":"Manuel","pictureUrl":"https://.../picture.png","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"},"environment":"Global","key":"ADM-2","lanes":[{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}],"name":"Blackhole Hot-deals","properties":{"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"},"sharedTeams":["SHOP"],"team":"ADM"} +type ExperimentAO struct { + // Created Timestamp when the experiment was created + // + // Example: 2023-01-01T09:00:00Z + Created time.Time `json:"created"` + + // CreatedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + CreatedBy UserSummaryAO `json:"createdBy"` + + // Edited Timestamp when the experiment was edited the last time + // + // Example: 2023-01-01T09:00:00Z + Edited time.Time `json:"edited"` + + // EditedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + EditedBy UserSummaryAO `json:"editedBy"` + + // Environment The name of the environment to be used + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // ExperimentVariables Variables that will be used when the experiment will be executed. Experiment variables will override existing environment variables. Each value is either a constant string, an array of constant strings, or a select expression object (`{"type":"select",...}`). + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal","httpEndpointZones":{"attribute":"aws.zone","count":2,"mode":"fixed","targetType":"com.steadybit.extension_container.container","type":"select"},"targetServices":["gateway","hot-deals","fashion-bestseller"]} + ExperimentVariables *map[string]VariableExpressionAO `json:"experimentVariables,omitempty"` + + // ExternalId An optional external identifier used for create-or-update semantics. + // + // Example: 1234567 + ExternalId *string `json:"externalId,omitempty"` + + // ExternalReference An optional external reference. Will be removed and is replaced by tags. If used with experiment creation, the value will be added as a tag. + // + // Example: INCIDENT-4711 + // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set + ExternalReference *string `json:"externalReference,omitempty"` + + // Hypothesis The hypothesis that is validated by the experiment + // + // Example: System is able to survive a latency in the network of 1500ms + Hypothesis *string `json:"hypothesis,omitempty"` + + // Key Unique experiment key that identifies the experiment. Combination of `team key` and increasing number + // + // Example: ADM-2 + Key string `json:"key"` + + // Lanes The lanes (steps executed in parallel) in the experiment. Each lane consists of multiple steps that are executed sequential per lane. + // + // Example: [{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}] + Lanes []ExperimentLaneAO `json:"lanes"` + + // Name Name of the experiment to easily identify the experiment + // + // Example: Shop survives unavailability of hot-deals products + Name string `json:"name"` + + // Properties The properties of the experiment + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // SharedTeams Team keys with which the experiment is shared with + // + // Example: [OPS, SHOP] + SharedTeams *[]string `json:"sharedTeams,omitempty"` + + // Tags An optional set of tags you can use to search for. + // + // Example: ["myTag","myOtherTag"] + Tags *[]string `json:"tags,omitempty"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` + + // TemplatePlaceholders The placeholders that were used to create this experiment from a template + TemplatePlaceholders *[]ExperimentTemplatePlaceholderValueAO `json:"templatePlaceholders,omitempty"` + + // TemplateTitle The title of the template that was used to create this experiment + TemplateTitle *string `json:"templateTitle,omitempty"` + + // Version Experiment database version. + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// ExperimentExecutionAO A single experiment execution that was triggered from a single experiment. +// +// Example: {"created":"2023-01-01T09:00:01.000000Z","createdBy":{"name":"Manuel","pictureUrl":"https://.../picture.png","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"},"createdVia":"UI","ended":"2023-01-01T09:10:00.000000Z","experimentVersion":"5","hypothesis":"When a single container from steadybit-demo/fashion-bestseller fails the shop is still working as expected.","id":58070,"key":"SHOP-1","name":"Shop should survive a single pod outage","reason":"Check failure.","requested":"2023-01-01T09:00:00.000000Z","state":"FAILED"} +type ExperimentExecutionAO struct { + // CanceledBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + CanceledBy *UserSummaryAO `json:"canceledBy,omitempty"` + + // Created Timestamp when the experiment was created + // + // Example: 2023-01-01T09:00:01Z + Created *time.Time `json:"created,omitempty"` + + // CreatedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + CreatedBy *UserSummaryAO `json:"createdBy,omitempty"` + + // CreatedVia The creation trigger that caused this experiment execution to be started + // + // Example: UI + CreatedVia *ExperimentExecutionAOCreatedVia `json:"createdVia,omitempty"` + + // Ended Timestamp when the experiment ended + // + // Example: 2023-01-01T09:00:00Z + Ended *time.Time `json:"ended,omitempty"` + + // ExperimentVersion Experiment design version which can be used to identify changes between experiment runs + // + // Example: 5 + ExperimentVersion *int32 `json:"experimentVersion,omitempty"` + + // Hypothesis The hypothesis that is validated by the experiment + // + // Example: System is able to survive a latency in the network of 1500ms + Hypothesis *string `json:"hypothesis,omitempty"` + + // Id Unique experiment execution id that identifies this specific experiment execution + // + // Example: 1523 + Id *int32 `json:"id,omitempty"` + + // Key Unique experiment key that identifies the experiment. Combination of `team key` and increasing number + // + // Example: ADM-2 + Key *string `json:"key,omitempty"` + + // Name Name of the experiment to easily identify the experiment + // + // Example: Shop survives unavailability of hot-deals products + Name *string `json:"name,omitempty"` + + // Properties The properties of the experiment execution + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"Chuck Norris allows that execution"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // PropertiesVersion Version of the properties for optimistic locking (optional in the Update-API) + // + // Example: 1 + PropertiesVersion *int32 `json:"propertiesVersion,omitempty"` + + // Reason Reason in case the experiment execution failed or errored + // + // Example: Action error + Reason *string `json:"reason,omitempty"` + + // Requested Timestamp when the experiment was requested + // + // Example: 2023-01-01T09:00:00Z + Requested *time.Time `json:"requested,omitempty"` + + // Started Timestamp when the experiment was started + // + // Example: 2023-01-01T09:00:02Z + Started *time.Time `json:"started,omitempty"` + + // State Current state of the experiment (e.g. CREATED, RUNNING, FAILED, ERRORED, COMPLETED) + // + // Example: RUNNING + State *string `json:"state,omitempty"` + + // Steps The steps that are executed in parallel or sequence in the experiment. + // + // Example: [{"ignoreFailure":false,"parameters":{"duration":"10s"}},{"actionId":"com.steadybit.extension_container.stress_cpu","actionKind":"ATTACK","ignoreFailure":false,"parameters":{"cpuLoad":100,"duration":"30s","workers":0},"predecessorId":"40b0f797-912d-4256-8887-1553561962a9","radius":{"percentage":50,"predicate":{"operator":"AND","predicates":[{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}]},"targetType":"com.steadybit.extension_container.container"},"targetExecutions":[{"attributes":[{"key":"container.port","value":"51152:2376"},{"key":"container.engine","value":"docker"},{"key":"container.host/name","value":"docker-desktop/minikube"},{"key":"container.host","value":"docker-desktop"}],"name":"docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea","state":"COMPLETED","type":"com.steadybit.extension_container.container"}],"totalTargetCount":1}] + Steps *[]AbstractExperimentExecutionStepAO `json:"steps,omitempty"` + + // Tags Tags of the experiment at the time the execution was requested + // + // Example: ["resilience","shop"] + Tags *[]string `json:"tags,omitempty"` + + // Variables Variables and their origins that have been used for this execution + // + // Example: {"httpEndpoint":{"origin":"ENVIRONMENT","value":"http://dev.shop.products.internal"}} + Variables *map[string]ExperimentExecutionVariableAO `json:"variables,omitempty"` +} + +// ExperimentExecutionAOCreatedVia The creation trigger that caused this experiment execution to be started +// +// Example: UI +type ExperimentExecutionAOCreatedVia string + +// ExperimentExecutionPageItemAO defines model for ExperimentExecutionPageItemAO. +type ExperimentExecutionPageItemAO struct { + // Created Timestamp when the experiment execution was created + // + // Example: 2023-01-01T09:00:01Z + Created *time.Time `json:"created,omitempty"` + CreatedBy *string `json:"createdBy,omitempty"` + + // CreatedByDetails The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + CreatedByDetails *UserSummaryAO `json:"createdByDetails,omitempty"` + + // Ended Timestamp when the experiment execution was ended + // + // Example: 2023-01-01T09:00:01Z + Ended *time.Time `json:"ended,omitempty"` + + // Environment The name of the environment that this execution was using + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // ExperimentKey Unique experiment key that identifies the experiment. Combination of `team key` and increasing number + // + // Example: ADM-2 + ExperimentKey *string `json:"experimentKey,omitempty"` + + // Id Unique experiment execution id that identifies a single experiment execution + // + // Example: 123 + Id *int64 `json:"id,omitempty"` + + // Name Name of the experiment to easily identify the experiment + // + // Example: Shop survives unavailability of hot-deals products + Name *string `json:"name,omitempty"` + + // Properties The properties of the experiment execution + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"Chuck Norris allows that execution"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // Reason Details about the failure/error reason. + Reason *string `json:"reason,omitempty"` + + // Requested Timestamp when the experiment execution was requested + // + // Example: 2023-01-01T09:00:01Z + Requested *time.Time `json:"requested,omitempty"` + + // Scheduled Was this execution triggered by a schedule? + // + // Example: true + Scheduled *bool `json:"scheduled,omitempty"` + + // Started Timestamp when the experiment execution was started + // + // Example: 2023-01-01T09:00:01Z + Started *time.Time `json:"started,omitempty"` + + // State Current state of the experiment execution (e.g. RUNNING, FAILED, ERRORED, COMPLETED) + // + // Example: RUNNING + State *string `json:"state,omitempty"` + + // TeamKey The key of the team that this experiment is assigned to + // + // Example: ADM + TeamKey *string `json:"teamKey,omitempty"` +} + +// ExperimentExecutionReportFilterAO Filter for experiment execution report data, optionally scoped to specific teams, environments, and services. +type ExperimentExecutionReportFilterAO struct { + // EnvironmentIds Restrict results to the given environment IDs. If not provided, all environments are included. + EnvironmentIds *[]openapi_types.UUID `json:"environmentIds,omitempty"` + + // From Start date of the report range (inclusive). + // + // Example: 2026-01-01 + From openapi_types.Date `json:"from"` + + // Rollup The time bucket granularity for report aggregation. + // + // Example: MONTHLY + Rollup *ExperimentExecutionReportFilterAORollup `json:"rollup,omitempty"` + + // ServiceIds Restrict results to the given service IDs. If not provided, all services are included. + ServiceIds *[]openapi_types.UUID `json:"serviceIds,omitempty"` + + // TeamIds Restrict results to the given team IDs. If not provided, all teams are included. + TeamIds *[]openapi_types.UUID `json:"teamIds,omitempty"` + + // To End date of the report range (inclusive). + // + // Example: 2026-03-01 + To openapi_types.Date `json:"to"` +} + +// ExperimentExecutionReportFilterAORollup The time bucket granularity for report aggregation. +// +// Example: MONTHLY +type ExperimentExecutionReportFilterAORollup string + +// ExperimentExecutionStepActionAO An action-step that is executed as part of an experiment. +// +// Example: {"actionId":"com.steadybit.extension_container.stress_cpu","actionKind":"ATTACK","ended":"2025-10-08T13:11:11.490268Z","id":"0199c3f2-48c7-706d-b102-9cb09dd41b5d","ignoreFailure":false,"parameters":{"cpuLoad":100,"duration":"30s","workers":0},"predecessorId":"40b0f797-912d-4256-8887-1553561962a9","radius":{"percentage":50,"predicate":{"operator":"AND","predicates":[{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}]},"targetType":"com.steadybit.extension_container.container"},"started":"2025-10-08T13:11:01.487541Z","state":"COMPLETED","stepType":"ACTION","targetExecutions":[{"attributes":[{"key":"container.port","value":"51152:2376"},{"key":"container.engine","value":"docker"},{"key":"container.host/name","value":"docker-desktop/minikube"},{"key":"container.host","value":"docker-desktop"}],"name":"docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea","state":"COMPLETED","type":"com.steadybit.extension_container.container"}],"totalTargetCount":1} +type ExperimentExecutionStepActionAO struct { + // ActionId Unique identifier of the action that is executed in this step + // + // Example: com.steadybit.extension_container.stress_cpu + ActionId *string `json:"actionId,omitempty"` + + // ActionKind Kind of the action (e.g. attack, check, loadtest) + // + // Example: ATTACK + ActionKind *ExperimentExecutionStepActionAOActionKind `json:"actionKind,omitempty"` + + // CustomLabel Custom label assigned during experiment design to express the intention of this step + // + // Example: Container 'xyz' can not be reached + CustomLabel *string `json:"customLabel,omitempty"` + + // Ended Timestamp when this experiment step ended + // + // Example: 2023-01-01T09:00:00Z + Ended *time.Time `json:"ended,omitempty"` + + // Id Unique identifier of this step execution + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + Id *openapi_types.UUID `json:"id,omitempty"` + + // IgnoreFailure Whether the experiment should fail/error immediately in case this step fails/errors. + // + // Example: false + IgnoreFailure *bool `json:"ignoreFailure,omitempty"` + + // Parameters Step-specific parameters of the experiment step configuration + // + // Example: {"duration":"10s"} + Parameters *map[string]interface{} `json:"parameters,omitempty"` + + // PredecessorId Unique identifier of the step execution that precedes this step, null if it is the first step of a lane + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + PredecessorId *openapi_types.UUID `json:"predecessorId,omitempty"` + + // Radius Blast radius that is applied to define the set of targets as well as an optional random subset + // + // Example: {"percentage":50,"predicate":{"operator":"AND","predicates":[{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}]},"targetType":"com.steadybit.extension_container.container"} + Radius *BlastRadiusAO `json:"radius,omitempty"` + + // Reason Reason in case this experiment step execution failed or errored + // + // Example: Couldn't read state of container... + Reason *string `json:"reason,omitempty"` + + // Started Timestamp when this experiment step was started + // + // Example: 2023-01-01T09:00:00Z + Started *time.Time `json:"started,omitempty"` + + // State Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) + // + // Example: RUNNING + State *string `json:"state,omitempty"` + + // StepType Type of this step execution (e.g. ACTION, WAIT) + // + // Example: ACTION + StepType string `json:"stepType"` + + // TargetExecutions List of targets that are expected to be effected by this action. This list may change in case targets aren't available at the specific time of execution + // + // Example: [{"attributes":[{"key":"container.port","value":"51152:2376"},{"key":"container.engine","value":"docker"},{"key":"container.host/name","value":"docker-desktop/minikube"},{"key":"container.host","value":"docker-desktop"}],"name":"docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea","state":"COMPLETED","type":"com.steadybit.extension_container.container"}] + TargetExecutions *[]TargetExecutionAO `json:"targetExecutions,omitempty"` + + // TotalTargetCount Amount of targets that are effect int total + // + // Example: 23 + TotalTargetCount *int64 `json:"totalTargetCount,omitempty"` +} + +// ExperimentExecutionStepActionAOActionKind Kind of the action (e.g. attack, check, loadtest) +// +// Example: ATTACK +type ExperimentExecutionStepActionAOActionKind string + +// ExperimentExecutionStepServiceValidationAO A service validation step that is executed as part of an experiment. +type ExperimentExecutionStepServiceValidationAO struct { + // CustomLabel Custom label assigned during experiment design to express the intention of this step + // + // Example: Container 'xyz' can not be reached + CustomLabel *string `json:"customLabel,omitempty"` + + // Ended Timestamp when this experiment step ended + // + // Example: 2023-01-01T09:00:00Z + Ended *time.Time `json:"ended,omitempty"` + + // Id Unique identifier of this step execution + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + Id *openapi_types.UUID `json:"id,omitempty"` + + // IgnoreFailure Whether the experiment should fail/error immediately in case this step fails/errors. + // + // Example: false + IgnoreFailure *bool `json:"ignoreFailure,omitempty"` + + // Parameters Step-specific parameters of the experiment step configuration + // + // Example: {"duration":"10s"} + Parameters *map[string]interface{} `json:"parameters,omitempty"` + + // PredecessorId Unique identifier of the step execution that precedes this step, null if it is the first step of a lane + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + PredecessorId *openapi_types.UUID `json:"predecessorId,omitempty"` + + // Reason Reason in case this experiment step execution failed or errored + // + // Example: Couldn't read state of container... + Reason *string `json:"reason,omitempty"` + + // ServiceId Unique identifier of the service. + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + ServiceId *openapi_types.UUID `json:"serviceId,omitempty"` + + // Started Timestamp when this experiment step was started + // + // Example: 2023-01-01T09:00:00Z + Started *time.Time `json:"started,omitempty"` + + // State Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) + // + // Example: RUNNING + State *string `json:"state,omitempty"` + + // StepType Type of this step execution (e.g. ACTION, WAIT) + // + // Example: ACTION + StepType string `json:"stepType"` + + // Validations List of actions performed as part of this service validation step. + Validations *[]ExperimentExecutionStepActionAO `json:"validations,omitempty"` + union json.RawMessage +} + +// ExperimentExecutionStepWaitAO A wait step that is executed as part of an experiment. +// +// Example: {"ended":"2025-06-18T08:32:11.886043Z","id":"40b0f797-912d-4256-8887-1553561962a9","ignoreFailure":false,"parameters":{"duration":"10s"},"predecessorId":null,"started":"2025-06-18T08:32:01.850479Z","state":"COMPLETED","stepType":"WAIT"} +type ExperimentExecutionStepWaitAO struct { + // CustomLabel Custom label assigned during experiment design to express the intention of this step + // + // Example: Container 'xyz' can not be reached + CustomLabel *string `json:"customLabel,omitempty"` + + // Ended Timestamp when this experiment step ended + // + // Example: 2023-01-01T09:00:00Z + Ended *time.Time `json:"ended,omitempty"` + + // Id Unique identifier of this step execution + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + Id *openapi_types.UUID `json:"id,omitempty"` + + // IgnoreFailure Whether the experiment should fail/error immediately in case this step fails/errors. + // + // Example: false + IgnoreFailure *bool `json:"ignoreFailure,omitempty"` + + // Parameters Step-specific parameters of the experiment step configuration + // + // Example: {"duration":"10s"} + Parameters *map[string]interface{} `json:"parameters,omitempty"` + + // PredecessorId Unique identifier of the step execution that precedes this step, null if it is the first step of a lane + // + // Example: 40b0f797-912d-4256-8887-1553561962a9 + PredecessorId *openapi_types.UUID `json:"predecessorId,omitempty"` + + // Reason Reason in case this experiment step execution failed or errored + // + // Example: Couldn't read state of container... + Reason *string `json:"reason,omitempty"` + + // Started Timestamp when this experiment step was started + // + // Example: 2023-01-01T09:00:00Z + Started *time.Time `json:"started,omitempty"` + + // State Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) + // + // Example: RUNNING + State *string `json:"state,omitempty"` + + // StepType Type of this step execution (e.g. ACTION, WAIT) + // + // Example: ACTION + StepType string `json:"stepType"` +} + +// ExperimentExecutionSummariesAO List of experiment exeuctions. +// +// Example: {"executions":[{"created":"2023-01-01T09:00:01.000000Z","ended":"2023-01-01T09:01:00.000000Z","id":102,"key":"SHOP-1","name":"Shop survives outage of a single pod","state":"FAILED"},{"created":"2023-01-01T09:10:00.000000Z","ended":"2023-01-01T09:11:00.000000Z","id":103,"key":"SHOP-1","name":"Shop survives outage of a single pod","state":"COMPLETED"},{"created":"2023-01-01T09:30:00.000000Z","ended":"2023-01-01T09:41:00.000000Z","id":110,"key":"SHOP-2","name":"DataDog monitors notices pod unavailability","state":"COMPLETED"}]} +type ExperimentExecutionSummariesAO struct { + // Executions List of experiment executions + // + // Example: [{"created":"2023-01-01T09:00:01.000000Z","ended":"2023-01-01T09:01:00.000000Z","id":102,"key":"SHOP-1","name":"Shop survives outage of a single pod","requested":"2023-01-01T09:00:00.000000Z","started":"2023-01-01T09:00:02.000000Z","state":"FAILED"},{"created":"2023-01-01T09:10:01.000000Z","ended":"2023-01-01T09:11:00.000000Z","id":103,"key":"SHOP-1","name":"Shop survives outage of a single pod","requested":"2023-01-01T09:10:00.000000Z","started":"2023-01-01T09:10:02.000000Z","state":"COMPLETED"},{"created":"2023-01-01T09:30:01.000000Z","ended":"2023-01-01T09:41:00.000000Z","id":110,"key":"SHOP-2","name":"DataDog monitors notices pod unavailability","requested":"2023-01-01T09:30:00.000000Z","started":"2023-01-01T09:30:02.000000Z","state":"COMPLETED"}] + Executions *[]ExperimentExecutionSummaryAO `json:"executions,omitempty"` +} + +// ExperimentExecutionSummaryAO List of experiment executions +// +// Example: [{"created":"2023-01-01T09:00:01.000000Z","ended":"2023-01-01T09:01:00.000000Z","id":102,"key":"SHOP-1","name":"Shop survives outage of a single pod","requested":"2023-01-01T09:00:00.000000Z","started":"2023-01-01T09:00:02.000000Z","state":"FAILED"},{"created":"2023-01-01T09:10:01.000000Z","ended":"2023-01-01T09:11:00.000000Z","id":103,"key":"SHOP-1","name":"Shop survives outage of a single pod","requested":"2023-01-01T09:10:00.000000Z","started":"2023-01-01T09:10:02.000000Z","state":"COMPLETED"},{"created":"2023-01-01T09:30:01.000000Z","ended":"2023-01-01T09:41:00.000000Z","id":110,"key":"SHOP-2","name":"DataDog monitors notices pod unavailability","requested":"2023-01-01T09:30:00.000000Z","started":"2023-01-01T09:30:02.000000Z","state":"COMPLETED"}] +type ExperimentExecutionSummaryAO struct { + // Created Timestamp when the experiment execution was created + // + // Example: 2023-01-01T09:00:01Z + Created *time.Time `json:"created,omitempty"` + + // Ended Timestamp when the experiment execution ended + // + // Example: 2023-01-01T09:01:00Z + Ended *time.Time `json:"ended,omitempty"` + + // Id Unique experiment execution id that identifies a single experiment execution + // + // Example: 123 + Id *int32 `json:"id,omitempty"` + + // Key Unique experiment key that identifies the experiment. Combination of `team key` and increasing number + // + // Example: ADM-2 + Key *string `json:"key,omitempty"` + + // Name Name of the experiment to easily identify the experiment + // + // Example: Shop survives unavailability of hot-deals products + Name *string `json:"name,omitempty"` + + // Properties The properties of the experiment execution + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"Chuck Norris allows that execution"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // Requested Timestamp when the experiment execution was requested + // + // Example: 2023-01-01T09:00:00Z + Requested *time.Time `json:"requested,omitempty"` + + // Started Timestamp when the experiment execution started + // + // Example: 2023-01-01T09:00:02Z + Started *time.Time `json:"started,omitempty"` + + // State Current state of the experiment execution (e.g. RUNNING, FAILED, ERRORED, COMPLETED) + // + // Example: RUNNING + State *string `json:"state,omitempty"` +} + +// ExperimentExecutionVariableAO The variables resolved for this specific execution, keyed by name. Each entry carries the resolved value(s) and the tier the winning value originated from (ENVIRONMENT, SERVICE, EXPERIMENT, SCHEDULE, EXECUTION). A single-value variable's value is a string, a multi-value variable's value is an array of strings. Empty until the execution starts, as dynamic values are resolved once at run start and then stay stable for the whole run. +// +// Example: {"httpEndpoint":{"origin":"EXECUTION","value":"http://shop.products.internal"}} +type ExperimentExecutionVariableAO struct { + Origin *ExperimentExecutionVariableAOOrigin `json:"origin,omitempty"` + + // Value Either a single value (the common case, including single-element select results) or an array of values (multi-value select expressions). + Value *ExperimentExecutionVariableAO_Value `json:"value,omitempty"` +} + +// ExperimentExecutionVariableAOOrigin defines model for ExperimentExecutionVariableAO.Origin. +type ExperimentExecutionVariableAOOrigin string + +// ExperimentExecutionVariableAOValue0 defines model for ExperimentExecutionVariableAO.Value.0. +type ExperimentExecutionVariableAOValue0 = string + +// ExperimentExecutionVariableAOValue1 defines model for ExperimentExecutionVariableAO.Value.1. +type ExperimentExecutionVariableAOValue1 = []string + +// ExperimentExecutionVariableAO_Value Either a single value (the common case, including single-element select results) or an array of values (multi-value select expressions). +type ExperimentExecutionVariableAO_Value struct { + union json.RawMessage +} + +// ExperimentExecutionsRequestAO Filters are defined in the body of the request. +// +// Example: {"endedFrom":"2024-05-17T00:00:00Z","endedTo":"2024-06-24T00:00:00Z","environments":["Global"],"experimentKeys":["ADM-9"],"page":0,"requestedFrom":"2024-05-17T00:00:00Z","requestedTo":"2024-06-24T00:00:00Z","services":["shopping-cart"],"states":["errored","canceled"],"teamKeys":["GITHUB"],"teamKeysExclude":["ADM"]} +type ExperimentExecutionsRequestAO struct { + // CreatedFrom Filter results by range of created date + // + // Example: 2021-01-01T00:00:00Z + CreatedFrom *time.Time `json:"createdFrom,omitempty"` + + // CreatedTo Filter results by range of created date + // + // Example: 2021-01-01T00:00:00Z + CreatedTo *time.Time `json:"createdTo,omitempty"` + + // EndedFrom Filter results by range of ended date + // + // Example: 2021-01-01T00:00:00Z + EndedFrom *time.Time `json:"endedFrom,omitempty"` + + // EndedTo Filter results by range of ended date + // + // Example: 2021-01-01T00:00:00Z + EndedTo *time.Time `json:"endedTo,omitempty"` + + // Environments Filter results by one or more environments + // + // Example: ["Global"] + Environments *[]string `json:"environments,omitempty"` + + // ExperimentKeys Filter results by one or more experiment-keys + // + // Example: ["ADM-9"] + ExperimentKeys *[]string `json:"experimentKeys,omitempty"` + + // Name Filter results by name and/or key of the experiment + // + // Example: Outage + Name *string `json:"name,omitempty"` + Page *int32 `json:"page,omitempty"` + + // RequestedFrom Filter results by range of requested date + // + // Example: 2021-01-01T00:00:00Z + RequestedFrom *time.Time `json:"requestedFrom,omitempty"` + + // RequestedTo Filter results by range of requested date + // + // Example: 2021-01-01T00:00:00Z + RequestedTo *time.Time `json:"requestedTo,omitempty"` + + // Services Filter results by one or more service names that should be included in the result + // + // Example: ["shopping-cart"] + Services *[]string `json:"services,omitempty"` + Size *int32 `json:"size,omitempty"` + + // States Filter results by one or more states. Possible values: [CREATED, PREPARED, RUNNING, FAILED, CANCELED, COMPLETED, ERRORED] + // + // Example: ["CREATED"] + States *[]string `json:"states,omitempty"` + + // TeamKeys Filter results by one or more team-keys that should be included in the result + // + // Example: ["ADM"] + TeamKeys *[]string `json:"teamKeys,omitempty"` + + // TeamKeysExclude Filter results by one or more team-keys that should be excluded in the result + // + // Example: ["ADM"] + TeamKeysExclude *[]string `json:"teamKeysExclude,omitempty"` +} + +// ExperimentLaneAO A single lane of an experiment design. This lane can contain multiple steps that are executed sequentially +// +// Example: {"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]} +type ExperimentLaneAO struct { + // Steps A list of steps that are executed sequentially in this lane. + // + // Example: [{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}] + Steps []BaseExperimentStepAO `json:"steps"` +} + +// ExperimentReportFilterAO Filter for experiment report data, optionally scoped to specific teams and environments. +type ExperimentReportFilterAO struct { + // EnvironmentIds Restrict results to the given environment IDs. If not provided, all environments are included. + EnvironmentIds *[]openapi_types.UUID `json:"environmentIds,omitempty"` + + // From Start date of the report range (inclusive). + // + // Example: 2026-01-01 + From openapi_types.Date `json:"from"` + + // Rollup The time bucket granularity for report aggregation. + // + // Example: MONTHLY + Rollup *ExperimentReportFilterAORollup `json:"rollup,omitempty"` + + // TeamIds Restrict results to the given team IDs. If not provided, all teams are included. + TeamIds *[]openapi_types.UUID `json:"teamIds,omitempty"` + + // To End date of the report range (inclusive). + // + // Example: 2026-03-01 + To openapi_types.Date `json:"to"` +} + +// ExperimentReportFilterAORollup The time bucket granularity for report aggregation. +// +// Example: MONTHLY +type ExperimentReportFilterAORollup string + +// ExperimentRiskAO The risk for a single experiment linked to a service +// +// Example: {"experimentKey":"ADM-8","risk":42} +type ExperimentRiskAO struct { + // ExperimentKey The experiment key + ExperimentKey *string `json:"experimentKey,omitempty"` + + // Risk The calculated risk score for this experiment (0-100) + Risk *int32 `json:"risk,omitempty"` +} + +// ExperimentScheduleAO A schedule for an experiment. +// +// Example: {"allowParallel":true,"cron":"30 * * * * ? *","editedBy":{"email":"manuel@example.org","name":"Manuel","pictureUrl":"https://.../picture.png","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"},"enabled":true,"experimentKey":"ADM-8","id":"01951394-727f-76a0-8675-c7519ebd0ff5","lastUpdated":"2025-02-17T11:04:10.623486Z","nextExecution":"2025-02-20T05:54:30Z","timezone":"Europe/Berlin","variables":{}} +type ExperimentScheduleAO struct { + // AllowParallel Should the experiment run if another experiment is running? Default is true. + // + // Example: true + AllowParallel *bool `json:"allowParallel,omitempty"` + + // Cron Cron expression for the experiment schedule. Can't be used in combination with `startAt`. + // + // Example: 0 15 10 ? * * + Cron *string `json:"cron,omitempty"` + + // EditedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + EditedBy UserSummaryAO `json:"editedBy"` + + // Enabled If `false`, the schedule is deactivated and no experiment will be executed. Default is true. + // + // Example: false + Enabled *bool `json:"enabled,omitempty"` + + // ExperimentKey The experiment that should be scheduled. + // + // Example: ADM-123 + ExperimentKey string `json:"experimentKey"` + Id string `json:"id"` + LastUpdated time.Time `json:"lastUpdated"` + NextExecution *time.Time `json:"nextExecution,omitempty"` + + // StartAt Start date for a single execution. Can't be used in combination with `cron`. + StartAt *time.Time `json:"startAt,omitempty"` + + // Timezone Optional timezone for a experiment schedule. Can only be used with `cron`. + // + // Example: Europe/Berlin + Timezone *string `json:"timezone,omitempty"` + + // Variables Variables that will be used when the experiment will be executed. The variables will override existing environment or experiment variables. Each value is either a constant string, an array of constant strings, or a select expression object. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal"} + Variables *map[string]VariableExpressionAO `json:"variables,omitempty"` +} + +// ExperimentStepActionAO A single step in a lane executing always exactly one action. +type ExperimentStepActionAO struct { + // ActionType The specific action that is used in this step + // + // Example: com.steadybit.extension_host.stress-cpu + ActionType string `json:"actionType"` + CustomLabel *string `json:"customLabel,omitempty"` + + // IgnoreFailure Ignore any errors and failures of this single step and continue the execution of an experiment run + // + // Example: false + IgnoreFailure *bool `json:"ignoreFailure,omitempty"` + + // MetricChecks Optional metric checks used to define success or failure of this step + MetricChecks *[]MetricCheckAO `json:"metricChecks,omitempty"` + + // MetricQueries Optional metric queries used of this step to filter e.g. monitoring data + MetricQueries *[]MetricQueryAO `json:"metricQueries,omitempty"` + + // Parameters Configuration parameters of this step that are saved during experiment design and evaluated at execution time. + // + // Example: {"duration":"30s"} + Parameters *map[string]interface{} `json:"parameters,omitempty"` + + // Radius Specifying the targets and random blast radius of the available targets + // + // Example: {"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"targetType":"com.steadybit.extension_container.container"} + Radius *ExperimentStepRadiusAO `json:"radius,omitempty"` + Type string `json:"type"` + union json.RawMessage +} + +// ExperimentStepRadiusAO Specifying the targets and random blast radius of the available targets +// +// Example: {"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"targetType":"com.steadybit.extension_container.container"} +type ExperimentStepRadiusAO struct { + Advanced *[]AdvancedRadiusAO `json:"advanced,omitempty"` + Maximum *int32 `json:"maximum,omitempty"` + Percentage *int32 `json:"percentage,omitempty"` + + // Predicate Query defining the overall superset of targets being effected + // + // Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] + Predicate *TargetPredicateAO `json:"predicate,omitempty"` + + // Query Query defining the overall superset of targets being effected + // + // Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] + Query *TargetPredicateAO `json:"query,omitempty"` + TargetType *string `json:"targetType,omitempty"` +} + +// ExperimentStepServiceValidationAO A step in a lane executing the defined validations of a service +type ExperimentStepServiceValidationAO struct { + CustomLabel *string `json:"customLabel,omitempty"` + + // IgnoreFailure Ignore any errors and failures of this single step and continue the execution of an experiment run + // + // Example: false + IgnoreFailure *bool `json:"ignoreFailure,omitempty"` + + // MetricChecks Optional metric checks used to define success or failure of this step + MetricChecks *[]MetricCheckAO `json:"metricChecks,omitempty"` + + // MetricQueries Optional metric queries used of this step to filter e.g. monitoring data + MetricQueries *[]MetricQueryAO `json:"metricQueries,omitempty"` + + // Parameters Configuration parameters of this step that are saved during experiment design and evaluated at execution time. + // + // Example: {"duration":"30s"} + Parameters *map[string]interface{} `json:"parameters,omitempty"` + + // ServiceId The name of the service to validate. + // + // Example: 1a04288d-6c85-4ba6-80d7-24ded64dd009 + ServiceId string `json:"serviceId"` + Type string `json:"type"` + union json.RawMessage +} + +// ExperimentStepWaitAO A single step in a lane waiting for a specified duration. +type ExperimentStepWaitAO = BaseExperimentStepAO + +// ExperimentSummariesAO List of experiments. +// +// Example: {"experiments":[{"key":"ADM-1","name":"Shop survives unavailability of hot-deals products"},{"key":"SHOP-2","name":"Network latency of Message Broker doesn't interfere with Online shop"}]} +type ExperimentSummariesAO struct { + // Experiments List of experiment summaries + // + // Example: [{"key":"ADM-1","name":"Shop survives unavailability of hot-deals products"},{"key":"SHOP-2","name":"Network latency of Message Broker doesn't interfere with Online shop"}] + Experiments *[]ExperimentSummaryAO `json:"experiments,omitempty"` +} + +// ExperimentSummaryAO Summary of a single experiment. +// +// Example: {"key":"ADM-1","name":"Shop survives unavailability of hot-deals products"} +type ExperimentSummaryAO struct { + // Key Unique experiment key that identifies the experiment. Combination of `team key` and increasing number + // + // Example: ADM-2 + Key *string `json:"key,omitempty"` + + // Name Name of the experiment to easily identify the experiment + // + // Example: Shop survives unavailability of hot-deals products + Name *string `json:"name,omitempty"` +} + +// ExperimentTemplateAO Example: {"created":"2024-03-22T09:24:12.802961166Z","createdBy":{"name":"Daniel","pictureUrl":"https://s.gravatar.com/avatar/4f27f3856530f8f2e4ec050b1d594306?s=480\u0026r=pg\u0026d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fda.png","username":"2bd1c2d7-4051-46ad-9f05-315062edd85e"},"edited":"2024-03-22T09:24:12.802961166Z","editedBy":{"name":"Daniel","pictureUrl":"https://s.gravatar.com/avatar/4f27f3856530f8f2e4ec050b1d594306?s=480\u0026r=pg\u0026d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fda.png","username":"2bd1c2d7-4051-46ad-9f05-315062edd85e"},"hidden":false,"id":"6bea7aec-3572-44cf-9151-c6ada57d08ca","lanes":[{"steps":[{"actionType":"com.steadybit.extension_http.check.periodically","ignoreFailure":false,"parameters":{"connectTimeout":"5s","duration":"60s","followRedirects":false,"headers":[],"maxConcurrent":5,"method":"GET","readTimeout":"5s","requestsPerSecond":1,"statusCode":"200-299","successRate":"100","url":"[[HTTP_ENDPOINT]]"},"radius":{},"type":"action"}]},{"steps":[{"ignoreFailure":false,"parameters":{"duration":"10s"},"type":"wait"},{"actionType":"com.steadybit.extension_kubernetes.rollout-restart","ignoreFailure":false,"parameters":{"wait":false},"radius":{"percentage":50,"predicate":{"operator":"AND","predicates":[{"key":"k8s.cluster-name","operator":"EQUALS","values":["[[CLUSTER]]"]},{"key":"k8s.namespace","operator":"EQUALS","values":["[[NAMESPACE]]"]},{"key":"k8s.deployment","operator":"EQUALS","values":["[[DEPLOYMENT]]"]}]},"query":null,"targetType":"com.steadybit.extension_kubernetes.kubernetes-deployment"},"type":"action"},{"actionType":"com.steadybit.extension_kubernetes.rollout-status","ignoreFailure":false,"parameters":{"duration":"10m"},"radius":{"percentage":50,"predicate":{"operator":"AND","predicates":[{"key":"k8s.cluster-name","operator":"EQUALS","values":["[[CLUSTER]]"]},{"key":"k8s.namespace","operator":"EQUALS","values":["[[NAMESPACE]]"]},{"key":"k8s.deployment","operator":"EQUALS","values":["[[DEPLOYMENT]]"]}]},"query":null,"targetType":"com.steadybit.extension_kubernetes.kubernetes-deployment"},"type":"action"}]}],"placeholders":[{"description":"Which HTTP Endpoint should be checked during experiment execution?","key":"HTTP_ENDPOINT","name":"HTTP Endpoint"},{"description":"Which Kubernetes deployment do you want to restart?","key":"DEPLOYMENT","name":"Kubernetes Deployment"},{"description":"In which Kubernetes cluster is the deployment deployed to?","key":"CLUSTER","name":"Kubernetes Cluster"},{"description":"In which Kubernetes namespace is the deployment deployed to?","key":"NAMESPACE","name":"Kubernetes Namespace"}],"properties":{"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"},"propertiesMetadata":[{"editableInExecution":false,"key":"EXAMPLE_CUSTOM_PROPERTY","required":true}],"tags":["Kubernetes"],"templateDescription":"Test if a given HTTP Endpoint remains funcitonal if a Kubernetes deployment is restarted.","templateTitle":"HTTP Endpoint remains functional during Kubernetes Rollout Restart","version":0} +type ExperimentTemplateAO struct { + // Created Timestamp when the experiment template was created + // + // Example: 2023-01-01T09:00:00Z + Created time.Time `json:"created"` + + // CreatedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + CreatedBy UserSummaryAO `json:"createdBy"` + + // Edited Timestamp when the experiment template was edited the last time + // + // Example: 2023-01-01T09:00:00Z + Edited time.Time `json:"edited"` + + // EditedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + EditedBy UserSummaryAO `json:"editedBy"` + + // ExperimentName Name of the experiment created by this template. If omitted, the name needs to be added when the template is used. + // + // Example: Shop survives unavailability of database + ExperimentName *string `json:"experimentName,omitempty"` + + // Hidden Should the experiment template be hidden + // + // Example: false + Hidden *bool `json:"hidden,omitempty"` + + // Hypothesis The hypothesis that is validated by the experiment + // + // Example: System is able to survive a latency in the network of 1500ms + Hypothesis *string `json:"hypothesis,omitempty"` + Id *openapi_types.UUID `json:"id,omitempty"` + + // Lanes The lanes (steps executed in parallel) in the experiment template. Each lane consists of multiple steps that are executed sequential per lane. + // + // Example: [{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}] + Lanes []ExperimentLaneAO `json:"lanes"` + + // Placeholders A list of placeholders used in this experiment template. + Placeholders *[]ExperimentTemplatePlaceholderAO `json:"placeholders,omitempty"` + + // Properties The properties of the experiment + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // PropertiesMetadata Metadata for properties used in this template. + // + // Example: [{"editableInExecution":false,"key":"EXAMPLE_CUSTOM_PROPERTY","required":true}] + PropertiesMetadata *[]PropertyMetadataAO `json:"propertiesMetadata,omitempty"` + + // Tags A list of tags for this experiment template. (Up to 5) + Tags *[]string `json:"tags,omitempty"` + + // TemplateDescription A brief description what the template is doing. + TemplateDescription string `json:"templateDescription"` + + // TemplateTitle The title of the template + // + // Example: Shop survives unavailability of database + TemplateTitle string `json:"templateTitle"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// ExperimentTemplatePlaceholderAO A list of placeholders used in this experiment template. +type ExperimentTemplatePlaceholderAO struct { + Description string `json:"description"` + Key string `json:"key"` + Name string `json:"name"` +} + +// ExperimentTemplatePlaceholderValueAO List of template placeholder values +type ExperimentTemplatePlaceholderValueAO struct { + // Key The key of a template placeholder + // + // Example: cluster-name + Key string `json:"key"` + + // Value The value of a template placeholder, can be a string, a number a boolean or an object with a given structure like `[{"key": "CLUSTER","value": "demo-cluster"}]` + // + // Example: prod-cluster-1 + Value interface{} `json:"value"` +} + +// ExperimentTemplateSummariesAO List of experiment template summaries. +// +// Example: {"templates":[{"id":"f5990c81-6427-4144-8304-eda765a3f852","templateTitle":"xxx"},{"id":"d7e65100-1d20-4980-be87-c351704910b8","templateTitle":"yyy"}]} +type ExperimentTemplateSummariesAO struct { + // Templates List of experiment template summaries. + // + // Example: [{"id":"f5990c81-6427-4144-8304-eda765a3f852","templateTitle":"xxx"},{"key":"d7e65100-1d20-4980-be87-c351704910b8","templateTitle":"yyy"}] + Templates *[]ExperimentTemplateSummaryAO `json:"templates,omitempty"` +} + +// ExperimentTemplateSummaryAO Summary of a single experiment template. +// +// Example: {"id":"e50deab2-2636-4a5b-ad5a-6cf904ed56c4","templateTitle":"HTTP Endpoint remains functional during Kubernetes Rollout Restart"} +type ExperimentTemplateSummaryAO struct { + // Hidden Is the template currently hidden? + // + // Example: true + Hidden *bool `json:"hidden,omitempty"` + + // Id Unique id that identifies the experiment template. + // + // Example: b9f4aae2-9b03-4ad3-a1a7-654774cc04eb + Id *openapi_types.UUID `json:"id,omitempty"` + + // TemplateDescription Description of the experiment template + // + // Example: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + TemplateDescription *string `json:"templateDescription,omitempty"` + + // TemplateTitle Title of the experiment template + // + // Example: Shop survives unavailability of hot-deals products + TemplateTitle *string `json:"templateTitle,omitempty"` +} + +// ExperimentTemplatesImportAO Request to import templates from a hub. +type ExperimentTemplatesImportAO struct { + // HubId ID of the hub to import templates from. + HubId openapi_types.UUID `json:"hubId"` + + // TemplateIds Optional list of Template IDs to import into the platform. If not provided, all templates from the hub will be imported. + TemplateIds *[]openapi_types.UUID `json:"templateIds,omitempty"` +} + +// GetAdviceApiRequestAO Request for getting and filtering pieces of advice. +// +// Example: {"environmentName":"Global","offset":0,"query":"k8s.cluster-name=sandbox-demo and k8s.namespace=steadybit-demo"} +type GetAdviceApiRequestAO struct { + // EnvironmentName The name of the environment of which the pieces of Advice should be listed + // + // Example: Global + EnvironmentName string `json:"environmentName"` + + // Offset The offset to be returned in the paginated result set + // + // Example: 20 + Offset *int64 `json:"offset,omitempty"` + + // Query An additional optional filter to search only for pieces of advice, whose target is included in the filter + // + // Example: k8s.cluster-name=prod-demo and k8s.namespace=steadybit-demo + Query *string `json:"query,omitempty"` +} + +// GetLicenseSummaryAO defines model for GetLicenseSummaryAO. +type GetLicenseSummaryAO struct { + Expires *time.Time `json:"expires,omitempty"` + Features *[]LicenseFeatureSummaryAO `json:"features,omitempty"` + License *LicenseSummaryAO `json:"license,omitempty"` + TenantKey *string `json:"tenantKey,omitempty"` +} + +// HintAO An informational or warning hint displayed to the user. +type HintAO struct { + // Content Content of the hint as markdown text. + Content string `json:"content"` + + // Type Type of the hint. + // + // Example: INFO + Type string `json:"type"` +} + +// HubAO defines model for HubAO. +type HubAO struct { + // Created Timestamp when the hub was connected + // + // Example: 2023-01-01T09:00:00Z + Created time.Time `json:"created"` + + // CreatedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + CreatedBy UserSummaryAO `json:"createdBy"` + + // Edited Timestamp when the hub was edited the last time + // + // Example: 2023-01-01T09:00:00Z + Edited time.Time `json:"edited"` + + // EditedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + EditedBy UserSummaryAO `json:"editedBy"` + + // HubLink Website address of the the hub + // + // Example: https://hub.steadybit.com/ + HubLink *string `json:"hubLink,omitempty"` + + // HubName Name of the hub + HubName string `json:"hubName"` + Id openapi_types.UUID `json:"id"` + + // LastRepositoryChange Timestamp of last change as defined in the repository content + LastRepositoryChange *time.Time `json:"lastRepositoryChange,omitempty"` + + // LastSync Timestamp of last hub synchronization + LastSync *time.Time `json:"lastSync,omitempty"` + + // RepositoryUrl HTTP address of the the hub's repository + // + // Example: https://github.com/steadybit/reliability-hub-db + RepositoryUrl string `json:"repositoryUrl"` + + // SyncError Last synchronization error description, if an error occurred + SyncError *string `json:"syncError,omitempty"` + + // Templates List of templates published in the hub. + Templates []ExperimentTemplateSummaryAO `json:"templates"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version int32 `json:"version"` +} + +// HubConnectionCheckAO defines model for HubConnectionCheckAO. +type HubConnectionCheckAO struct { + // RepositoryUrl HTTP address of the the hub's repository + // + // Example: https://github.com/steadybit/reliability-hub-db + RepositoryUrl string `json:"repositoryUrl"` +} + +// HubConnectionCheckResponseAO defines model for HubConnectionCheckResponseAO. +type HubConnectionCheckResponseAO struct { + Error *string `json:"error,omitempty"` +} + +// HubSummariesAO List of all hubs. Fetch a single hub by `id` to get more information. +// +// Example: {"hubs":[{"hubName":"Hub Name","id":"1b267dc1-5f4e-4803-894d-92ecd9b83413"}]} +type HubSummariesAO struct { + Hubs *[]HubSummaryAO `json:"hubs,omitempty"` +} + +// HubSummaryAO Summary containing the most important hub details. +// +// Example: {"hubName":"Hub Name","id":"1b267dc1-5f4e-4803-894d-92ecd9b83413"} +type HubSummaryAO struct { + // HubName Name of the hub + HubName string `json:"hubName"` + Id openapi_types.UUID `json:"id"` +} + +// InvitationAO Request to invite users to the platform +// +// Example: {"email":"aa@bb.com","role":"USER","teamKey":"TST"} +type InvitationAO struct { + Email openapi_types.Email `json:"email"` + Role *InvitationAORole `json:"role,omitempty"` + TeamKey *string `json:"teamKey,omitempty"` +} + +// InvitationAORole defines model for InvitationAO.Role. +type InvitationAORole string + +// InviteUsersRequestAO Request to invite users to the platform +// +// Example: {"invitations":[{"email":"aa@bb.com","role":"ADMIN","teamKey":"ADM"}]} +type InviteUsersRequestAO struct { + Invitations []InvitationAO `json:"invitations"` +} + +// KillswitchAO Determines the current status of the kill switch (emergency stop). If the kill switch is active, all experiments are cancelled immediately and no new experiments can be executed. +// +// Example: {"active":"true","engaged":"2023-01-01T09:00:00Z","engagedBy":"71ab0180-8abc-4d30-8acb-6aa024e3065f"} +type KillswitchAO struct { + // Active Determines whether the kill switch is currently active / engaged. + // + // Example: true + Active *bool `json:"active,omitempty"` + + // Engaged Time at which the kill switch was activated / engaged + // + // Example: 2023-01-01T09:00:00Z + Engaged *time.Time `json:"engaged,omitempty"` + + // EngagedBy Username (internal identifier of Steadybit) of the user that has activated / engaged the kill switch + // + // Example: 13av2737-b318-4048-a79d-4789d645bc31 + EngagedBy *string `json:"engagedBy,omitempty"` + + // EngagedByDetails The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + EngagedByDetails *UserSummaryAO `json:"engagedByDetails,omitempty"` +} + +// LandscapeViewAO A saved view of the explorer landscape. +type LandscapeViewAO struct { + // ColorBy The color-by dimension: the attribute the targets are colored by, together with its advanced configuration. + ColorBy *LandscapeViewColorByAO `json:"colorBy,omitempty"` + + // Description Description of the saved view. + // + // Example: All shop workloads grouped by namespace. + Description *string `json:"description,omitempty"` + + // Environment Name of the environment the view is scoped to. + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // FilterQuery Explorer filter query narrowing the targets shown on the landscape. + // + // Example: k8s.namespace="shop" + FilterQuery *string `json:"filterQuery,omitempty"` + + // GroupBy Ordered list of group-by dimensions the targets are grouped by, each with its own advanced configuration. + GroupBy *[]LandscapeViewGroupByAO `json:"groupBy,omitempty"` + + // Id Unique identifier of the saved view. + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id *openapi_types.UUID `json:"id,omitempty"` + + // LastUpdated Point in time the saved view was last updated. + // + // Example: 2026-07-23T10:15:30Z + LastUpdated *time.Time `json:"lastUpdated,omitempty"` + + // Name Title of the saved view. + // + // Example: Kubernetes by namespace + Name *string `json:"name,omitempty"` + + // ShowAdvice Whether reliability advice is shown on the landscape. + // + // Example: false + ShowAdvice *bool `json:"showAdvice,omitempty"` + + // SizeBy Attribute key the size of a target is derived from. + // + // Example: k8s.container.cpu.limit + SizeBy *string `json:"sizeBy,omitempty"` + + // Team Key of the team the saved view belongs to. + // + // Example: ADM + Team string `json:"team"` +} + +// LandscapeViewColorByAO The color-by dimension: the attribute the targets are colored by, together with its advanced configuration. +type LandscapeViewColorByAO struct { + // Attribute Attribute key the color of a target is derived from. + // + // Example: k8s.namespace + Attribute *string `json:"attribute,omitempty"` + + // Mappings Buckets that map specific attribute values to named color groups. + Mappings *[]LandscapeViewMappedGroupingAO `json:"mappings,omitempty"` + + // Overrides Explicit color overrides keyed by the color-by attribute value. Each color must be one of the predefined landscape colors. + // + // Example: {"shop":"GREEN"} + Overrides *map[string]LandscapeViewColorByAOOverrides `json:"overrides,omitempty"` +} + +// LandscapeViewColorByAOOverrides Explicit color overrides keyed by the color-by attribute value. Each color must be one of the predefined landscape colors. +// +// Example: {"shop":"GREEN"} +type LandscapeViewColorByAOOverrides string + +// LandscapeViewGroupByAO A single group-by dimension: the attribute the targets are grouped by, together with its advanced configuration. +type LandscapeViewGroupByAO struct { + // Attribute Attribute key the targets are grouped by. + // + // Example: k8s.namespace + Attribute string `json:"attribute"` + + // Mappings Buckets that map specific attribute values to named groups. + Mappings *[]LandscapeViewMappedGroupingAO `json:"mappings,omitempty"` + + // MergeUnmappedToUnknown Whether attribute values that are not mapped to any bucket are merged into the unknown group. + // + // Example: false + MergeUnmappedToUnknown *bool `json:"mergeUnmappedToUnknown,omitempty"` + + // ShowUnknown Whether an additional group collecting all targets without a value for this dimension is shown. + // + // Example: true + ShowUnknown *bool `json:"showUnknown,omitempty"` +} + +// LandscapeViewMappedGroupingAO A bucket that groups multiple attribute values under a single named group. +type LandscapeViewMappedGroupingAO struct { + // AttributeValues Attribute values that are collected into this bucket. + // + // Example: ["prod","production"] + AttributeValues *[]string `json:"attributeValues,omitempty"` + + // GroupName Display name of the bucket. + // + // Example: Production + GroupName *string `json:"groupName,omitempty"` + + // Id Unique identifier of the bucket. Optional on create/update — a new identifier is generated when omitted; supply the returned identifier to keep a bucket stable across updates. + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id *openapi_types.UUID `json:"id,omitempty"` +} + +// LicenseFeatureSummaryAO defines model for LicenseFeatureSummaryAO. +type LicenseFeatureSummaryAO struct { + HardLimit *int32 `json:"hardLimit,omitempty"` + Name string `json:"name"` + SoftLimit *int32 `json:"softLimit,omitempty"` + Type LicenseFeatureSummaryAOType `json:"type"` + Usage *int32 `json:"usage,omitempty"` +} + +// LicenseFeatureSummaryAOType defines model for LicenseFeatureSummaryAO.Type. +type LicenseFeatureSummaryAOType string + +// LicenseSummaryAO defines model for LicenseSummaryAO. +type LicenseSummaryAO struct { + Id *int64 `json:"id,omitempty"` + LicenseType *LicenseSummaryAOLicenseType `json:"licenseType,omitempty"` + OrderNumber *string `json:"orderNumber,omitempty"` + ValidFrom openapi_types.Date `json:"validFrom"` + ValidTo openapi_types.Date `json:"validTo"` +} + +// LicenseSummaryAOLicenseType defines model for LicenseSummaryAO.LicenseType. +type LicenseSummaryAOLicenseType string + +// LinkCustomExperimentRequestAO defines model for LinkCustomExperimentRequestAO. +type LinkCustomExperimentRequestAO struct { + // Category The category to which the experiment should be linked + // + // Example: Scalability + Category string `json:"category"` + + // ExperimentKey The experiment that should be linked + // + // Example: ADM-18 + ExperimentKey string `json:"experimentKey"` +} + +// ListResponseCustomWebhookAO defines model for ListResponseCustomWebhookAO. +type ListResponseCustomWebhookAO struct { + Content *[]CustomWebhookAO `json:"content,omitempty"` +} + +// ListResponseLandscapeViewAO defines model for ListResponseLandscapeViewAO. +type ListResponseLandscapeViewAO struct { + Content *[]LandscapeViewAO `json:"content,omitempty"` +} + +// ListResponsePreflightActionIntegrationAO defines model for ListResponsePreflightActionIntegrationAO. +type ListResponsePreflightActionIntegrationAO struct { + Content *[]PreflightActionIntegrationAO `json:"content,omitempty"` +} + +// ListResponsePreflightWebhookAO defines model for ListResponsePreflightWebhookAO. +type ListResponsePreflightWebhookAO struct { + Content *[]PreflightWebhookAO `json:"content,omitempty"` +} + +// ListResponseSlackWebhookAO defines model for ListResponseSlackWebhookAO. +type ListResponseSlackWebhookAO struct { + Content *[]SlackWebhookAO `json:"content,omitempty"` +} + +// MemberAO Member of a team. +// +// Example: {"email":"aa@bb.com","name":"Max Mustermann","role":"OWNER","username":"13av2737-b318-4048-a79d-4789d645bc31"} +type MemberAO struct { + Email *string `json:"email,omitempty"` + + // ManagedBy How a team or team membership is managed + // + // Example: MANUAL + ManagedBy *MemberAOManagedBy `json:"managedBy,omitempty"` + + // Name Name of the user + // + // Example: Jane Doe + Name *string `json:"name,omitempty"` + PictureUrl *string `json:"pictureUrl,omitempty"` + + // Role Role of the team member + // + // Example: OWNER + Role MemberAORole `json:"role"` + + // Username Username of the user, internal identifier of Steadybit + // + // Example: 13av2737-b318-4048-a79d-4789d645bc31 + Username string `json:"username"` +} + +// MemberAOManagedBy How a team or team membership is managed +// +// Example: MANUAL +type MemberAOManagedBy string + +// MemberAORole Role of the team member +// +// Example: OWNER +type MemberAORole string + +// MemberUpdateAO Add a Member to a Team by providing the username or the email. +// +// Example: {"members":[{"email":"example@example.com","role":"MEMBER","username":"example"}]} +type MemberUpdateAO struct { + // Email E-mail of the user, unique within Steadybit + // + // Example: example@example.com + Email *string `json:"email,omitempty"` + + // Role Role of the team member + // + // Example: OWNER + Role MemberUpdateAORole `json:"role"` + + // Username Username of the user, internal identifier of Steadybit + // + // Example: 13av2737-b318-4048-a79d-4789d645bc31 + Username *string `json:"username,omitempty"` +} + +// MemberUpdateAORole Role of the team member +// +// Example: OWNER +type MemberUpdateAORole string + +// MetricCheckAO Optional metric checks used to define success or failure of this step +type MetricCheckAO struct { + A MetricValueAO `json:"a"` + B *MetricCheckAO_B `json:"b,omitempty"` + Condition MetricCheckAOCondition `json:"condition"` + Id openapi_types.UUID `json:"id"` +} + +// MetricCheckAO_B defines model for MetricCheckAO.B. +type MetricCheckAO_B struct { + union json.RawMessage +} + +// MetricCheckAOCondition defines model for MetricCheckAO.Condition. +type MetricCheckAOCondition string + +// MetricQueryAO Optional metric queries used of this step to filter e.g. monitoring data +type MetricQueryAO struct { + Id openapi_types.UUID `json:"id"` + Label string `json:"label"` + Parameters map[string]interface{} `json:"parameters"` +} + +// MetricValueAO defines model for MetricValueAO. +type MetricValueAO struct { + Metric *map[string]string `json:"metric,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` +} + +// NegationTargetPredicateAO defines model for NegationTargetPredicateAO. +type NegationTargetPredicateAO struct { + // Not Query defining the overall superset of targets being effected + // + // Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] + Not *TargetPredicateAO `json:"not,omitempty"` + union json.RawMessage +} + +// OptionAO defines model for OptionAO. +type OptionAO struct { + Attribute *string `json:"attribute,omitempty"` + Label *string `json:"label,omitempty"` + Value *string `json:"value,omitempty"` +} + +// PageRequestAO defines model for PageRequestAO. +type PageRequestAO struct { + Page *int32 `json:"page,omitempty"` + Size *int32 `json:"size,omitempty"` +} + +// PagedResponseAOAccessTokensPageItemAO defines model for PagedResponseAOAccessTokensPageItemAO. +type PagedResponseAOAccessTokensPageItemAO struct { + Items *[]AccessTokensPageItemAO `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PagedResponseAOAccessTokensPageItemV2AO defines model for PagedResponseAOAccessTokensPageItemV2AO. +type PagedResponseAOAccessTokensPageItemV2AO struct { + Items *[]AccessTokensPageItemV2AO `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PagedResponseAOExperimentExecutionPageItemAO defines model for PagedResponseAOExperimentExecutionPageItemAO. +type PagedResponseAOExperimentExecutionPageItemAO struct { + Items *[]ExperimentExecutionPageItemAO `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PagedResponseAOPropertyAssociationAO defines model for PagedResponseAOPropertyAssociationAO. +type PagedResponseAOPropertyAssociationAO struct { + Items *[]PropertyAssociationAO `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PagedResponseAOPropertyDefinitionAO defines model for PagedResponseAOPropertyDefinitionAO. +type PagedResponseAOPropertyDefinitionAO struct { + Items *[]PropertyDefinitionAO `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PagedResponseAOServiceExperimentAO defines model for PagedResponseAOServiceExperimentAO. +type PagedResponseAOServiceExperimentAO struct { + Items *[]ServiceExperimentAO `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PagedResponseAOServiceProfileAO defines model for PagedResponseAOServiceProfileAO. +type PagedResponseAOServiceProfileAO struct { + Items *[]ServiceProfileAO `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PagedResponseAOServiceSummaryAO defines model for PagedResponseAOServiceSummaryAO. +type PagedResponseAOServiceSummaryAO struct { + Items *[]ServiceSummaryAO `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PagedResponseAOString defines model for PagedResponseAOString. +type PagedResponseAOString struct { + Items *[]string `json:"items,omitempty"` + + // NextPage Next page to query for next page of runs or null if there are none. + // + // Example: 4 + NextPage *int32 `json:"nextPage,omitempty"` + + // TotalItems Total amount of runs matching your query + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// ParameterAO Parameters that describe how to fetch metrics for this action. +type ParameterAO struct { + AcceptedFileTypes *[]string `json:"acceptedFileTypes,omitempty"` + Advanced *bool `json:"advanced,omitempty"` + DefaultValue *string `json:"defaultValue,omitempty"` + Deprecated *bool `json:"deprecated,omitempty"` + DeprecationMessage *string `json:"deprecationMessage,omitempty"` + Description *string `json:"description,omitempty"` + DurationUnits *[]string `json:"durationUnits,omitempty"` + + // Hint An informational or warning hint displayed to the user. + Hint *HintAO `json:"hint,omitempty"` + Label string `json:"label"` + Max *int32 `json:"max,omitempty"` + Min *int32 `json:"min,omitempty"` + Name string `json:"name"` + Options *[]OptionAO `json:"options,omitempty"` + OptionsOnly *bool `json:"optionsOnly,omitempty"` + Order *int32 `json:"order,omitempty"` + Required *bool `json:"required,omitempty"` + Type string `json:"type"` +} + +// PatchExperimentScheduleAO A partial update for an experiment schedule. Only non-null fields will be updated. +// +// Example: {"enabled":false} +type PatchExperimentScheduleAO struct { + // AllowParallel Should the experiment run if another experiment is running? + // + // Example: true + AllowParallel *bool `json:"allowParallel,omitempty"` + + // Cron Cron expression for the experiment schedule. If provided, startAt will be cleared. + // + // Example: 0 15 10 ? * * + Cron *string `json:"cron,omitempty"` + + // Enabled If `false`, the schedule is deactivated and no experiment will be executed. + // + // Example: false + Enabled *bool `json:"enabled,omitempty"` + + // StartAt Start date for a single execution. If provided, cron will be cleared. + StartAt *time.Time `json:"startAt,omitempty"` + + // Timezone Optional timezone for a experiment schedule. Can only be used with `cron`. + // + // Example: Europe/Berlin + Timezone *string `json:"timezone,omitempty"` + + // Variables Variables that will be used when the experiment will be executed. The variables will override existing environment or experiment variables. Each value is either a constant string, an array of constant strings, or a select expression object. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal"} + Variables *map[string]VariableExpressionAO `json:"variables,omitempty"` +} + +// PreflightActionAO A pageable list of pieces of preflight actions. +// +// Example: { +// id: "com.steadybit.extension_preflight.preflightaction.check-configuration", +// version: "0.1.0", +// description: "Check if a execution of a specific experiment in a environment is permitted.", +// targetAttributeIncludes: [ +// "k8s.cluster-name", +// "k8s.namespace" +// ] +// } +type PreflightActionAO struct { + // Description The description of the preflight action + // + // Example: Check if a execution of a specific experiment in a environment is permitted. + Description *string `json:"description,omitempty"` + + // Id The unique identifier of the preflight action + // + // Example: com.steadybit.extension_preflight.preflightaction.check-configuration + Id string `json:"id"` + + // Name The name of the preflight action + // + // Example: Check configuration + Name string `json:"name"` + + // TargetAttributeIncludes The list of target attributes that are included in the preflight action + // + // Example: ["k8s.cluster-name","k8s.namespace"] + TargetAttributeIncludes *[]string `json:"targetAttributeIncludes,omitempty"` + + // Version The version of the preflight action + // + // Example: 0.1.0 + Version string `json:"version"` +} + +// PreflightActionIntegrationAO Example: {"id":"ac456d58-8fb2-4df4-86d8-ca81d7562739","inflightInterval":"10s","inflightTimeout":"5s","name":"Example Preflight Action Integration","preflightActionId":"com.example.preflightaction.MyPreflightAction","scope":"TEAM","team":"ADM","version":1} +type PreflightActionIntegrationAO struct { + // Id The id of the webhook + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id openapi_types.UUID `json:"id"` + InflightInterval *string `json:"inflightInterval,omitempty"` + InflightTimeout *string `json:"inflightTimeout,omitempty"` + + // Name The name of the preflightActionIntegration + // + // Example: Preflight PreflightActionIntegration + Name string `json:"name"` + + // PreflightActionId The preflight action id which is used to identify the preflight action + // + // Example: com.example.preflightaction.MyPreflightAction + PreflightActionId string `json:"preflightActionId"` + + // Scope The scope of the preflight action integration + // + // Example: TEAM + Scope PreflightActionIntegrationAOScope `json:"scope"` + + // Team The key of the team if the scope is `TEAM` + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version int32 `json:"version"` +} + +// PreflightActionIntegrationAOScope The scope of the preflight action integration +// +// Example: TEAM +type PreflightActionIntegrationAOScope string + +// PreflightActionIntegrationUpsertAO Example: {"id":"ac456d58-8fb2-4df4-86d8-ca81d7562739","inflightInterval":"10s","inflightTimeout":"5s","name":"Example Preflight Action Integration","preflightActionId":"com.example.preflightaction.MyPreflightAction","scope":"TEAM","team":"ADM","version":1} +type PreflightActionIntegrationUpsertAO struct { + // Id The id of the webhook or null if a new webhook should be created. + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id *openapi_types.UUID `json:"id,omitempty"` + InflightInterval *string `json:"inflightInterval,omitempty"` + InflightTimeout *string `json:"inflightTimeout,omitempty"` + + // Name The name of the preflightActionIntegration + // + // Example: Preflight PreflightActionIntegration + Name string `json:"name"` + + // PreflightActionId The preflight action id which is used to identify the preflight action + // + // Example: com.example.preflightaction.MyPreflightAction + PreflightActionId string `json:"preflightActionId"` + + // Scope The scope of the preflight action integration + // + // Example: TEAM + Scope PreflightActionIntegrationUpsertAOScope `json:"scope"` + + // Team The key of the team if the scope is `TEAM` + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// PreflightActionIntegrationUpsertAOScope The scope of the preflight action integration +// +// Example: TEAM +type PreflightActionIntegrationUpsertAOScope string + +// PreflightActionSummaryAO A pageable list of pieces of perflight actions. +// +// Example: {"items":[],"nextOffset":3,"totalItems":108} +type PreflightActionSummaryAO struct { + Items *[]PreflightActionAO `json:"items,omitempty"` + + // NextOffset Next queryable offset to query for next batch of preflight actions + // + // Example: 21 + NextOffset *int32 `json:"nextOffset,omitempty"` + + // TotalItems Total amount of preflight actions + // + // Example: 241 + TotalItems *int64 `json:"totalItems,omitempty"` +} + +// PreflightWebhookAO defines model for PreflightWebhookAO. +type PreflightWebhookAO struct { + // Events The events that you want to intercept. Currently only `experiment.execution.preflight` is supported. + // + // + // Example: ["experiment.execution.preflight"] + Events []string `json:"events"` + + // Headers Additional headers to include in the webhook request. + // + // Example: {"X-Another-Header":"AnotherValue","X-Custom-Header":"CustomValue"} + Headers *map[string]string `json:"headers,omitempty"` + + // Id The id of the webhook + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id openapi_types.UUID `json:"id"` + + // Name The name of the webhook + // + // Example: Preflight Webhook + Name string `json:"name"` + + // Scope The scope of the webhook / integration + // + // Example: TEAM + Scope PreflightWebhookAOScope `json:"scope"` + + // Secret If a secret is provided a signature of the body is computed using `HMAC SHA-256` and sent as `X-SB-Signature` http header. You can use this header to verify the message. + // + // Example: secret123!! + Secret *string `json:"secret,omitempty"` + + // TargetAttributeIncludes The body size can get very large as we include all target attributes for each target of your experiments. When having experiments with many targets, it might be useful to filter the attributes to only include the ones you are interested in. You can use the wildcard character '*' to match all attributes or a comma-separated-list of attribute-names. If the field is empty, no attributes will be included. + // + // Example: ["k8s.cluster-name","k8s.deployment"] + TargetAttributeIncludes []string `json:"targetAttributeIncludes"` + + // Team The key of the team if the scope is `TEAM` + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Url The URL of the webhook + // + // Example: https://example.com/webhook + Url string `json:"url"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version int32 `json:"version"` +} + +// PreflightWebhookAOScope The scope of the webhook / integration +// +// Example: TEAM +type PreflightWebhookAOScope string + +// PreflightWebhookUpsertAO defines model for PreflightWebhookUpsertAO. +type PreflightWebhookUpsertAO struct { + // Events The events that you want to intercept. Currently only `experiment.execution.preflight` is supported. + // + // + // Example: ["experiment.execution.preflight"] + Events []string `json:"events"` + + // Headers Additional headers to include in the webhook request. + // + // Example: {"X-Another-Header":"AnotherValue","X-Custom-Header":"CustomValue"} + Headers *map[string]string `json:"headers,omitempty"` + + // Id The id of the webhook or null if a new webhook should be created. + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id *openapi_types.UUID `json:"id,omitempty"` + + // Name The name of the webhook + // + // Example: Preflight Webhook + Name string `json:"name"` + + // Scope The scope of the webhook / integration + // + // Example: TEAM + Scope PreflightWebhookUpsertAOScope `json:"scope"` + + // Secret If a secret is provided a signature of the body is computed using `HMAC SHA-256` and sent as `X-SB-Signature` http header. You can use this header to verify the message. + // + // Example: secret123!! + Secret *string `json:"secret,omitempty"` + + // TargetAttributeIncludes The body size can get very large as we include all target attributes for each target of your experiments. When having experiments with many targets, it might be useful to filter the attributes to only include the ones you are interested in. You can use the wildcard character '*' to match all attributes or a comma-separated-list of attribute-names. If the field is empty, no attributes will be included. + // + // Example: ["k8s.cluster-name","k8s.deployment"] + TargetAttributeIncludes []string `json:"targetAttributeIncludes"` + + // Team The key of the team if the scope is `TEAM` + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Url The URL of the webhook + // + // Example: https://example.com/webhook + Url string `json:"url"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// PreflightWebhookUpsertAOScope The scope of the webhook / integration +// +// Example: TEAM +type PreflightWebhookUpsertAOScope string + +// PrincipalAL The principal that has performed the logged event +// +// Example: {"email":"example@example.com","name":"Jane Doe","principalType":"USER","role":"ADMIN","username":"1ava2afg-xju33-4c6a-9451-2854584c15be"} +type PrincipalAL struct { + // PrincipalType The principal type that has performed the logged event + // + // Example: USER + PrincipalType PrincipalALPrincipalType `json:"principalType"` + union json.RawMessage +} + +// PrincipalALPrincipalType The principal type that has performed the logged event +// +// Example: USER +type PrincipalALPrincipalType string + +// PropertyAssociationAO A property association. +// +// Example: {"editableInExecution":true,"id":"2v1av42-e525-4c00-a13a-1ac32d170724","key":"RESULT_COLOR","required":true,"version":1} +type PropertyAssociationAO struct { + // AssociationType Always defined to either `EXPERIMENT` for experiment design or run related associations or `SERVICE` for service-associations. Only for the former, an `experimentKey` can be defined and only for the latter, a `serviceId` can be defined + // + // Example: EXPERIMENT + AssociationType *PropertyAssociationAOAssociationType `json:"associationType,omitempty"` + + // EditableInExecution Is the property editable in the execution view. Only used when `associationType` is set to `EXPERIMENT`. + // + // Example: true + EditableInExecution *bool `json:"editableInExecution,omitempty"` + + // ExperimentKey The key of the associated experiment. When `associationType` is set to `EXPERIMENT` and `experimentKey` is `null`, it is associated to ALL experiment designs. Can't be changed during updates. + // + // Example: EXP-1 + ExperimentKey *string `json:"experimentKey,omitempty"` + + // Id Id of the Property-Association. + Id openapi_types.UUID `json:"id"` + + // Key The key of the property definition + // + // Example: RESULT_COLOR + Key string `json:"key"` + + // Required Is the value required? + // + // Example: true + Required *bool `json:"required,omitempty"` + + // ServiceId The serviceId of the associated service. When `associationType` is set to `SERVICE` and `serviceId` is `null`, it is associated to ALL services. Can't be changed during updates. + // + // Example: 3308b47d-5c1f-4f08-a25b-a18fc10f8a56 + ServiceId *openapi_types.UUID `json:"serviceId,omitempty"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version int32 `json:"version"` +} + +// PropertyAssociationAOAssociationType Always defined to either `EXPERIMENT` for experiment design or run related associations or `SERVICE` for service-associations. Only for the former, an `experimentKey` can be defined and only for the latter, a `serviceId` can be defined +// +// Example: EXPERIMENT +type PropertyAssociationAOAssociationType string + +// PropertyDefinitionAO Definition of a property definition that can be associated. +// +// Example: {"dataType":"ENUM","description":"How would you describe the result of your experiment, thinking in beautiful colors?","enumValues":["RED","GREEN","BLUE"],"key":"RESULT_COLOR","label":"Result Color","version":1} +type PropertyDefinitionAO struct { + // DataType The data type of the property + // + // Example: STRING + DataType PropertyDefinitionAODataType `json:"dataType"` + + // Description The text describing the property. + // + // Example: How would you describe the result of your experiment, thinking in beautiful colors? + Description *string `json:"description,omitempty"` + + // EnumValues Valid values if the dataType `ENUM` is used + // + // Example: ["RED","GREEN","BLUE"] + EnumValues *[]string `json:"enumValues,omitempty"` + + // Key The unique key of the property definition + // + // Example: RESULT_COLOR + Key string `json:"key"` + + // Label The label shown in the ui for this property + // + // Example: Result color + Label string `json:"label"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version int32 `json:"version"` +} + +// PropertyDefinitionAODataType The data type of the property +// +// Example: STRING +type PropertyDefinitionAODataType string + +// PropertyMetadataAO Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the template already exists and should be updated or newly inserted. +// +// Example: {"editableInExecution":false,"key":"EXAMPLE_CUSTOM_PROPERTY","required":true} +type PropertyMetadataAO struct { + EditableInExecution *bool `json:"editableInExecution,omitempty"` + Key string `json:"key"` + Required *bool `json:"required,omitempty"` +} + +// QueryLanguagePredicateAO defines model for QueryLanguagePredicateAO. +type QueryLanguagePredicateAO struct { + Query string `json:"query"` + union json.RawMessage +} + +// RecreateAccessTokenRequestV2AO defines model for RecreateAccessTokenRequestV2AO. +type RecreateAccessTokenRequestV2AO struct { + // ExpiresAt New expiration date for the recreated token. + // + // Example: 2027-01-01T00:00:00Z + ExpiresAt *time.Time `json:"expiresAt,omitempty"` +} + +// ReportFilterAO Filter for time-series report data. +type ReportFilterAO struct { + // From Start date of the report range (inclusive). + // + // Example: 2026-01-01 + From openapi_types.Date `json:"from"` + + // Rollup The time bucket granularity for report aggregation. + // + // Example: MONTHLY + Rollup *ReportFilterAORollup `json:"rollup,omitempty"` + + // To End date of the report range (inclusive). + // + // Example: 2026-03-01 + To openapi_types.Date `json:"to"` +} + +// ReportFilterAORollup The time bucket granularity for report aggregation. +// +// Example: MONTHLY +type ReportFilterAORollup string + +// ScalarValueAO defines model for ScalarValueAO. +type ScalarValueAO struct { + Type *string `json:"type,omitempty"` + Value *float64 `json:"value,omitempty"` +} + +// SelectExpressionAO defines model for SelectExpressionAO. +type SelectExpressionAO struct { + Attribute string `json:"attribute"` + Count *int32 `json:"count,omitempty"` + Filter *string `json:"filter,omitempty"` + Mode SelectExpressionAOMode `json:"mode"` + Percent *int32 `json:"percent,omitempty"` + + // Scope Evaluation scope of a dynamic value, **required for service variables** and rejected for all other variables (environment, experiment, schedule, execution overrides). `service` samples from the service's own targets (its environment narrowed by the service's target query); `environment` samples from the whole environment the service lives in. There is no default — a service variable must state its scope explicitly. + // + // Example: service + Scope *SelectExpressionAOScope `json:"scope,omitempty"` + TargetType string `json:"targetType"` + Type *string `json:"type,omitempty"` +} + +// SelectExpressionAOMode defines model for SelectExpressionAO.Mode. +type SelectExpressionAOMode string + +// SelectExpressionAOScope Evaluation scope of a dynamic value, **required for service variables** and rejected for all other variables (environment, experiment, schedule, execution overrides). `service` samples from the service's own targets (its environment narrowed by the service's target query); `environment` samples from the whole environment the service lives in. There is no default — a service variable must state its scope explicitly. +// +// Example: service +type SelectExpressionAOScope string + +// ServiceAO Example: {"created":"2023-01-01T09:00:00Z","createdBy":{"name":"Manuel","pictureUrl":"https://.../picture.png","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"},"edited":"2023-01-01T09:00:00Z","editedBy":{"name":"Manuel","pictureUrl":"https://.../picture.png","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"},"environment":"Global","id":"2v1av42-e525-4c00-a13a-1ac32d170724","logoColor":"blue","logoId":"service-router","name":"shopping-service","query":"aws.account=\"123\" OR aws.account=\"456\"","serviceProfile":"Steadybit Starter","team":"ADM","validations":[{"actionType":"com.steadybit.extension_http.check.periodically","parameters":{"method":"GET","url":"https://my-service/health"},"type":"action"}],"variables":{"httpEndpoint":"http://prod.shop.products.internal","targets":{"attribute":"k8s.deployment","count":1,"filter":"k8s.namespace=\"shop\"","mode":"fixed","targetType":"com.steadybit.extension_kubernetes.kubernetes-deployment","type":"select"}},"version":1} +type ServiceAO struct { + // Created Timestamp when the service was created + // + // Example: 2023-01-01T09:00:00Z + Created time.Time `json:"created"` + + // CreatedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + CreatedBy UserSummaryAO `json:"createdBy"` + + // Edited Timestamp when the service was edited the last time + // + // Example: 2023-01-01T09:00:00Z + Edited time.Time `json:"edited"` + + // EditedBy The user that canceled the experiment execution, only present if the execution was canceled + // + // Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} + EditedBy UserSummaryAO `json:"editedBy"` + + // Environment The name of the environment to be used + // + // Example: Global + Environment string `json:"environment"` + + // Id The unique id of the service + Id *openapi_types.UUID `json:"id,omitempty"` + + // LogoColor Color scheme of the logo used to identify the service in the Platform UI + // + // Example: orangeLight + LogoColor string `json:"logoColor"` + + // LogoId Identifier of the logo used to identify the service in the Platform UI + // + // Example: service-router + LogoId string `json:"logoId"` + + // Name The name of the service + // + // Example: calculator-service + Name string `json:"name"` + + // Properties The properties of the service + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this service!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // Query Query-Language predicate, specifies the targets belonging to this Service + // + // Example: aws.account="123" OR aws.account="456" + Query string `json:"query"` + + // ServiceProfile Name of the service profile that should be used for this service + // + // Example: Steadybit provided + ServiceProfile string `json:"serviceProfile"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` + + // Validations List of validations to be executed against the service + Validations []ExperimentStepActionAO `json:"validations"` + + // Variables Variables owned by the service. Each value is either a constant string, an array of constant strings, or a select expression object. A select-expression value **must set `scope`** (`service` or `environment`) — the request is rejected otherwise. On `POST /api/services` (upsert): omitting this field leaves existing variables untouched, an empty object removes all of them. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal","targetServices":["gateway","hot-deals","fashion-bestseller"]} + Variables *map[string]VariableExpressionAO `json:"variables,omitempty"` + + // Version Version for optimistic locking + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// ServiceExperimentAO defines model for ServiceExperimentAO. +type ServiceExperimentAO struct { + // AssociationType The type of the association + // + // Example: PROVIDED + AssociationType ServiceExperimentAOAssociationType `json:"associationType"` + + // Category The category of the experiment. + // + // Example: Scalability + Category string `json:"category"` + + // ExperimentKey The key of the experiment. If type is PROVIDED and the experiment has not been created, the experimentKey will be null + // + // Example: EX-754 + ExperimentKey *string `json:"experimentKey,omitempty"` + + // TemplateId The id of the experiment template if type is PROVIDED and the experiment has not been created. + // + // Example: 6bea7aec-3572-44cf-9151-c6ada57d08ca + TemplateId *openapi_types.UUID `json:"templateId,omitempty"` +} + +// ServiceExperimentAOAssociationType The type of the association +// +// Example: PROVIDED +type ServiceExperimentAOAssociationType string + +// ServiceProfileAO A service profile that groups experiment templates by category +type ServiceProfileAO struct { + // Created Timestamp when the profile was created + // + // Example: 2023-01-01T09:00:00Z + Created time.Time `json:"created"` + + // CreatedBy Username of the user that created the profile + // + // Example: admin@example.com + CreatedBy string `json:"createdBy"` + + // DefaultProfile Whether this is the default profile. + // + // Example: true + DefaultProfile bool `json:"defaultProfile"` + + // Edited Timestamp when the profile was last edited + // + // Example: 2023-01-01T09:00:00Z + Edited time.Time `json:"edited"` + + // EditedBy Username of the user that last edited the profile + // + // Example: admin@example.com + EditedBy string `json:"editedBy"` + + // Id The unique id of the profile + Id openapi_types.UUID `json:"id"` + + // Name The name of the profile + // + // Example: Default Resilience Tests + Name string `json:"name"` + + // Origin Origin of a service profile + // + // Example: CUSTOM + Origin ServiceProfileAOOrigin `json:"origin"` + + // Templates Template entries in this profile + Templates []ServiceProfileCategoryAO `json:"templates"` + + // Version Version for optimistic locking + // + // Example: 1 + Version int32 `json:"version"` +} + +// ServiceProfileAOOrigin Origin of a service profile +// +// Example: CUSTOM +type ServiceProfileAOOrigin string + +// ServiceProfileCategoryAO Template entries in this profile +type ServiceProfileCategoryAO struct { + // Category The category name + // + // Example: Scalability + Category *string `json:"category,omitempty"` + + // TemplateIds The template IDs in this category + // + // Example: ["6bea7aec-3572-44cf-9151-c6ada57d08ca","6bea7aec-3572-44cf-9151-c6ada57d08cb"] + TemplateIds *[]openapi_types.UUID `json:"templateIds,omitempty"` +} + +// ServiceRiskAO The risk for a given service. +// +// Example: {"categoryRisks":{"Dependency":{"advice":50,"experiment":50,"total":50},"Redundancy":{"advice":51,"experiment":100,"total":91},"Scalability":{"advice":58,"experiment":100,"total":92}},"experimentRisks":[{"experimentKey":"ADM-8","risk":100},{"experimentKey":"ADM-16","risk":100}],"lastCalculated":"2026-03-31T10:13:38.902372Z","risk":78} +type ServiceRiskAO struct { + // CategoryRisks Risk per category + CategoryRisks *map[string]CategoryRiskAO `json:"categoryRisks,omitempty"` + + // ExperimentRisks Risk per experiment + ExperimentRisks *[]ExperimentRiskAO `json:"experimentRisks,omitempty"` + + // LastCalculated Timestamp of the last risk calculation + LastCalculated *time.Time `json:"lastCalculated,omitempty"` + + // Risk The overall risk for the service + Risk *int32 `json:"risk,omitempty"` +} + +// ServiceRiskReportFilterAO Filter for service risk report data, optionally scoped to specific teams, environments, and services. +type ServiceRiskReportFilterAO struct { + // CategoryKeys Restrict results to services whose categoryRisks map contains any of the given category keys. + CategoryKeys *[]string `json:"categoryKeys,omitempty"` + + // EnvironmentIds Restrict results to the given environment IDs. If not provided, all environments are included. + EnvironmentIds *[]openapi_types.UUID `json:"environmentIds,omitempty"` + + // From Start date of the report range (inclusive). + // + // Example: 2026-01-01 + From openapi_types.Date `json:"from"` + + // Rollup The time bucket granularity for report aggregation. + // + // Example: MONTHLY + Rollup *ServiceRiskReportFilterAORollup `json:"rollup,omitempty"` + + // ServiceIds Restrict results to the given service IDs. If not provided, all services are included. + ServiceIds *[]openapi_types.UUID `json:"serviceIds,omitempty"` + + // ServiceProperties Filter on the service's enum/enum-list custom property values. Map of property key and values; values are OR within a key, AND across keys. + ServiceProperties *map[string][]string `json:"serviceProperties,omitempty"` + + // TeamIds Restrict results to the given team IDs. If not provided, all teams are included. + TeamIds *[]openapi_types.UUID `json:"teamIds,omitempty"` + + // To End date of the report range (inclusive). + // + // Example: 2026-03-01 + To openapi_types.Date `json:"to"` +} + +// ServiceRiskReportFilterAORollup The time bucket granularity for report aggregation. +// +// Example: MONTHLY +type ServiceRiskReportFilterAORollup string + +// ServiceSummaryAO defines model for ServiceSummaryAO. +type ServiceSummaryAO struct { + // Environment The name of the environment to be used + // + // Example: Global + Environment string `json:"environment"` + + // Id The unique id of the service + Id *openapi_types.UUID `json:"id,omitempty"` + + // LogoColor Color scheme of the logo used to identify the service in the Platform UI + // + // Example: blue + LogoColor *string `json:"logoColor,omitempty"` + + // LogoId Identifier of the logo used to identify the service in the Platform UI + // + // Example: 1 + LogoId *string `json:"logoId,omitempty"` + + // Name The name of the service + // + // Example: calculator-service + Name string `json:"name"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` +} + +// SlackWebhookAO defines model for SlackWebhookAO. +type SlackWebhookAO struct { + // Channel The name of the slack channel + // + // Example: #steadybit-notifications + Channel string `json:"channel"` + + // Events The events that are being sent or a list containing a single `*` if all supported event types should be used. + // + // Supported Events: + // - "experiment.execution.requested" + // - "experiment.execution.created" + // - "experiment.execution.preflight" + // - "experiment.execution.completed" + // - "experiment.execution.failed" + // - "experiment.execution.errored" + // - "experiment.execution.canceled" + // - "experiment.execution.step-started" + // - "experiment.execution.step-completed" + // - "experiment.execution.step-failed" + // - "experiment.execution.step-errored" + // - "experiment.execution.step-canceled" + // - "experiment.execution.step-skipped" + // - "killswitch.engaged" + // - "killswitch.disengaged" + // + // + // Example: ["experiment.execution.created","experiment.execution.completed"] + Events *[]string `json:"events,omitempty"` + + // IconUrl The icon URL of the slack channel, defaults to the Steadybit logo. + // + // Example: https://platform.steadybit.com/assets/logo512.png + IconUrl *string `json:"iconUrl,omitempty"` + + // Id The id of the webhook + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id openapi_types.UUID `json:"id"` + + // Name The name of the integration + // + // Example: Slack ACME corporation. + Name string `json:"name"` + + // Scope The scope of the webhook / integration + // + // Example: TEAM + Scope SlackWebhookAOScope `json:"scope"` + + // Team The key of the team if the scope is `TEAM` + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Url The Slack webhook url + // + // Example: https://hooks.slack.com/services/ + Url string `json:"url"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version int32 `json:"version"` +} + +// SlackWebhookAOScope The scope of the webhook / integration +// +// Example: TEAM +type SlackWebhookAOScope string + +// SlackWebhookUpsertAO defines model for SlackWebhookUpsertAO. +type SlackWebhookUpsertAO struct { + // Channel The name of the slack channel + // + // Example: #steadybit-notifications + Channel string `json:"channel"` + + // Events The events that are being sent or a list containing a single `*` if all supported event types should be used. + // + // Supported Events: + // - "experiment.execution.requested" + // - "experiment.execution.created" + // - "experiment.execution.preflight" + // - "experiment.execution.completed" + // - "experiment.execution.failed" + // - "experiment.execution.errored" + // - "experiment.execution.canceled" + // - "experiment.execution.step-started" + // - "experiment.execution.step-completed" + // - "experiment.execution.step-failed" + // - "experiment.execution.step-errored" + // - "experiment.execution.step-canceled" + // - "experiment.execution.step-skipped" + // - "killswitch.engaged" + // - "killswitch.disengaged" + // + // + // Example: ["experiment.execution.created","experiment.execution.completed"] + Events *[]string `json:"events,omitempty"` + + // IconUrl The icon URL of the slack channel, defaults to the Steadybit logo. + // + // Example: https://platform.steadybit.com/assets/logo512.png + IconUrl *string `json:"iconUrl,omitempty"` + + // Id The id of the webhook or null if a new webhook should be created. + // + // Example: ac456d58-8fb2-4df4-86d8-ca81d7562739 + Id *openapi_types.UUID `json:"id,omitempty"` + + // Name The name of the integration + // + // Example: Slack ACME corporation. + Name string `json:"name"` + + // Scope The scope of the webhook / integration + // + // Example: TEAM + Scope SlackWebhookUpsertAOScope `json:"scope"` + + // Team The key of the team if the scope is `TEAM` + // + // Example: ADM + Team *string `json:"team,omitempty"` + + // Url The Slack webhook url + // + // Example: https://hooks.slack.com/services/ + Url string `json:"url"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// SlackWebhookUpsertAOScope The scope of the webhook / integration +// +// Example: TEAM +type SlackWebhookUpsertAOScope string + +// TargetAO defines model for TargetAO. +type TargetAO struct { + // AgentId The ID of the agent this target belongs to + // + // Example: 019504aa-0f60-781b-862d-9763010d5948 + AgentId *string `json:"agentId,omitempty"` + + // Attributes The attributes for this target. A key may be associated multiple time to a single target. + // + // Example: [{"key":"container.port","value":"51152:2376"},{"key":"container.engine","value":"docker"}] + Attributes *[]AttributeAO `json:"attributes,omitempty"` + + // Name The name of the target + // + // Example: fashion-bestseller + Name *string `json:"name,omitempty"` + + // Type The name of the target + // + // Example: com.steadybit.extension_kubernetes.kubernetes-deployment + Type *string `json:"type,omitempty"` + + // Version The version of the target, will be increased by every update via the agent. + // + // Example: 0 + Version *int64 `json:"version,omitempty"` +} + +// TargetAdviceAO A pageable list of pieces of advice. +// +// Example: {"advice":{"label":"Limit CPU Resources","status":"Validation needed","summary":"You already took action and configured a CPU limit. Validate your configuration via an experiment.","tags":["kubernetes","limit","cpu"],"type":"com.steadybit.extension_kubernetes.advice.k8s-cpu-limit"},"target":{"label":"gateway","reference":"prod-demo/steadybit-demo/gateway","type":"com.steadybit.extension_kubernetes.kubernetes-deployment"},"url":"https://platform.steadybit.com/permalink/advice/eyAiZW52..."} +type TargetAdviceAO struct { + // Advice Summary of a single advice for the referenced target + // + // Example: {"label":"Limit CPU Resources","status":"Validation needed","summary":"You already took action and configured a CPU limit. Validate your configuration via an experiment.","tags":["kubernetes","limit","cpu"],"type":"com.steadybit.extension_kubernetes.advice.k8s-cpu-limit"} + Advice *TargetAdviceAdvicePartAO `json:"advice,omitempty"` + + // Target A reference to identify the target for a given advice + // + // Example: {"label":"gateway","reference":"prod-demo/steadybit-demo/gateway","type":"com.steadybit.extension_kubernetes.kubernetes-deployment"} + Target *TargetAdviceTargetPartAO `json:"target,omitempty"` + + // Url URL to see all details to this advice for this target + // + // Example: https://platform.steadybit.com/permalink/advice/eyAiZW52... + Url *string `json:"url,omitempty"` +} + +// TargetAdviceAdvicePartAO Summary of a single advice for the referenced target +// +// Example: {"label":"Limit CPU Resources","status":"Validation needed","summary":"You already took action and configured a CPU limit. Validate your configuration via an experiment.","tags":["kubernetes","limit","cpu"],"type":"com.steadybit.extension_kubernetes.advice.k8s-cpu-limit"} +type TargetAdviceAdvicePartAO struct { + // Label Human readable label of the advice + // + // Example: Limit CPU Resources + Label *string `json:"label,omitempty"` + + // Status Current status of the advice applied to the referenced target. One of 'Action Needed', 'Validation needed', 'Implemented'. + // + // Example: Validation needed + Status *string `json:"status,omitempty"` + + // Summary Summary of the advice to describe the current status and next step. + // + // Example: You already took action and configured a CPU limit. Validate your configuration via an experiment + Summary string `json:"summary"` + + // Tags Tags associated to the advice definition + // + // Example: ["AWS","Kubernetes"] + Tags *[]string `json:"tags,omitempty"` + + // Type Identifier of the advice definition that is applied to the target + // + // Example: com.steadybit.extension_kubernetes.advice.k8s-cpu-limit + Type *string `json:"type,omitempty"` +} + +// TargetAdviceTargetPartAO A reference to identify the target for a given advice +// +// Example: {"label":"gateway","reference":"prod-demo/steadybit-demo/gateway","type":"com.steadybit.extension_kubernetes.kubernetes-deployment"} +type TargetAdviceTargetPartAO struct { + // Label Human readable identifier to be displayed for the target + // + // Example: gateway + Label string `json:"label"` + + // Reference Unique stable identifier of the target for this target type + // + // Example: prod-demo/steadybit-demo/gateway + Reference string `json:"reference"` + + // Type Target type of the referenced target + // + // Example: com.steadybit.extension_kubernetes.kubernetes-deployment + Type string `json:"type"` +} + +// TargetAgentIdPredicateAO defines model for TargetAgentIdPredicateAO. +type TargetAgentIdPredicateAO struct { + AgentId openapi_types.UUID `json:"agentId"` + union json.RawMessage +} + +// TargetAttributeKeyCountPredicateAO defines model for TargetAttributeKeyCountPredicateAO. +type TargetAttributeKeyCountPredicateAO struct { + Key string `json:"key"` + Value string `json:"value"` + ValueCountOperator TargetAttributeKeyCountPredicateAOValueCountOperator `json:"valueCountOperator"` + union json.RawMessage +} + +// TargetAttributeKeyCountPredicateAOValueCountOperator defines model for TargetAttributeKeyCountPredicateAO.ValueCountOperator. +type TargetAttributeKeyCountPredicateAOValueCountOperator string + +// TargetAttributeKeyPredicateAO defines model for TargetAttributeKeyPredicateAO. +type TargetAttributeKeyPredicateAO struct { + Key string `json:"key"` + Operator string `json:"operator"` + union json.RawMessage +} + +// TargetAttributeKeyPresencePredicateAO defines model for TargetAttributeKeyPresencePredicateAO. +type TargetAttributeKeyPresencePredicateAO struct { + Key string `json:"key"` + PresenceOperator TargetAttributeKeyPresencePredicateAOPresenceOperator `json:"presenceOperator"` + union json.RawMessage +} + +// TargetAttributeKeyPresencePredicateAOPresenceOperator defines model for TargetAttributeKeyPresencePredicateAO.PresenceOperator. +type TargetAttributeKeyPresencePredicateAOPresenceOperator string + +// TargetAttributeKeyValuePredicateAO defines model for TargetAttributeKeyValuePredicateAO. +type TargetAttributeKeyValuePredicateAO struct { + Key string `json:"key"` + Operator string `json:"operator"` + Values []string `json:"values"` + union json.RawMessage +} + +// TargetExecutionAO A target that is expected to be effected by this action. +// +// Example: {"attributes":[{"key":"container.port","value":"51152:2376"},{"key":"container.engine","value":"docker"},{"key":"container.host/name","value":"docker-desktop/minikube"},{"key":"container.host","value":"docker-desktop"}],"name":"docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea","state":"COMPLETED","type":"com.steadybit.extension_container.container"} +type TargetExecutionAO struct { + // AgentHostname The agent that processed this target-action command and forwarded it to the proper extension instance. + // + // Example: prod-demo/steadybit-agent/steadybit-agent-0 + AgentHostname *string `json:"agentHostname,omitempty"` + + // Artifacts List of artifact identifiers that are associated with this target execution + // + // Example: ["jmeter-report.zip","system-metrics.csv"] + Artifacts *[]string `json:"artifacts,omitempty"` + + // Attributes A set of attributes that have been discovered for this target. A key may be associated multiple time to a single target. + // + // Example: [{"key":"container.port","value":"51152:2376"},{"key":"container.engine","value":"docker"}] + Attributes *[]AttributeAO `json:"attributes,omitempty"` + + // Id Unique identifier of this target execution + // + // Example: 019aba52-558d-7d44-b793-92839f3c3152 + Id *openapi_types.UUID `json:"id,omitempty"` + + // Name Identifier of the target that is expected to be effected + // + // Example: docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea + Name *string `json:"name,omitempty"` + + // Reason Reason on a per target-level why the experiment failed or errored. If this step didn't failed or errored (`state != 'FAILED' and state != 'ERRORED') the reason is `null`. + // + // Example: Failed to start Stop Container (com.steadybit.extension_container.stop) + Reason *string `json:"reason,omitempty"` + + // ReasonDetails Optional additional reason details on a per target-level why the the experiment failed or errored. + // + // Example: Could not read state of target container: exit status 1 (time="2023-09-29T12:41:32Z" level=error msg="container does not exist" + ReasonDetails *string `json:"reasonDetails,omitempty"` + + // Source The source (i.e. call to the extension) that caused the step to error or fail. + // + // Example: POST http://11.20.86.255:9093/com.steadybit.extension_container.container_stop/prepare + Source *string `json:"source,omitempty"` + + // State State of this specific step on a per target-level. + // + // Example: COMPLETED + State *string `json:"state,omitempty"` + + // Summary A summary for a target execution + // + // Example: {"level":"INFO","text":"Hello world!"} + Summary *TargetExecutionSummaryAO `json:"summary,omitempty"` + + // Type Type of the target that is expected to be effected + // + // Example: container + Type *string `json:"type,omitempty"` +} + +// TargetExecutionSummaryAO A summary for a target execution +// +// Example: {"level":"INFO","text":"Hello world!"} +type TargetExecutionSummaryAO struct { + Level *string `json:"level,omitempty"` + Text *string `json:"text,omitempty"` +} + +// TargetNamePredicateAO defines model for TargetNamePredicateAO. +type TargetNamePredicateAO struct { + Name string `json:"name"` + union json.RawMessage +} + +// TargetPredicateAO Query defining the overall superset of targets being effected +// +// Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] +type TargetPredicateAO struct { + union json.RawMessage +} + +// TargetPredicateTemplateAO A predefined target predicate template that users can apply when configuring an action. +type TargetPredicateTemplateAO struct { + // Description Description of the template. + Description *string `json:"description,omitempty"` + + // Name Display name of the template. + Name *string `json:"name,omitempty"` + + // Template Query language template. + Template string `json:"template"` +} + +// TargetSelectorAO defines model for TargetSelectorAO. +type TargetSelectorAO struct { + TargetQuery *string `json:"targetQuery,omitempty"` + Type string `json:"type"` +} + +// TargetStatsRequest defines model for TargetStatsRequest. +type TargetStatsRequest struct { + // Predicate Query defining the overall superset of targets being effected + // + // Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] + Predicate *TargetPredicateAO `json:"predicate,omitempty"` + + // Query Alternative to `predicate`. If both `query` and `predicate` will be provided, `query` will override the `predicate`. + // + // Example: (aws.account="123" OR aws.account="456" + Query *string `json:"query,omitempty"` +} + +// TargetTypePredicateAO defines model for TargetTypePredicateAO. +type TargetTypePredicateAO struct { + Types []string `json:"types"` + union json.RawMessage +} + +// TeamAL The team in which the event was triggered +// +// Example: {"id":"a2167b29-e73b-4445-8468-4670a0b459b3","key":"ADMIN","name":"Administrators"} +type TeamAL struct { + Id openapi_types.UUID `json:"id"` + Key string `json:"key"` + Name string `json:"name"` +} + +// TeamAO A team that is uniquely identified via it's teamKey and has members, allowed environments and actions. +// +// Example: {"allowedActions":["com.steadybit.extension_host.host.stress-cpu"],"allowedEnvironments":["Global"],"id":"71ab0180-8abc-4d30-8acb-6aa024e3065f","key":"ADM","logoColor":"cyanDark","logoId":"1","members":[{"role":"OWNER","username":"auth0|11a9315afc84590069cd53b2"},{"role":"MEMBER","username":"auth0|13b1s51vg184590069cd51ab"},{"role":"MEMBER","username":"auth0|1va2g15afc84590069cd53c3"}],"name":"Administrators","version":1} +type TeamAO struct { + // AllowedActions Set of allowed actions that can be used in an experiment of this team + // + // Example: ["com.steadybit.extension_host.host.stress-cpu"] + AllowedActions []string `json:"allowedActions"` + + // AllowedEnvironments Set of allowed environments, identified via name + // + // Example: ["Global"] + AllowedEnvironments []string `json:"allowedEnvironments"` + + // Description An optional description of a team + Description *string `json:"description,omitempty"` + Id *openapi_types.UUID `json:"id,omitempty"` + + // Key Unique identifier of a team + // + // Example: ADM + Key string `json:"key"` + + // LogoColor Color scheme of the logo used to identify the team in the Platform UI + // + // Example: cyanDark + LogoColor *string `json:"logoColor,omitempty"` + + // LogoId Identifier of the logo used to identify the team in the Platform UI + // + // Example: 1 + LogoId *string `json:"logoId,omitempty"` + + // ManagedBy How a team or team membership is managed + // + // Example: MANUAL + ManagedBy *TeamAOManagedBy `json:"managedBy,omitempty"` + + // Members Members that are associated to this team + // + // Example: {"role":"OWNER","username":"auth0|11a9315afc84590069cd53b2"} + Members []MemberAO `json:"members"` + + // Name Name of a team + // + // Example: ADMIN + Name string `json:"name"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// TeamAOManagedBy How a team or team membership is managed +// +// Example: MANUAL +type TeamAOManagedBy string + +// TeamEnvironmentAO Environment assigned to a team. +// +// Example: {"name":"Global"} +type TeamEnvironmentAO struct { + // Name Name of the environment. + // + // Example: Global + Name string `json:"name"` +} + +// TeamEnvironmentsAO List of environments that are assigned to this team. +// +// Example: {"environments":[{"name":"Global"},{"name":"Shop Production"}]} +type TeamEnvironmentsAO struct { + // Environments Environments that are assigned to this team + // + // Example: [{"name":"Global"},{"name":"Shop Production"}] + Environments []TeamEnvironmentAO `json:"environments"` +} + +// TeamEnvironmentsUpdateAO Update request to change the environments of a specific team. +// +// Example: {"environments":[{"name":"Global"},{"name":"Shop Production"}]} +type TeamEnvironmentsUpdateAO struct { + // Environments Environments that should be updated to this team + // + // Example: [{"name":"Global"},{"name":"Shop Production"}] + Environments []TeamEnvironmentAO `json:"environments"` +} + +// TeamMembersAO List of members that are part of this team. +// +// Example: {"members":[{"role":"OWNER","username":"13av2737-b318-4048-a79d-4789d645bc31"},{"role":"MEMBER","username":"google-oauth2|931412422966030837225"}]} +type TeamMembersAO struct { + // Members Members that are associated to this team + // + // Example: [{"email":"jane.doe@steadybit.com","name":"jane doe","role":"OWNER","username":"13av2737-b318-4048-a79d-4789d645bc31"},{"email":"jane.smith@steadybit.com","name":"john smith","role":"MEMBER","username":"google-oauth2|931412422966030837225"}] + Members []MemberAO `json:"members"` +} + +// TeamMembersRemoveAO Team members that should be removed from a given team. You can specify the user to be removed via the internal identifier `usernames` or via the user's `emails`. If you specify both, both set of users will be removed. +// +// Example: {"emails":["jane.doe@example.com","javier.rodriguez@example.com"],"usernames":["13av2737-b318-4048-a79d-4789d645bc31"]} +type TeamMembersRemoveAO struct { + Emails *[]string `json:"emails,omitempty"` + Usernames *[]string `json:"usernames,omitempty"` +} + +// TeamMembersUpdateAO Update request to change the members of a specific team. Specify either username, being a Steadybit user id, or the email address of the user. +// +// Example: {"members":[{"email":"jane.doe@example.com","role":"OWNER"},{"email":"javier.rodriguez@example.com","role":"MEMBER"},{"role":"MEMBER","username":"auth0|1va2g15afc84590069cd53c3"}]} +type TeamMembersUpdateAO struct { + // Members Members that should be updated to this team + // + // Example: {"email":"jane.doe@example.com","role":"OWNER"} + Members []MemberUpdateAO `json:"members"` +} + +// TeamSummariesAO List of teams. +// +// Example: {"teams":[{"allowedActions":["com.steadybit.extension_host.host.stress-cpu"],"allowedEnvironments":["Global"],"id":"714b0180-8abc-4d30-8acb-6aa024e3065f","key":"ADM","logoColor":"cyanDark","logoId":"1","members":[{"role":"OWNER","username":"auth0|11a9315afc84590069cd53b2"}],"name":"Administrators","version":1}]} +type TeamSummariesAO struct { + Teams *[]TeamAO `json:"teams,omitempty"` +} + +// TenantAL The tenant in which the event was performed. Only relevant in case you are using multiple tenants of the Steadybit platform. +// +// Example: {"key":"Demo","name":"Demo Tenant"} +type TenantAL struct { + Key string `json:"key"` + Name string `json:"name"` +} + +// TimeSeriesAO A named time series with date-value pairs. +type TimeSeriesAO struct { + // Name Name of the series, corresponding to the group label. + Name *string `json:"name,omitempty"` + + // Values Date-value pairs, each serialized as [date, count]. + Values *[]TimeSeriesValueAO `json:"values,omitempty"` +} + +// TimeSeriesReportAO Time-series report data with metadata describing the query context. +// +// Example: {"from":"2026-01-01","groupBy":"NONE","rollup":"MONTHLY","series":[{"name":"users","values":[["2026-01-01",23],["2026-02-01",42],["2026-03-01",42]]}],"to":"2026-03-01"} +type TimeSeriesReportAO struct { + // From Start date of the requested report range (inclusive). + // + // Example: 2026-01-01 + From *openapi_types.Date `json:"from,omitempty"` + + // GroupBy The grouping dimension applied to the series. + GroupBy *TimeSeriesReportAOGroupBy `json:"groupBy,omitempty"` + + // Rollup The time bucket granularity for report aggregation. + Rollup *TimeSeriesReportAORollup `json:"rollup,omitempty"` + + // Series The time-series data, one entry per group. + Series *[]TimeSeriesAO `json:"series,omitempty"` + + // To End date of the requested report range (inclusive). + // + // Example: 2026-03-01 + To *openapi_types.Date `json:"to,omitempty"` +} + +// TimeSeriesReportAOGroupBy The grouping dimension applied to the series. +type TimeSeriesReportAOGroupBy string + +// TimeSeriesReportAORollup The time bucket granularity for report aggregation. +type TimeSeriesReportAORollup string + +// TimeSeriesValueAO A date-value pair serialized as a two-element array [date, count]. +type TimeSeriesValueAO struct { + // Date Start date of the time bucket. + // + // Example: 2026-01-01 + Date *openapi_types.Date `json:"date,omitempty"` + + // Value The count for this time bucket. + Value *int32 `json:"value,omitempty"` +} + +// UpdateExperimentAO Update the experiment with the given experiment design. +// +// Example: {"environment":"Global","lanes":[{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}],"name":"Blackhole Hot-deals","properties":{"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"},"team":"ADM"} +type UpdateExperimentAO struct { + // Environment The name of the environment to be used + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // ExperimentVariables Variables that will be used when the experiment will be executed. Experiment variables will override existing environment variables. Each value is either a constant string, an array of constant strings, or a select expression object (`{"type":"select",...}`). + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal","httpEndpointZones":{"attribute":"aws.zone","count":2,"mode":"fixed","targetType":"com.steadybit.extension_container.container","type":"select"},"targetServices":["gateway","hot-deals","fashion-bestseller"]} + ExperimentVariables *map[string]VariableExpressionAO `json:"experimentVariables,omitempty"` + + // ExternalId An optional external identifier used for create-or-update semantics. + // + // Example: 1234567 + ExternalId *string `json:"externalId,omitempty"` + + // ExternalReference An optional external reference. Will be removed and is replaced by tags. If used with experiment creation, the value will be added as a tag. + // + // Example: INCIDENT-4711 + // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set + ExternalReference *string `json:"externalReference,omitempty"` + + // Hypothesis The hypothesis that is validated by the experiment + // + // Example: System is able to survive a latency in the network of 1500ms + Hypothesis *string `json:"hypothesis,omitempty"` + + // Lanes The lanes (steps executed in parallel) in the experiment. Each lane consists of multiple steps that are executed sequential per lane. + // + // Example: [{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}] + Lanes []ExperimentLaneAO `json:"lanes"` + + // Name Name of the experiment to easily identify the experiment + // + // Example: Shop survives unavailability of hot-deals products + Name string `json:"name"` + + // Properties The properties of the experiment + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // SharedTeams Team keys with which the experiment is shared with + // + // Example: [OPS, SHOP] + SharedTeams *[]string `json:"sharedTeams,omitempty"` + + // Tags An optional set of tags you can use to search for. + // + // Example: ["myTag","myOtherTag"] + Tags *[]string `json:"tags,omitempty"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` +} + +// UpdateExperimentExecutionPropertiesAO Experiment execution data that should be used only for that specific experiment execution and will not update the experiment design. +// +// Example: {"properties":{"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment execution!"},"propertiesOrder":["EXAMPLE_CUSTOM_PROPERTY"],"propertiesVersion":1} +type UpdateExperimentExecutionPropertiesAO struct { + // Properties The properties of the experiment execution + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment execution!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // PropertiesOrder The order of the properties for this experiment execution. This may include global and experiment scoped assigned properties. + // + // Example: ["EXAMPLE_CUSTOM_PROPERTY"] + PropertiesOrder *[]string `json:"propertiesOrder,omitempty"` + + // PropertiesVersion Version for optimistic locking (optional in the API) + // + // Example: 1 + PropertiesVersion *int32 `json:"propertiesVersion,omitempty"` +} + +// UpdateExperimentFromTemplateAO Update an experiment based on an experiment template. +// +// Example: { +// "placeholders": [ +// { +// "key": "CLUSTER", +// "value": "demo-cluster" +// }, +// { +// "key": "BOOL", +// "value": true +// }, +// { +// "key": "NUMBER", +// "value": 15 +// }, +// { +// "key": "KEYVALUE", +// "value": [ +// { +// "key": "example-a", +// "value": "abc" +// }, +// { +// "key": "example-b", +// "value": "123" +// } +// ] +// }, +// { +// "key": "LIST", +// "value": [ +// "entry1", +// "entry2", +// "entry3" +// ] +// }, +// { +// "key": "FILE", +// "value": { +// "fileName": "example.txt", +// "data": "SGVsbG8gV29ybGQh" +// } +// } +// ], +// } +type UpdateExperimentFromTemplateAO struct { + // Placeholders List of template placeholder values + Placeholders *[]ExperimentTemplatePlaceholderValueAO `json:"placeholders,omitempty"` +} + +// UpsertEnvironmentAO Update or insert environment. +// +// Example: {"id":"2v1av42-e525-4c00-a13a-1ac32d170724","name":"Global","query":"aws.account=\"123\" OR aws.account=\"456\"","version":0} +type UpsertEnvironmentAO struct { + // Id Unique identifier of a environment + Id *openapi_types.UUID `json:"id,omitempty"` + + // Name Name of the environment. + // + // Example: Global + Name string `json:"name"` + + // Predicate Query defining the overall superset of targets being effected + // + // Example: [{"key":"container.host/name","operator":"EQUALS","values":["docker-desktop/minikube"]}] + Predicate *TargetPredicateAO `json:"predicate,omitempty"` + + // Query Alternative to `predicate`. If both `query` and `predicate` will be provided, `query` will override the `predicate`. + // + // Example: aws.account="123" OR aws.account="456" + Query *string `json:"query,omitempty"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// UpsertExperimentScheduleAO An update or insert for an experiment schedule +// +// Example: {"cron":"30 * * * * ? *","experimentKey":"ADM-8"} +type UpsertExperimentScheduleAO struct { + // AllowParallel Should the experiment run if another experiment is running? Default is true. + // + // Example: true + AllowParallel *bool `json:"allowParallel,omitempty"` + + // Cron Cron expression for the experiment schedule. Can't be used in combination with `startAt`. + // + // Example: 0 15 10 ? * * + Cron *string `json:"cron,omitempty"` + + // Enabled If `false`, the schedule is deactivated and no experiment will be executed. Default is true. + // + // Example: false + Enabled *bool `json:"enabled,omitempty"` + + // ExperimentKey The experiment that should be scheduled. + // + // Example: ADM-123 + ExperimentKey string `json:"experimentKey"` + + // Id The unique identifier of the schedule. If not set, a new schedule will be created. + // + // Example: 01951394-727f-76a0-8675-c7519ebd0ff5 + Id *string `json:"id,omitempty"` + + // StartAt Start date for a single execution. Can't be used in combination with `cron`. + StartAt *time.Time `json:"startAt,omitempty"` + + // Timezone Optional timezone for a experiment schedule. Can only be used with `cron`. + // + // Example: Europe/Berlin + Timezone *string `json:"timezone,omitempty"` + + // Variables Variables that will be used when the experiment will be executed. The variables will override existing environment or experiment variables. Each value is either a constant string, an array of constant strings, or a select expression object. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal"} + Variables *map[string]VariableExpressionAO `json:"variables,omitempty"` +} + +// UpsertExperimentTemplateAO Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the template already exists and should be updated or newly inserted. +// +// Example: {"lanes":[{"steps":[{"actionType":"com.steadybit.extension_http.check.periodically","ignoreFailure":false,"parameters":{"connectTimeout":"5s","duration":"60s","followRedirects":false,"headers":[],"maxConcurrent":5,"method":"GET","readTimeout":"5s","requestsPerSecond":1,"statusCode":"200-299","successRate":"100","url":"[[HTTP_ENDPOINT]]"},"type":"action"}]},{"steps":[{"ignoreFailure":false,"parameters":{"duration":"10s"},"type":"wait"},{"actionType":"com.steadybit.extension_kubernetes.rollout-restart","ignoreFailure":false,"parameters":{"wait":false},"radius":{"percentage":50,"predicate":{"operator":"AND","predicates":[{"key":"k8s.cluster-name","operator":"EQUALS","values":["[[CLUSTER]]"]},{"key":"k8s.namespace","operator":"EQUALS","values":["[[NAMESPACE]]"]},{"key":"k8s.deployment","operator":"EQUALS","values":["[[DEPLOYMENT]]"]}]},"targetType":"com.steadybit.extension_kubernetes.kubernetes-deployment"},"type":"action"},{"actionType":"com.steadybit.extension_kubernetes.rollout-status","ignoreFailure":false,"parameters":{"duration":"10m"},"radius":{"percentage":50,"predicate":{"operator":"AND","predicates":[{"key":"k8s.cluster-name","operator":"EQUALS","values":["[[CLUSTER]]"]},{"key":"k8s.namespace","operator":"EQUALS","values":["[[NAMESPACE]]"]},{"key":"k8s.deployment","operator":"EQUALS","values":["[[DEPLOYMENT]]"]}]},"targetType":"com.steadybit.extension_kubernetes.kubernetes-deployment"},"type":"action"}]}],"placeholders":[{"description":"Which HTTP Endpoint should be checked during experiment execution?","key":"HTTP_ENDPOINT","name":"HTTP Endpoint"},{"description":"Which Kubernetes deployment do you want to restart?","key":"DEPLOYMENT","name":"Kubernetes Deployment"},{"description":"In which Kubernetes cluster is the deployment deployed to?","key":"CLUSTER","name":"Kubernetes Cluster"},{"description":"In which Kubernetes namespace is the deployment deployed to?","key":"NAMESPACE","name":"Kubernetes Namespace"}],"properties":{"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"},"propertiesMetadata":[{"editableInExecution":false,"key":"EXAMPLE_CUSTOM_PROPERTY","required":true}],"tags":["Kubernetes"],"templateDescription":"Test if a given HTTP Endpoint remains funcitonal if a Kubernetes deployment is restarted.","templateTitle":"HTTP Endpoint remains functional during Kubernetes Rollout Restart"} +type UpsertExperimentTemplateAO struct { + // ExperimentName Name of the experiment created by this template. If omitted, the name needs to be added when the template is used. + // + // Example: Shop survives unavailability of database + ExperimentName *string `json:"experimentName,omitempty"` + + // Hidden Should the experiment template be hidden + // + // Example: false + Hidden *bool `json:"hidden,omitempty"` + + // Hypothesis The hypothesis that is validated by the experiment + // + // Example: System is able to survive a latency in the network of 1500ms + Hypothesis *string `json:"hypothesis,omitempty"` + Id *openapi_types.UUID `json:"id,omitempty"` + + // Lanes The lanes (steps executed in parallel) in the experiment template. Each lane consists of multiple steps that are executed sequential per lane. + // + // Example: [{"steps":[{"actionType":"com.steadybit.extension_host.stress-cpu","ignoreFailure":false,"parameters":{"duration":"30s"},"radius":{"percentage":100,"predicate":{"operator":"AND","predicates":[{"key":"k8s.deployment","operator":"EQUALS","values":["hot-deals"]}]},"query":null,"targetType":"com.steadybit.extension_container.container"},"type":"action"}]}] + Lanes []ExperimentLaneAO `json:"lanes"` + + // Placeholders A list of placeholders used in this experiment template. + Placeholders *[]ExperimentTemplatePlaceholderAO `json:"placeholders,omitempty"` + + // Properties The properties of the experiment + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this experiment!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // PropertiesMetadata Metadata for properties used in this template. + // + // Example: [{"editableInExecution":false,"key":"EXAMPLE_CUSTOM_PROPERTY","required":true}] + PropertiesMetadata *[]PropertyMetadataAO `json:"propertiesMetadata,omitempty"` + + // Tags A list of tags for this experiment template. (Up to 5) + Tags *[]string `json:"tags,omitempty"` + + // TemplateDescription A brief description what the template is doing. + TemplateDescription string `json:"templateDescription"` + + // TemplateTitle The title of the template + // + // Example: Shop survives unavailability of database + TemplateTitle string `json:"templateTitle"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// UpsertHubAO defines model for UpsertHubAO. +type UpsertHubAO struct { + // HubLink Website address of the the hub + // + // Example: https://hub.steadybit.com/ + HubLink *string `json:"hubLink,omitempty"` + + // HubName Name of the hub + HubName string `json:"hubName"` + Id *openapi_types.UUID `json:"id,omitempty"` + + // RepositoryUrl HTTP address of the the hub's repository + // + // Example: https://github.com/steadybit/reliability-hub-db + RepositoryUrl string `json:"repositoryUrl"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// UpsertLandscapeViewAO Create or update a saved view of the explorer landscape. +type UpsertLandscapeViewAO struct { + // ColorBy The color-by dimension: the attribute the targets are colored by, together with its advanced configuration. + ColorBy *LandscapeViewColorByAO `json:"colorBy,omitempty"` + + // Description Description of the saved view. + // + // Example: All shop workloads grouped by namespace. + Description *string `json:"description,omitempty"` + + // Environment Name of the environment the view is scoped to. + // + // Example: Global + Environment *string `json:"environment,omitempty"` + + // FilterQuery Explorer filter query narrowing the targets shown on the landscape. + // + // Example: k8s.namespace="shop" + FilterQuery *string `json:"filterQuery,omitempty"` + + // GroupBy Ordered list of group-by dimensions the targets are grouped by, each with its own advanced configuration. + GroupBy *[]LandscapeViewGroupByAO `json:"groupBy,omitempty"` + + // Name Title of the saved view. + // + // Example: Kubernetes by namespace + Name *string `json:"name,omitempty"` + + // ShowAdvice Whether reliability advice is shown on the landscape. + // + // Example: false + ShowAdvice *bool `json:"showAdvice,omitempty"` + + // SizeBy Attribute key the size of a target is derived from. + // + // Example: k8s.container.cpu.limit + SizeBy *string `json:"sizeBy,omitempty"` + + // Team Key of the team the saved view belongs to. + // + // Example: ADM + Team string `json:"team"` +} + +// UpsertPropertyAssociationAO A property association upsert. +// +// Example: {"editableInExecution":true,"key":"RESULT_COLOR","required":true} +type UpsertPropertyAssociationAO struct { + // AssociationType Always defined to either `EXPERIMENT` for experiment design or run related associations or `SERVICE` for service-associations. Only for the former, an `experimentKey` can be defined and only for the latter, a `serviceId` can be defined + // + // Example: EXPERIMENT + AssociationType *UpsertPropertyAssociationAOAssociationType `json:"associationType,omitempty"` + + // EditableInExecution Is the property editable in the execution view. Only used when `associationType` is set to `EXPERIMENT`. + // + // Example: true + EditableInExecution *bool `json:"editableInExecution,omitempty"` + + // ExperimentKey The key of the associated experiment. When `associationType` is set to `EXPERIMENT` and `experimentKey` is `null`, it is associated to ALL experiment designs. Can't be changed during updates. + // + // Example: EXP-1 + ExperimentKey *string `json:"experimentKey,omitempty"` + + // Id Id of an existing Property-Association. A new association will be created if no id is provided or no matching association could be found + Id *openapi_types.UUID `json:"id,omitempty"` + + // Key The key of the property definition + // + // Example: RESULT_COLOR + Key string `json:"key"` + + // Required Is the value required? + // + // Example: true + Required *bool `json:"required,omitempty"` + + // ServiceId The serviceId of the associated service. When `associationType` is set to `SERVICE` and `serviceId` is `null`, it is associated to ALL services. Can't be changed during updates. + // + // Example: 3308b47d-5c1f-4f08-a25b-a18fc10f8a56 + ServiceId *openapi_types.UUID `json:"serviceId,omitempty"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// UpsertPropertyAssociationAOAssociationType Always defined to either `EXPERIMENT` for experiment design or run related associations or `SERVICE` for service-associations. Only for the former, an `experimentKey` can be defined and only for the latter, a `serviceId` can be defined +// +// Example: EXPERIMENT +type UpsertPropertyAssociationAOAssociationType string + +// UpsertPropertyDefinitionAO A property association upsert. +// +// Example: {"dataType":"ENUM","description":"How would you describe the result of your experiment, thinking in beautiful colors?","enumValues":["RED","GREEN","BLUE"],"key":"RESULT_COLOR","label":"Result Color"} +type UpsertPropertyDefinitionAO struct { + // DataType The data type of the property + // + // Example: STRING + DataType UpsertPropertyDefinitionAODataType `json:"dataType"` + + // Description The text describing the property. + // + // Example: How would you describe the result of your experiment, thinking in beautiful colors? + Description *string `json:"description,omitempty"` + + // EnumValues Valid values if the dataType `ENUM` is used + // + // Example: ["RED","GREEN","BLUE"] + EnumValues *[]string `json:"enumValues,omitempty"` + + // Key The unique key of the property definition + // + // Example: RESULT_COLOR + Key string `json:"key"` + + // Label The label shown in the ui for this property + // + // Example: Result color + Label string `json:"label"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// UpsertPropertyDefinitionAODataType The data type of the property +// +// Example: STRING +type UpsertPropertyDefinitionAODataType string + +// UpsertProvidedExperimentRequestAO defines model for UpsertProvidedExperimentRequestAO. +type UpsertProvidedExperimentRequestAO struct { + // ExperimentKey Update only - the experiment that should be updated + // + // Example: ADM-18 + ExperimentKey *string `json:"experimentKey,omitempty"` + + // Placeholders List of template placeholder values + Placeholders *[]ExperimentTemplatePlaceholderValueAO `json:"placeholders,omitempty"` + + // TemplateId The templateId that should be used for the provided experiment (needs to be included in the used service profile) + // + // Example: e1c22d74-a48b-4661-ab56-4e90c584c4e0 + TemplateId openapi_types.UUID `json:"templateId"` +} + +// UpsertServiceAO Example: {"environment":"Global","name":"shopping-service","query":"aws.account=\"123\" OR aws.account=\"456\"","serviceProfile":"Steadybit Starter","team":"ADM","validations":[{"actionType":"com.steadybit.extension_http.check.periodically","parameters":{"method":"GET","url":"https://my-service/health"},"type":"action"}],"variables":{"httpEndpoint":"http://prod.shop.products.internal","targets":{"attribute":"k8s.deployment","count":1,"filter":"k8s.namespace=\"shop\"","mode":"fixed","targetType":"com.steadybit.extension_kubernetes.kubernetes-deployment","type":"select"}}} +type UpsertServiceAO struct { + // Environment The name of the environment to be used + // + // Example: Global + Environment string `json:"environment"` + + // Id The unique id of the service, will be created if not provided + Id *openapi_types.UUID `json:"id,omitempty"` + + // LogoColor Color scheme of the logo used to identify the service in the Platform UI + // + // Example: orangeLight + LogoColor string `json:"logoColor"` + + // LogoId Identifier of the logo used to identify the service in the Platform UI + // + // Example: service-router + LogoId string `json:"logoId"` + + // Name The name of the service + // + // Example: calculator-service + Name string `json:"name"` + + // Properties The properties of the service + // + // Example: {"EXAMPLE_CUSTOM_PROPERTY":"I like this service!"} + Properties *map[string]interface{} `json:"properties,omitempty"` + + // Query Query-Language predicate, specifies the targets belonging to this Service + // + // Example: aws.account="123" OR aws.account="456" + Query string `json:"query"` + + // ServiceProfile Name of the service profile that should be used for this service + // + // Example: Steadybit provided + ServiceProfile string `json:"serviceProfile"` + + // Team The key of the team to be used + // + // Example: ADM + Team string `json:"team"` + + // Validations List of validations to be executed against the service + Validations []ExperimentStepActionAO `json:"validations"` + + // Variables Variables owned by the service. Each value is either a constant string, an array of constant strings, or a select expression object. A select-expression value **must set `scope`** (`service` or `environment`) — the request is rejected otherwise. On `POST /api/services` (upsert): omitting this field leaves existing variables untouched, an empty object removes all of them. + // + // Example: {"httpEndpoint":"http://dev.shop.products.internal","targetServices":["gateway","hot-deals","fashion-bestseller"]} + Variables *map[string]VariableExpressionAO `json:"variables,omitempty"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// UpsertServiceProfileAO Request to create or update a service profile +// +// Example: {"name":"My Custom Templates","origin":"CUSTOM","templates":{"availability":["550e8400-e29b-41d4-a716-446655440000"],"latency":["550e8400-e29b-41d4-a716-446655440002"]}} +type UpsertServiceProfileAO struct { + // Id The unique id of the profile. Will be created if not provided. + Id *openapi_types.UUID `json:"id,omitempty"` + + // Name The name of the profile + // + // Example: Default Resilience Tests + Name string `json:"name"` + + // Origin Origin of a service profile + // + // Example: CUSTOM + Origin UpsertServiceProfileAOOrigin `json:"origin"` + + // Templates Template entries in this profile + Templates []ServiceProfileCategoryAO `json:"templates"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// UpsertServiceProfileAOOrigin Origin of a service profile +// +// Example: CUSTOM +type UpsertServiceProfileAOOrigin string + +// UpsertTeamAO Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. +// +// Example: {"allowedActions":["com.steadybit.extension_host.host.stress-cpu"],"allowedEnvironments":["Global"],"id":"71ab0180-8abc-4d30-8acb-6aa024e3065f","key":"ADM","logoColor":"cyanDark","logoId":"1","members":[{"email":"jane.doe@example.com","role":"OWNER"},{"email":"javier.rodriguez@example.com","role":"MEMBER"},{"role":"MEMBER","username":"auth0|1va2g15afc84590069cd53c3"}],"name":"Administrators","version":1} +type UpsertTeamAO struct { + // AllowedActions Set of allowed actions that can be used in an experiment of this team + // + // Example: ["com.steadybit.extension_host.host.stress-cpu"] + AllowedActions []string `json:"allowedActions"` + + // AllowedEnvironments Set of allowed environments, identified via name + // + // Example: ["Global"] + AllowedEnvironments []string `json:"allowedEnvironments"` + + // Description An optional description of a team + Description *string `json:"description,omitempty"` + Id *openapi_types.UUID `json:"id,omitempty"` + + // Key Unique identifier of a team + // + // Example: ADM + Key string `json:"key"` + + // LogoColor Color scheme of the logo used to identify the team in the Platform UI + // + // Example: cyanDark + LogoColor *string `json:"logoColor,omitempty"` + + // LogoId Identifier of the logo used to identify the team in the Platform UI + // + // Example: 1 + LogoId *string `json:"logoId,omitempty"` + + // ManagedBy How a team or team membership is managed + // + // Example: MANUAL + ManagedBy *UpsertTeamAOManagedBy `json:"managedBy,omitempty"` + + // Members Members that should be added to this team + // + // Example: {"email":"jane.doe@example.com","role":"OWNER"} + Members *[]MemberUpdateAO `json:"members,omitempty"` + + // Name Name of a team + // + // Example: ADMIN + Name string `json:"name"` + + // Version Version for optimistic locking (optional in the API) + // + // Example: 1 + Version *int32 `json:"version,omitempty"` +} + +// UpsertTeamAOManagedBy How a team or team membership is managed +// +// Example: MANUAL +type UpsertTeamAOManagedBy string + +// UserPrincipalAL A user has performed the logged event e.g. via UI +// +// Example: {"email":"example@example.com","name":"Jane Doe","principalType":"USER","role":"ADMIN","username":"1ava2afg-xju33-4c6a-9451-2854584c15be"} +type UserPrincipalAL struct { + // Email E-mail of the user, unique within Steadybit + // + // Example: example@example.com + Email string `json:"email"` + + // Name Name of the user + // + // Example: Jane Doe + Name string `json:"name"` + + // PrincipalType Principal type for user based principal + // + // Example: USER + PrincipalType UserPrincipalALPrincipalType `json:"principalType"` + + // Role Role of the user in the platform + // + // Example: ADMIN + Role *UserPrincipalALRole `json:"role,omitempty"` + + // Username Username of the user, internal identifier of Steadybit + // + // Example: 13av2737-b318-4048-a79d-4789d645bc31 + Username string `json:"username"` +} + +// UserPrincipalALPrincipalType Principal type for user based principal +// +// Example: USER +type UserPrincipalALPrincipalType string + +// UserPrincipalALRole Role of the user in the platform +// +// Example: ADMIN +type UserPrincipalALRole string + +// UserSummaryAO The user that canceled the experiment execution, only present if the execution was canceled +// +// Example: {"email":"max@steadybit.com","name":"Max Mustermann","username":"ag1hb7ap-d299-47ab-998f-c2a53b433820"} +type UserSummaryAO struct { + Email *string `json:"email,omitempty"` + + // Name Name of the user + // + // Example: Jane Doe + Name *string `json:"name,omitempty"` + PictureUrl *string `json:"pictureUrl,omitempty"` + + // Username Username of the user, internal identifier of Steadybit + // + // Example: 13av2737-b318-4048-a79d-4789d645bc31 + Username string `json:"username"` +} + +// VariableExpressionAO A variable value: a constant string (≤5000 chars), an array of constant strings, or a select expression object. +type VariableExpressionAO struct { + union json.RawMessage +} + +// VariableExpressionAO0 defines model for VariableExpressionAO.0. +type VariableExpressionAO0 = string + +// VariableExpressionAO1 defines model for VariableExpressionAO.1. +type VariableExpressionAO1 = []string + +// VariableValueAO defines model for VariableValueAO. +type VariableValueAO struct { + Type *string `json:"type,omitempty"` + Value string `json:"value"` +} + +// GetAccessTokensParams defines parameters for GetAccessTokens. +type GetAccessTokensParams struct { + Type *GetAccessTokensParamsType `form:"type,omitempty" json:"type,omitempty"` + Team *string `form:"team,omitempty" json:"team,omitempty"` + PageRequest PageRequestAO `form:"pageRequest" json:"pageRequest"` +} + +// GetAccessTokensParamsType defines parameters for GetAccessTokens. +type GetAccessTokensParamsType string + +// GetAccessTokens1Params defines parameters for GetAccessTokens1. +type GetAccessTokens1Params struct { + Name *string `form:"name,omitempty" json:"name,omitempty"` + CreatedBy *string `form:"createdBy,omitempty" json:"createdBy,omitempty"` + Type *GetAccessTokens1ParamsType `form:"type,omitempty" json:"type,omitempty"` + Teams *[]string `form:"teams,omitempty" json:"teams,omitempty"` + Expired *bool `form:"expired,omitempty" json:"expired,omitempty"` + PageRequest PageRequestAO `form:"pageRequest" json:"pageRequest"` +} + +// GetAccessTokens1ParamsType defines parameters for GetAccessTokens1. +type GetAccessTokens1ParamsType string + +// FindAllActionsParams defines parameters for FindAllActions. +type FindAllActionsParams struct { + // Page The page number to retrieve. Starts from 0. + Page *int32 `form:"page,omitempty" json:"page,omitempty"` + + // Size The number of items to return per page. + Size *int32 `form:"size,omitempty" json:"size,omitempty"` +} + +// FindParams defines parameters for Find. +type FindParams struct { + // From Starting point with the earliest time to be included.
If neither `to` nor `from` is specified, it defaults to a 7 days date range from today. + From *time.Time `form:"from,omitempty" json:"from,omitempty"` + + // To End point with the latest time to be included.
If neither `to` nor `from` is specified, it defaults to a 7 days date range from today. + To *time.Time `form:"to,omitempty" json:"to,omitempty"` +} + +// ForwardToPlatformParams defines parameters for ForwardToPlatform. +type ForwardToPlatformParams struct { + // TenantKey Key of the Steadybit tenant. You can get the key from the Platform URL or by asking the Steadybit team + TenantKey string `form:"tenantKey" json:"tenantKey"` + + // Tag Tag that identifies the experiment. This is used to identify whether an experiment was already created for these tag or not. Can be e.g. an incident or ticket identifier of pager duty or JIRA + Tag *string `form:"tag,omitempty" json:"tag,omitempty"` + + // ExternalReference External reference that identifies the experiment. This is used to identify whether an experiment was already created for that reference or not. Can be e.g. an incident or ticket identifier of pager duty or JIRA + // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set + ExternalReference *string `form:"externalReference,omitempty" json:"externalReference,omitempty"` +} + +// GetLinkedBadgeParams defines parameters for GetLinkedBadge. +type GetLinkedBadgeParams struct { + // TenantKey Key of the Steadybit tenant (only for SaaS customers). You can get the key from the Platform URL or by asking the Steadybit team + TenantKey string `form:"tenantKey" json:"tenantKey"` + + // ExternalReference External reference that identifies the experiment. This is used to identify whether an experiment was already created for that reference or not. Can be e.g. an incident or ticket identifier of pager duty or JIRA + // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set + ExternalReference *string `form:"externalReference,omitempty" json:"externalReference,omitempty"` + + // Tag A tag that identifies the experiment. This is used to identify whether an experiment was already created having this tag or not. Can be e.g. an incident or ticket identifier of pager duty or JIRA + Tag *string `form:"tag,omitempty" json:"tag,omitempty"` + + // CreateCaption Caption that is shown at the badge when no experiment exists in order to create a new experiment + CreateCaption *string `form:"createCaption,omitempty" json:"createCaption,omitempty"` + + // Scale Optional parameter in case you need to scale the svg image. Defaults to 1 + Scale *int32 `form:"scale,omitempty" json:"scale,omitempty"` +} + +// GetEnvironmentsParams defines parameters for GetEnvironments. +type GetEnvironmentsParams struct { + // Search If set, only environments matching the search are returned. Matches the environment name or the name or key of a team the environment is assigned to. + Search *string `form:"search,omitempty" json:"search,omitempty"` +} + +// SetEnvironmentVariablesJSONBody defines parameters for SetEnvironmentVariables. +type SetEnvironmentVariablesJSONBody map[string]VariableExpressionAO + +// UpdateEnvironmentVariablesJSONBody defines parameters for UpdateEnvironmentVariables. +type UpdateEnvironmentVariablesJSONBody map[string]VariableExpressionAO + +// GetExperimentsParams defines parameters for GetExperiments. +type GetExperimentsParams struct { + // Runnable Include only experiments which are runnable by the authorized user? + Runnable *bool `form:"runnable,omitempty" json:"runnable,omitempty"` + + // Name Filter results by name and/or key of the experiment + Name *string `form:"name,omitempty" json:"name,omitempty"` + + // Team Filter results by one or more team-keys owning an experiment + Team *[]string `form:"team,omitempty" json:"team,omitempty"` + + // TeamSharedWith Filter results by one or more team keys the experiment is shared with + TeamSharedWith *[]string `form:"teamSharedWith,omitempty" json:"teamSharedWith,omitempty"` + + // Key Filter results by one or more experiments-keys + Key *[]string `form:"key,omitempty" json:"key,omitempty"` + + // ExternalId Filter results by one or more external-ids + ExternalId *[]string `form:"externalId,omitempty" json:"externalId,omitempty"` + + // TargetType Filter results by experiments using the specified target-type. If multiple target-types are specified, all of them needs to be used in the experiment + TargetType *[]string `form:"targetType,omitempty" json:"targetType,omitempty"` + + // Action Filter results by experiments using the specified action. If multiple actions are specified, all of them needs to be used in the experiment + Action *[]string `form:"action,omitempty" json:"action,omitempty"` + + // Tag Filter results by experiments having the specified tag. If multiple tags are specified, all of them needs to be assigned to the experiment + Tag *[]string `form:"tag,omitempty" json:"tag,omitempty"` + + // Kind Filter results by experiments using an action with the specified kind. If multiple kinds are specified, all of them needs to be used in the experiment + Kind *[]GetExperimentsParamsKind `form:"kind,omitempty" json:"kind,omitempty"` + + // Service Filter results by experiments linked to the given Service. If multiple services are specified, the experiment needs to be linked to all of them + Service *[]string `form:"service,omitempty" json:"service,omitempty"` + + // FreeTextPhrases Filter results via free text phrases searching for experiment name, key, property values, and 10 last run ids + FreeTextPhrases *[]string `form:"freeTextPhrases,omitempty" json:"freeTextPhrases,omitempty"` + + // Properties Filter results via properties + Properties *[]string `form:"properties,omitempty" json:"properties,omitempty"` +} + +// GetExperimentsParamsKind defines parameters for GetExperiments. +type GetExperimentsParamsKind string + +// SaveAndRunParams defines parameters for SaveAndRun. +type SaveAndRunParams struct { + // AllowParallel Should this experiment also be executed when there is already at least one experiment running? + AllowParallel *bool `form:"allowParallel,omitempty" json:"allowParallel,omitempty"` + + // ForcePersist Optional parameter to always store runs on any failure. If false, won´t be stored on validation errors (default behaviour). + ForcePersist *bool `form:"forcePersist,omitempty" json:"forcePersist,omitempty"` +} + +// GetExperimentExecutions1Params defines parameters for GetExperimentExecutions1. +type GetExperimentExecutions1Params struct { + // Name Filter results by name and/or key of the experiment + Name *string `form:"name,omitempty" json:"name,omitempty"` + + // Team Filter results by one or more team-keys + Team *[]string `form:"team,omitempty" json:"team,omitempty"` + + // State Filter results by one or more states + State *[]GetExperimentExecutions1ParamsState `form:"state,omitempty" json:"state,omitempty"` +} + +// GetExperimentExecutions1ParamsState defines parameters for GetExperimentExecutions1. +type GetExperimentExecutions1ParamsState string + +// GetExperimentExecutionParams defines parameters for GetExperimentExecution. +type GetExperimentExecutionParams struct { + // Fields Additional fields to be returned for the experiment execution + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// AddExecutionPropertyValueJSONBody defines parameters for AddExecutionPropertyValue. +type AddExecutionPropertyValueJSONBody = map[string]interface{} + +// SetExecutionPropertyValueJSONBody defines parameters for SetExecutionPropertyValue. +type SetExecutionPropertyValueJSONBody = map[string]interface{} + +// GetAllSchedulesV2Params defines parameters for GetAllSchedulesV2. +type GetAllSchedulesV2Params struct { + // Team Filter results by one or more team-keys + Team *[]string `form:"team,omitempty" json:"team,omitempty"` + + // Experiment Filter results by one or more experiment-keys + Experiment *[]string `form:"experiment,omitempty" json:"experiment,omitempty"` +} + +// GetExperimentTemplatesParams defines parameters for GetExperimentTemplates. +type GetExperimentTemplatesParams struct { + // Tag Filter results by one or more tags + Tag *[]string `form:"tag,omitempty" json:"tag,omitempty"` + + // TargetType Filter results by one or more target type, like `com.steadybit.extension_container.container` + TargetType *[]string `form:"targetType,omitempty" json:"targetType,omitempty"` + + // Action Filter results by one or more action, like `com.steadybit.extension_host.stress-cpu` + Action *[]string `form:"action,omitempty" json:"action,omitempty"` + + // FreeTextPhrases Filter results by one or more free text phrases searching in the template title and template description + FreeTextPhrases *[]string `form:"freeTextPhrases,omitempty" json:"freeTextPhrases,omitempty"` + + // IncludeHidden Include hidden templates (requires an admin token) + IncludeHidden *bool `form:"includeHidden,omitempty" json:"includeHidden,omitempty"` + + // IncludeNonAvailable Include templates referencing actions/target-types/property-definitions that are not available + IncludeNonAvailable *bool `form:"includeNonAvailable,omitempty" json:"includeNonAvailable,omitempty"` +} + +// ImportFromHubParams defines parameters for ImportFromHub. +type ImportFromHubParams struct { + // Overwrite Do you want to overwrite a template that already exists? If set to `false` and any of the templates already exist, the API will return HTTP status 409 and none of the templates are imported. + Overwrite *bool `form:"overwrite,omitempty" json:"overwrite,omitempty"` +} + +// CreateExperimentByTemplateParams defines parameters for CreateExperimentByTemplate. +type CreateExperimentByTemplateParams struct { + // ResetProperties If `true`, all properties will be reset to properties specified in the template either with their fixed values in the template or via template placeholder. If `false`, existing properties will stay untouched, only new properties will be added. Only relevant for experiment updates via `externalId`. + ResetProperties *bool `form:"resetProperties,omitempty" json:"resetProperties,omitempty"` +} + +// SaveAndRunFromTemplateParams defines parameters for SaveAndRunFromTemplate. +type SaveAndRunFromTemplateParams struct { + // ResetProperties If `true`, all properties will be reset to properties specified in the template either with their fixed values in the template or via template placeholder. If `false`, existing properties will stay untouched, only new properties will be added. Only relevant for experiment updates via `externalId`. + ResetProperties *bool `form:"resetProperties,omitempty" json:"resetProperties,omitempty"` + + // AllowParallel Should this experiment also be executed when there is already another experiment running? + AllowParallel *bool `form:"allowParallel,omitempty" json:"allowParallel,omitempty"` + + // ForcePersist Optional parameter to always store runs on any failure. If false, won´t be stored on validation errors (default behaviour). + ForcePersist *bool `form:"forcePersist,omitempty" json:"forcePersist,omitempty"` +} + +// UpdateExperimentByTemplateParams defines parameters for UpdateExperimentByTemplate. +type UpdateExperimentByTemplateParams struct { + // ResetProperties If `true`, all properties will be reset to properties specified in the template either with their fixed values in the template or via template placeholder. If `false`, existing properties will stay untouched, only new properties will be added. + ResetProperties *bool `form:"resetProperties,omitempty" json:"resetProperties,omitempty"` +} + +// GetExperimentBadgeParams defines parameters for GetExperimentBadge. +type GetExperimentBadgeParams struct { + // TenantKey Key of the Steadybit tenant (only for SaaS customers). You can get the key from the Platform URL or by asking the Steadybit team + TenantKey string `form:"tenantKey" json:"tenantKey"` + + // Scale Optional parameter in case you need to scale the svg image. Defaults to 1 + Scale *int32 `form:"scale,omitempty" json:"scale,omitempty"` + + // ColorMappingErrored Override the default hex-color `e05d44` for executions in state `errored`. + ColorMappingErrored *string `form:"colorMappingErrored,omitempty" json:"colorMappingErrored,omitempty"` + + // ColorMappingFailed Override the default hex-color `e05d44` for executions in state `failed`. + ColorMappingFailed *string `form:"colorMappingFailed,omitempty" json:"colorMappingFailed,omitempty"` + + // ColorMappingRequested Override the default hex-color `fe7d37` for executions in state `requested`. + ColorMappingRequested *string `form:"colorMappingRequested,omitempty" json:"colorMappingRequested,omitempty"` + + // ColorMappingCreated Override the default hex-color `fe7d37` for executions in state `created`. + ColorMappingCreated *string `form:"colorMappingCreated,omitempty" json:"colorMappingCreated,omitempty"` + + // ColorMappingPrepared Override the default hex-color `fe7d37` for executions in state `prepared`. + ColorMappingPrepared *string `form:"colorMappingPrepared,omitempty" json:"colorMappingPrepared,omitempty"` + + // ColorMappingRunning Override the default hex-color `fe7d37` for executions in state `running`. + ColorMappingRunning *string `form:"colorMappingRunning,omitempty" json:"colorMappingRunning,omitempty"` + + // ColorMappingCanceled Override the default hex-color `9f9f9f` for executions in state `canceled`. + ColorMappingCanceled *string `form:"colorMappingCanceled,omitempty" json:"colorMappingCanceled,omitempty"` + + // ColorMappingCompleted Override the default hex-color `4c1` for executions in state `completed`. + ColorMappingCompleted *string `form:"colorMappingCompleted,omitempty" json:"colorMappingCompleted,omitempty"` +} + +// ExecuteExperimentParams defines parameters for ExecuteExperiment. +type ExecuteExperimentParams struct { + // AllowParallel By default an experiment is only executed when no other experiment is running. This can be overriden by starting the new experiment execution although another one is currently running + AllowParallel *bool `form:"allowParallel,omitempty" json:"allowParallel,omitempty"` + + // ForcePersist Optional parameter to always store runs on any failure. If false, won´t be stored on validation errors (default behaviour). + ForcePersist *bool `form:"forcePersist,omitempty" json:"forcePersist,omitempty"` +} + +// GetExperimentExecutions3Params defines parameters for GetExperimentExecutions3. +type GetExperimentExecutions3Params struct { + // State Filter results by one or more states + State *[]GetExperimentExecutions3ParamsState `form:"state,omitempty" json:"state,omitempty"` +} + +// GetExperimentExecutions3ParamsState defines parameters for GetExperimentExecutions3. +type GetExperimentExecutions3ParamsState string + +// GetLandscapeViewsParams defines parameters for GetLandscapeViews. +type GetLandscapeViewsParams struct { + // Team Key of the team whose saved views should be returned. + Team string `form:"team" json:"team"` +} + +// UpsertHubParams defines parameters for UpsertHub. +type UpsertHubParams struct { + // Synchronize Whether to synchronize the hub or not. + Synchronize *bool `form:"synchronize,omitempty" json:"synchronize,omitempty"` +} + +// DeleteHubParams defines parameters for DeleteHub. +type DeleteHubParams struct { + // DeleteImportedTemplates Whether imported templates of the hub should be deleted as well. + DeleteImportedTemplates *bool `form:"deleteImportedTemplates,omitempty" json:"deleteImportedTemplates,omitempty"` +} + +// GetPreflightActionSummaryParams defines parameters for GetPreflightActionSummary. +type GetPreflightActionSummaryParams struct { + Offset int32 `form:"offset" json:"offset"` +} + +// GetAssociationsParams defines parameters for GetAssociations. +type GetAssociationsParams struct { + // Page The number of the page, responses are limited to 50 elements per page. + Page *int32 `form:"page,omitempty" json:"page,omitempty"` + + // Key Filter association based on a single property definition key + Key *string `form:"key,omitempty" json:"key,omitempty"` + + // ExperimentKey Filter association that are explicitly assigned to the given experimentKey. (There might still be associations for ALL Experiment Designs) + ExperimentKey *string `form:"experimentKey,omitempty" json:"experimentKey,omitempty"` + + // ServiceId Filter association that are explicitly assigned to the given serviceId. (There might still be associations for ALL Services) + ServiceId *openapi_types.UUID `form:"serviceId,omitempty" json:"serviceId,omitempty"` + + // AssociationTypeAO Filter association based on association type (`EXPERIMENT` for experiment-related associations and `SERVICE` for service-related associations, no matter whether globally or individually) + AssociationTypeAO *GetAssociationsParamsAssociationTypeAO `form:"associationTypeAO,omitempty" json:"associationTypeAO,omitempty"` +} + +// GetAssociationsParamsAssociationTypeAO defines parameters for GetAssociations. +type GetAssociationsParamsAssociationTypeAO string + +// DeletePropertyAssociationParams defines parameters for DeletePropertyAssociation. +type DeletePropertyAssociationParams struct { + // DeleteValues Associations can only be deleted, if no experiment design or experiment schedule is still using the value. Setting this parameter to `true` will delete those values. + DeleteValues *bool `form:"deleteValues,omitempty" json:"deleteValues,omitempty"` +} + +// GetPropertyDefinitionsParams defines parameters for GetPropertyDefinitions. +type GetPropertyDefinitionsParams struct { + // Page The number of the page, responses are limited to 50 elements per page. + Page PageRequestAO `form:"page" json:"page"` +} + +// UpsertPropertyDefinitionParams defines parameters for UpsertPropertyDefinition. +type UpsertPropertyDefinitionParams struct { + // DeleteValues You can remove enum-values for a ENUM or ENUM_LIST property if they are still in use in experiment designs. Setting this parameter to `true` will delete those values in experiment designs. + DeleteValues *bool `form:"deleteValues,omitempty" json:"deleteValues,omitempty"` +} + +// DeletePropertyDefinitionParams defines parameters for DeletePropertyDefinition. +type DeletePropertyDefinitionParams struct { + // DeleteAssociations Definitions can only be deleted, if no associations are still refering to this property. Setting the value to `true` will delete all associations and all current values in experiment designs and schedules. Existing executions won't get touched. + DeleteAssociations *bool `form:"deleteAssociations,omitempty" json:"deleteAssociations,omitempty"` +} + +// GetExperimentCreationsParams defines parameters for GetExperimentCreations. +type GetExperimentCreationsParams struct { + // GroupBy Grouping dimension for the results. + GroupBy *GetExperimentCreationsParamsGroupBy `form:"groupBy,omitempty" json:"groupBy,omitempty"` +} + +// GetExperimentCreationsParamsGroupBy defines parameters for GetExperimentCreations. +type GetExperimentCreationsParamsGroupBy string + +// GetExperimentExecutionsParams defines parameters for GetExperimentExecutions. +type GetExperimentExecutionsParams struct { + // GroupBy Grouping dimension for the results. + GroupBy *GetExperimentExecutionsParamsGroupBy `form:"groupBy,omitempty" json:"groupBy,omitempty"` +} + +// GetExperimentExecutionsParamsGroupBy defines parameters for GetExperimentExecutions. +type GetExperimentExecutionsParamsGroupBy string + +// GetServiceListParams defines parameters for GetServiceList. +type GetServiceListParams struct { + // TeamKey Filter results by one or more team key, like 'ADM' + TeamKey *[]string `form:"teamKey,omitempty" json:"teamKey,omitempty"` + + // ExperimentKey Filter results by one or more experiment keys being linked to a service, like 'ADM-123' + ExperimentKey *[]string `form:"experimentKey,omitempty" json:"experimentKey,omitempty"` + + // EnvironmentName Filter results by one or more environment name, like 'Global' + EnvironmentName *[]string `form:"environmentName,omitempty" json:"environmentName,omitempty"` + Page PageRequestAO `form:"page" json:"page"` +} + +// UpsertServiceParams defines parameters for UpsertService. +type UpsertServiceParams struct { + // DeleteExperiments When the service profile of a service gets updated, provided experiments whose template ids are not part of the new service profile are not allowed. Setting the value to `true` will delete those experiments. + DeleteExperiments *bool `form:"deleteExperiments,omitempty" json:"deleteExperiments,omitempty"` +} + +// GetProfilesParams defines parameters for GetProfiles. +type GetProfilesParams struct { + // Name Filter results by name (partial match) + Name *string `form:"name,omitempty" json:"name,omitempty"` + + // Origin Filter results by origin (PROVIDED, CUSTOM) + Origin *[]string `form:"origin,omitempty" json:"origin,omitempty"` + + // DefaultProfile Filter results by defaultProfile flag + DefaultProfile *bool `form:"defaultProfile,omitempty" json:"defaultProfile,omitempty"` + Page PageRequestAO `form:"page" json:"page"` +} + +// UpsertProfileParams defines parameters for UpsertProfile. +type UpsertProfileParams struct { + // DeleteExperiments When templates are removed from a service profile, provided experiments using those templates will be affected. Setting the value to `true` will delete those experiments. + DeleteExperiments *bool `form:"deleteExperiments,omitempty" json:"deleteExperiments,omitempty"` +} + +// GetServiceExperimentsParams defines parameters for GetServiceExperiments. +type GetServiceExperimentsParams struct { + // Category Filter results by one or more categories + Category *[]string `form:"category,omitempty" json:"category,omitempty"` + + // CategoryMissing Include custom experiments with missing categories + CategoryMissing *bool `form:"categoryMissing,omitempty" json:"categoryMissing,omitempty"` + + // Type Filter results by type (PROVIDED,CUSTOM) + Type *[]GetServiceExperimentsParamsType `form:"type,omitempty" json:"type,omitempty"` + Page PageRequestAO `form:"page" json:"page"` +} + +// GetServiceExperimentsParamsType defines parameters for GetServiceExperiments. +type GetServiceExperimentsParamsType string + +// UnlinkCustomExperimentParams defines parameters for UnlinkCustomExperiment. +type UnlinkCustomExperimentParams struct { + ExperimentKey string `form:"experimentKey" json:"experimentKey"` +} + +// UpsertProvidedExperimentParams defines parameters for UpsertProvidedExperiment. +type UpsertProvidedExperimentParams struct { + // ResetProperties If `true`, all properties will be reset to properties specified in the template either with their fixed values in the template or via template placeholder. If `false`, existing properties will stay untouched, only new properties will be added. Only relevant for experiment updates. + ResetProperties *bool `form:"resetProperties,omitempty" json:"resetProperties,omitempty"` +} + +// MergeServiceVariablesJSONBody defines parameters for MergeServiceVariables. +type MergeServiceVariablesJSONBody map[string]VariableExpressionAO + +// SetServiceVariablesJSONBody defines parameters for SetServiceVariables. +type SetServiceVariablesJSONBody map[string]VariableExpressionAO + +// GetTargetsParams defines parameters for GetTargets. +type GetTargetsParams struct { + // Environment The name of the environment + Environment string `form:"environment" json:"environment"` + + // TargetType Optional, the type of the target + TargetType *string `form:"targetType,omitempty" json:"targetType,omitempty"` + + // Query Optional, additional target selection query + Query *string `form:"query,omitempty" json:"query,omitempty"` + + // Attribute Optional, list of requested target attribute keys. If not specified, all attributes will be returned. Multiple values allowed. Example: `k8s.deployment` + Attribute *[]string `form:"attribute,omitempty" json:"attribute,omitempty"` + + // Cursor Optional, the cursor to use to fetch the next page + Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` + + // Size Optional, the number of items to return per page. default is 100, maximum is 1000. + Size *int32 `form:"size,omitempty" json:"size,omitempty"` +} + +// GetTargetAttributeKeysParams defines parameters for GetTargetAttributeKeys. +type GetTargetAttributeKeysParams struct { + // Environment The name of the environment + Environment string `form:"environment" json:"environment"` + + // TargetType The type of the target, required if actionId is not set + TargetType *string `form:"targetType,omitempty" json:"targetType,omitempty"` + + // ActionId If the action specifies a extended target selector and you want to fetch all attribute keys for a given action. Required if targetType is not set + ActionId *string `form:"actionId,omitempty" json:"actionId,omitempty"` + + // Page The page number to retrieve. Starts from 0. default is 0 + Page *int32 `form:"page,omitempty" json:"page,omitempty"` + + // Size The number of items to return per page. default is 100, maximum is 100. + Size *int32 `form:"size,omitempty" json:"size,omitempty"` +} + +// GetTargetAttributeValuesParams defines parameters for GetTargetAttributeValues. +type GetTargetAttributeValuesParams struct { + // Environment The name of the environment + Environment string `form:"environment" json:"environment"` + + // TargetType The type of the target, required if actionId is not set + TargetType *string `form:"targetType,omitempty" json:"targetType,omitempty"` + + // ActionId If the action specifies a extended target selector and you want to fetch all attribute keys for a given action. Required if targetType is not set + ActionId *string `form:"actionId,omitempty" json:"actionId,omitempty"` + + // AttributeKey The key of of the attribute + AttributeKey string `form:"attributeKey" json:"attributeKey"` + + // Page The page number to retrieve. Starts from 0. default is 0 + Page *int32 `form:"page,omitempty" json:"page,omitempty"` + + // Size The number of items to return per page. default is 100, maximum is 100. + Size *int32 `form:"size,omitempty" json:"size,omitempty"` +} + +// GetTeamsParams defines parameters for GetTeams. +type GetTeamsParams struct { + // OnlyAccessible If set and used with an `accessToken` associated to one or multiple teams, only the team associated to the token are returned. Otherwise, all teams are listed. + OnlyAccessible *bool `form:"onlyAccessible,omitempty" json:"onlyAccessible,omitempty"` + + // Search If set, only teams matching the search are returned. Matches the team name or key, the name or email of a team member, or the name of an allowed environment. + Search *string `form:"search,omitempty" json:"search,omitempty"` +} + +// UpsertTeamParams defines parameters for UpsertTeam. +type UpsertTeamParams struct { + // ValidateActions By default, Steadybit checks whether the allowed actions exists and are reported by an agent. For convenience, this can be deactivated to decouple team creation and agent-installation + ValidateActions *bool `form:"validateActions,omitempty" json:"validateActions,omitempty"` + + // ValidateMembers By default, Steadybit will skip members, which are not yet know. If set to true, Steadybit will validate the given members and show a 422 response. + ValidateMembers *bool `form:"validateMembers,omitempty" json:"validateMembers,omitempty"` +} + +// DeleteTeamParams defines parameters for DeleteTeam. +type DeleteTeamParams struct { + // PurgeIncludingExperiments Safety-Parameter - purge team including all experiments and executions. + PurgeIncludingExperiments bool `form:"purgeIncludingExperiments" json:"purgeIncludingExperiments"` +} + +// SetTeamEnvironmentsParams defines parameters for SetTeamEnvironments. +type SetTeamEnvironmentsParams struct { + // ValidateEnvironments By default, Steadybit will skip environments, which are not yet know. If set to true, Steadybit will validate the given environments and show a 422 response. + ValidateEnvironments *bool `form:"validateEnvironments,omitempty" json:"validateEnvironments,omitempty"` +} + +// AddTeamEnvironmentsParams defines parameters for AddTeamEnvironments. +type AddTeamEnvironmentsParams struct { + // ValidateEnvironments By default, Steadybit will skip environments, which are not yet know. If set to true, Steadybit will validate the given environments and show a 422 response. + ValidateEnvironments *bool `form:"validateEnvironments,omitempty" json:"validateEnvironments,omitempty"` +} + +// SetTeamMembersParams defines parameters for SetTeamMembers. +type SetTeamMembersParams struct { + // ValidateMembers By default, Steadybit will skip members, which are not yet know. If set to true, Steadybit will validate the given members and show a 422 response. + ValidateMembers *bool `form:"validateMembers,omitempty" json:"validateMembers,omitempty"` +} + +// AddTeamMembersParams defines parameters for AddTeamMembers. +type AddTeamMembersParams struct { + // ValidateMembers By default, Steadybit will skip members, which are not yet know. If set to true, Steadybit will validate the given members and show a 422 response. + ValidateMembers *bool `form:"validateMembers,omitempty" json:"validateMembers,omitempty"` +} + +// CreateAccessTokenJSONRequestBody defines body for CreateAccessToken for application/json ContentType. +// +// Deprecated: this type has been marked as deprecated upstream, but no `x-deprecated-reason` was set +type CreateAccessTokenJSONRequestBody = CreateAccessTokenRequestAO + +// CreateAccessToken1JSONRequestBody defines body for CreateAccessToken1 for application/json ContentType. +type CreateAccessToken1JSONRequestBody = CreateAccessTokenRequestV2AO + +// RecreateAccessTokenJSONRequestBody defines body for RecreateAccessToken for application/json ContentType. +type RecreateAccessTokenJSONRequestBody = RecreateAccessTokenRequestV2AO + +// GetTargetAdviceSummaryJSONRequestBody defines body for GetTargetAdviceSummary for application/json ContentType. +type GetTargetAdviceSummaryJSONRequestBody = GetAdviceApiRequestAO + +// UpsertEnvironmentJSONRequestBody defines body for UpsertEnvironment for application/json ContentType. +type UpsertEnvironmentJSONRequestBody = UpsertEnvironmentAO + +// SetEnvironmentVariablesJSONRequestBody defines body for SetEnvironmentVariables for application/json ContentType. +type SetEnvironmentVariablesJSONRequestBody SetEnvironmentVariablesJSONBody + +// UpdateEnvironmentVariablesJSONRequestBody defines body for UpdateEnvironmentVariables for application/json ContentType. +type UpdateEnvironmentVariablesJSONRequestBody UpdateEnvironmentVariablesJSONBody + +// CreateOrUpdateExperimentJSONRequestBody defines body for CreateOrUpdateExperiment for application/json ContentType. +type CreateOrUpdateExperimentJSONRequestBody = CreateExperimentAO + +// SaveAndRunJSONRequestBody defines body for SaveAndRun for application/json ContentType. +type SaveAndRunJSONRequestBody = CreateAndRunExperimentAO + +// GetExperimentExecutions2JSONRequestBody defines body for GetExperimentExecutions2 for application/json ContentType. +type GetExperimentExecutions2JSONRequestBody = ExperimentExecutionsRequestAO + +// UpdateExecutionPropertiesJSONRequestBody defines body for UpdateExecutionProperties for application/json ContentType. +type UpdateExecutionPropertiesJSONRequestBody = UpdateExperimentExecutionPropertiesAO + +// AddExecutionPropertyValueJSONRequestBody defines body for AddExecutionPropertyValue for application/json ContentType. +type AddExecutionPropertyValueJSONRequestBody = AddExecutionPropertyValueJSONBody + +// SetExecutionPropertyValueJSONRequestBody defines body for SetExecutionPropertyValue for application/json ContentType. +type SetExecutionPropertyValueJSONRequestBody = SetExecutionPropertyValueJSONBody + +// UpsertScheduleJSONRequestBody defines body for UpsertSchedule for application/json ContentType. +type UpsertScheduleJSONRequestBody = UpsertExperimentScheduleAO + +// PatchScheduleJSONRequestBody defines body for PatchSchedule for application/json ContentType. +type PatchScheduleJSONRequestBody = PatchExperimentScheduleAO + +// UpsertExperimentTemplateJSONRequestBody defines body for UpsertExperimentTemplate for application/json ContentType. +type UpsertExperimentTemplateJSONRequestBody = UpsertExperimentTemplateAO + +// ImportFromHubJSONRequestBody defines body for ImportFromHub for application/json ContentType. +type ImportFromHubJSONRequestBody = ExperimentTemplatesImportAO + +// CreateExperimentByTemplateJSONRequestBody defines body for CreateExperimentByTemplate for application/json ContentType. +type CreateExperimentByTemplateJSONRequestBody = CreateExperimentFromTemplateAO + +// SaveAndRunFromTemplateJSONRequestBody defines body for SaveAndRunFromTemplate for application/json ContentType. +type SaveAndRunFromTemplateJSONRequestBody = CreateAndRunExperimentFromTemplateAO + +// UpdateExperimentByTemplateJSONRequestBody defines body for UpdateExperimentByTemplate for application/json ContentType. +type UpdateExperimentByTemplateJSONRequestBody = UpdateExperimentFromTemplateAO + +// UpdateExperimentJSONRequestBody defines body for UpdateExperiment for application/json ContentType. +type UpdateExperimentJSONRequestBody = UpdateExperimentAO + +// ExecuteExperimentJSONRequestBody defines body for ExecuteExperiment for application/json ContentType. +type ExecuteExperimentJSONRequestBody = ExecuteExperimentRequestAO + +// CreateLandscapeViewJSONRequestBody defines body for CreateLandscapeView for application/json ContentType. +type CreateLandscapeViewJSONRequestBody = UpsertLandscapeViewAO + +// UpdateLandscapeViewJSONRequestBody defines body for UpdateLandscapeView for application/json ContentType. +type UpdateLandscapeViewJSONRequestBody = UpsertLandscapeViewAO + +// UpsertHubJSONRequestBody defines body for UpsertHub for application/json ContentType. +type UpsertHubJSONRequestBody = UpsertHubAO + +// ConnectionCheckJSONRequestBody defines body for ConnectionCheck for application/json ContentType. +type ConnectionCheckJSONRequestBody = HubConnectionCheckAO + +// UpsertPreflightWebhookJSONRequestBody defines body for UpsertPreflightWebhook for application/json ContentType. +type UpsertPreflightWebhookJSONRequestBody = PreflightWebhookUpsertAO + +// UpsertPreflightActionIntegrationJSONRequestBody defines body for UpsertPreflightActionIntegration for application/json ContentType. +type UpsertPreflightActionIntegrationJSONRequestBody = PreflightActionIntegrationUpsertAO + +// UpsertSlackIntegrationJSONRequestBody defines body for UpsertSlackIntegration for application/json ContentType. +type UpsertSlackIntegrationJSONRequestBody = SlackWebhookUpsertAO + +// UpsertCustomWebhookJSONRequestBody defines body for UpsertCustomWebhook for application/json ContentType. +type UpsertCustomWebhookJSONRequestBody = CustomWebhookUpsertAO + +// UpsertPropertyAssociationJSONRequestBody defines body for UpsertPropertyAssociation for application/json ContentType. +type UpsertPropertyAssociationJSONRequestBody = UpsertPropertyAssociationAO + +// UpsertPropertyDefinitionJSONRequestBody defines body for UpsertPropertyDefinition for application/json ContentType. +type UpsertPropertyDefinitionJSONRequestBody = UpsertPropertyDefinitionAO + +// GetEnvironmentCountsJSONRequestBody defines body for GetEnvironmentCounts for application/json ContentType. +type GetEnvironmentCountsJSONRequestBody = ReportFilterAO + +// GetExperimentCreationsJSONRequestBody defines body for GetExperimentCreations for application/json ContentType. +type GetExperimentCreationsJSONRequestBody = ExperimentReportFilterAO + +// GetExperimentExecutionsJSONRequestBody defines body for GetExperimentExecutions for application/json ContentType. +type GetExperimentExecutionsJSONRequestBody = ExperimentExecutionReportFilterAO + +// GetAverageRiskJSONRequestBody defines body for GetAverageRisk for application/json ContentType. +type GetAverageRiskJSONRequestBody = ServiceRiskReportFilterAO + +// GetRiskByCategoryJSONRequestBody defines body for GetRiskByCategory for application/json ContentType. +type GetRiskByCategoryJSONRequestBody = ServiceRiskReportFilterAO + +// GetRiskDistributionJSONRequestBody defines body for GetRiskDistribution for application/json ContentType. +type GetRiskDistributionJSONRequestBody = ServiceRiskReportFilterAO + +// GetTeamCountsJSONRequestBody defines body for GetTeamCounts for application/json ContentType. +type GetTeamCountsJSONRequestBody = ReportFilterAO + +// GetUserCountsJSONRequestBody defines body for GetUserCounts for application/json ContentType. +type GetUserCountsJSONRequestBody = ReportFilterAO + +// UpsertServiceJSONRequestBody defines body for UpsertService for application/json ContentType. +type UpsertServiceJSONRequestBody = UpsertServiceAO + +// UpsertProfileJSONRequestBody defines body for UpsertProfile for application/json ContentType. +type UpsertProfileJSONRequestBody = UpsertServiceProfileAO + +// LinkCustomExperimentJSONRequestBody defines body for LinkCustomExperiment for application/json ContentType. +type LinkCustomExperimentJSONRequestBody = LinkCustomExperimentRequestAO + +// UpsertProvidedExperimentJSONRequestBody defines body for UpsertProvidedExperiment for application/json ContentType. +type UpsertProvidedExperimentJSONRequestBody = UpsertProvidedExperimentRequestAO + +// MergeServiceVariablesJSONRequestBody defines body for MergeServiceVariables for application/json ContentType. +type MergeServiceVariablesJSONRequestBody MergeServiceVariablesJSONBody + +// SetServiceVariablesJSONRequestBody defines body for SetServiceVariables for application/json ContentType. +type SetServiceVariablesJSONRequestBody SetServiceVariablesJSONBody + +// GetTargetsStats1JSONRequestBody defines body for GetTargetsStats1 for application/json ContentType. +type GetTargetsStats1JSONRequestBody = TargetStatsRequest + +// UpsertTeamJSONRequestBody defines body for UpsertTeam for application/json ContentType. +type UpsertTeamJSONRequestBody = UpsertTeamAO + +// SetTeamEnvironmentsJSONRequestBody defines body for SetTeamEnvironments for application/json ContentType. +type SetTeamEnvironmentsJSONRequestBody = TeamEnvironmentsAO + +// AddTeamEnvironmentsJSONRequestBody defines body for AddTeamEnvironments for application/json ContentType. +type AddTeamEnvironmentsJSONRequestBody = TeamEnvironmentsUpdateAO + +// RemoveTeamEnvironmentsJSONRequestBody defines body for RemoveTeamEnvironments for application/json ContentType. +type RemoveTeamEnvironmentsJSONRequestBody = TeamEnvironmentsUpdateAO + +// SetTeamMembersJSONRequestBody defines body for SetTeamMembers for application/json ContentType. +type SetTeamMembersJSONRequestBody = TeamMembersUpdateAO + +// AddTeamMembersJSONRequestBody defines body for AddTeamMembers for application/json ContentType. +type AddTeamMembersJSONRequestBody = TeamMembersUpdateAO + +// RemoveTeamMembersJSONRequestBody defines body for RemoveTeamMembers for application/json ContentType. +type RemoveTeamMembersJSONRequestBody = TeamMembersRemoveAO + +// InviteUserJSONRequestBody defines body for InviteUser for application/json ContentType. +type InviteUserJSONRequestBody = InviteUsersRequestAO + +// AsExperimentExecutionStepActionAO returns the union data inside the AbstractExperimentExecutionStepAO as a ExperimentExecutionStepActionAO +func (t AbstractExperimentExecutionStepAO) AsExperimentExecutionStepActionAO() (ExperimentExecutionStepActionAO, error) { + var body ExperimentExecutionStepActionAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentExecutionStepActionAO overwrites any union data inside the AbstractExperimentExecutionStepAO as the provided ExperimentExecutionStepActionAO +func (t *AbstractExperimentExecutionStepAO) FromExperimentExecutionStepActionAO(v ExperimentExecutionStepActionAO) error { + t.StepType = "ACTION" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"ACTION"}`)) + t.union = b + return err +} + +// MergeExperimentExecutionStepActionAO performs a merge with any union data inside the AbstractExperimentExecutionStepAO, using the provided ExperimentExecutionStepActionAO +func (t *AbstractExperimentExecutionStepAO) MergeExperimentExecutionStepActionAO(v ExperimentExecutionStepActionAO) error { + t.StepType = "ACTION" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"ACTION"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentExecutionStepWaitAO returns the union data inside the AbstractExperimentExecutionStepAO as a ExperimentExecutionStepWaitAO +func (t AbstractExperimentExecutionStepAO) AsExperimentExecutionStepWaitAO() (ExperimentExecutionStepWaitAO, error) { + var body ExperimentExecutionStepWaitAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentExecutionStepWaitAO overwrites any union data inside the AbstractExperimentExecutionStepAO as the provided ExperimentExecutionStepWaitAO +func (t *AbstractExperimentExecutionStepAO) FromExperimentExecutionStepWaitAO(v ExperimentExecutionStepWaitAO) error { + t.StepType = "WAIT" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"WAIT"}`)) + t.union = b + return err +} + +// MergeExperimentExecutionStepWaitAO performs a merge with any union data inside the AbstractExperimentExecutionStepAO, using the provided ExperimentExecutionStepWaitAO +func (t *AbstractExperimentExecutionStepAO) MergeExperimentExecutionStepWaitAO(v ExperimentExecutionStepWaitAO) error { + t.StepType = "WAIT" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"WAIT"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentExecutionStepServiceValidationAO returns the union data inside the AbstractExperimentExecutionStepAO as a ExperimentExecutionStepServiceValidationAO +func (t AbstractExperimentExecutionStepAO) AsExperimentExecutionStepServiceValidationAO() (ExperimentExecutionStepServiceValidationAO, error) { + var body ExperimentExecutionStepServiceValidationAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentExecutionStepServiceValidationAO overwrites any union data inside the AbstractExperimentExecutionStepAO as the provided ExperimentExecutionStepServiceValidationAO +func (t *AbstractExperimentExecutionStepAO) FromExperimentExecutionStepServiceValidationAO(v ExperimentExecutionStepServiceValidationAO) error { + t.StepType = "SERVICE-VALIDATION" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"SERVICE-VALIDATION"}`)) + t.union = b + return err +} + +// MergeExperimentExecutionStepServiceValidationAO performs a merge with any union data inside the AbstractExperimentExecutionStepAO, using the provided ExperimentExecutionStepServiceValidationAO +func (t *AbstractExperimentExecutionStepAO) MergeExperimentExecutionStepServiceValidationAO(v ExperimentExecutionStepServiceValidationAO) error { + t.StepType = "SERVICE-VALIDATION" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"SERVICE-VALIDATION"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t AbstractExperimentExecutionStepAO) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"stepType"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t AbstractExperimentExecutionStepAO) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "ACTION": + return t.AsExperimentExecutionStepActionAO() + case "SERVICE-VALIDATION": + return t.AsExperimentExecutionStepServiceValidationAO() + case "WAIT": + return t.AsExperimentExecutionStepWaitAO() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t AbstractExperimentExecutionStepAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + if t.CustomLabel != nil { + object["customLabel"], err = json.Marshal(t.CustomLabel) + if err != nil { + return nil, fmt.Errorf("error marshaling 'customLabel': %w", err) + } + } + + if t.Ended != nil { + object["ended"], err = json.Marshal(t.Ended) + if err != nil { + return nil, fmt.Errorf("error marshaling 'ended': %w", err) + } + } + + if t.Id != nil { + object["id"], err = json.Marshal(t.Id) + if err != nil { + return nil, fmt.Errorf("error marshaling 'id': %w", err) + } + } + + if t.IgnoreFailure != nil { + object["ignoreFailure"], err = json.Marshal(t.IgnoreFailure) + if err != nil { + return nil, fmt.Errorf("error marshaling 'ignoreFailure': %w", err) + } + } + + if t.Parameters != nil { + object["parameters"], err = json.Marshal(t.Parameters) + if err != nil { + return nil, fmt.Errorf("error marshaling 'parameters': %w", err) + } + } + + if t.PredecessorId != nil { + object["predecessorId"], err = json.Marshal(t.PredecessorId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'predecessorId': %w", err) + } + } + + if t.Reason != nil { + object["reason"], err = json.Marshal(t.Reason) + if err != nil { + return nil, fmt.Errorf("error marshaling 'reason': %w", err) + } + } + + if t.Started != nil { + object["started"], err = json.Marshal(t.Started) + if err != nil { + return nil, fmt.Errorf("error marshaling 'started': %w", err) + } + } + + if t.State != nil { + object["state"], err = json.Marshal(t.State) + if err != nil { + return nil, fmt.Errorf("error marshaling 'state': %w", err) + } + } + + object["stepType"], err = json.Marshal(t.StepType) + if err != nil { + return nil, fmt.Errorf("error marshaling 'stepType': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *AbstractExperimentExecutionStepAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["customLabel"]; found { + err = json.Unmarshal(raw, &t.CustomLabel) + if err != nil { + return fmt.Errorf("error reading 'customLabel': %w", err) + } + } + + if raw, found := object["ended"]; found { + err = json.Unmarshal(raw, &t.Ended) + if err != nil { + return fmt.Errorf("error reading 'ended': %w", err) + } + } + + if raw, found := object["id"]; found { + err = json.Unmarshal(raw, &t.Id) + if err != nil { + return fmt.Errorf("error reading 'id': %w", err) + } + } + + if raw, found := object["ignoreFailure"]; found { + err = json.Unmarshal(raw, &t.IgnoreFailure) + if err != nil { + return fmt.Errorf("error reading 'ignoreFailure': %w", err) + } + } + + if raw, found := object["parameters"]; found { + err = json.Unmarshal(raw, &t.Parameters) + if err != nil { + return fmt.Errorf("error reading 'parameters': %w", err) + } + } + + if raw, found := object["predecessorId"]; found { + err = json.Unmarshal(raw, &t.PredecessorId) + if err != nil { + return fmt.Errorf("error reading 'predecessorId': %w", err) + } + } + + if raw, found := object["reason"]; found { + err = json.Unmarshal(raw, &t.Reason) + if err != nil { + return fmt.Errorf("error reading 'reason': %w", err) + } + } + + if raw, found := object["started"]; found { + err = json.Unmarshal(raw, &t.Started) + if err != nil { + return fmt.Errorf("error reading 'started': %w", err) + } + } + + if raw, found := object["state"]; found { + err = json.Unmarshal(raw, &t.State) + if err != nil { + return fmt.Errorf("error reading 'state': %w", err) + } + } + + if raw, found := object["stepType"]; found { + err = json.Unmarshal(raw, &t.StepType) + if err != nil { + return fmt.Errorf("error reading 'stepType': %w", err) + } + } + + return err +} + +// AsExperimentStepActionAO returns the union data inside the BaseExperimentStepAO as a ExperimentStepActionAO +func (t BaseExperimentStepAO) AsExperimentStepActionAO() (ExperimentStepActionAO, error) { + var body ExperimentStepActionAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepActionAO overwrites any union data inside the BaseExperimentStepAO as the provided ExperimentStepActionAO +func (t *BaseExperimentStepAO) FromExperimentStepActionAO(v ExperimentStepActionAO) error { + t.Type = "action" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"action"}`)) + t.union = b + return err +} + +// MergeExperimentStepActionAO performs a merge with any union data inside the BaseExperimentStepAO, using the provided ExperimentStepActionAO +func (t *BaseExperimentStepAO) MergeExperimentStepActionAO(v ExperimentStepActionAO) error { + t.Type = "action" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"action"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentStepWaitAO returns the union data inside the BaseExperimentStepAO as a ExperimentStepWaitAO +func (t BaseExperimentStepAO) AsExperimentStepWaitAO() (ExperimentStepWaitAO, error) { + var body ExperimentStepWaitAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepWaitAO overwrites any union data inside the BaseExperimentStepAO as the provided ExperimentStepWaitAO +func (t *BaseExperimentStepAO) FromExperimentStepWaitAO(v ExperimentStepWaitAO) error { + t.Type = "wait" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"wait"}`)) + t.union = b + return err +} + +// MergeExperimentStepWaitAO performs a merge with any union data inside the BaseExperimentStepAO, using the provided ExperimentStepWaitAO +func (t *BaseExperimentStepAO) MergeExperimentStepWaitAO(v ExperimentStepWaitAO) error { + t.Type = "wait" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"wait"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentStepServiceValidationAO returns the union data inside the BaseExperimentStepAO as a ExperimentStepServiceValidationAO +func (t BaseExperimentStepAO) AsExperimentStepServiceValidationAO() (ExperimentStepServiceValidationAO, error) { + var body ExperimentStepServiceValidationAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepServiceValidationAO overwrites any union data inside the BaseExperimentStepAO as the provided ExperimentStepServiceValidationAO +func (t *BaseExperimentStepAO) FromExperimentStepServiceValidationAO(v ExperimentStepServiceValidationAO) error { + t.Type = "service-validation" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"service-validation"}`)) + t.union = b + return err +} + +// MergeExperimentStepServiceValidationAO performs a merge with any union data inside the BaseExperimentStepAO, using the provided ExperimentStepServiceValidationAO +func (t *BaseExperimentStepAO) MergeExperimentStepServiceValidationAO(v ExperimentStepServiceValidationAO) error { + t.Type = "service-validation" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"service-validation"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t BaseExperimentStepAO) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t BaseExperimentStepAO) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "action": + return t.AsExperimentStepActionAO() + case "service-validation": + return t.AsExperimentStepServiceValidationAO() + case "wait": + return t.AsExperimentStepWaitAO() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t BaseExperimentStepAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + if t.CustomLabel != nil { + object["customLabel"], err = json.Marshal(t.CustomLabel) + if err != nil { + return nil, fmt.Errorf("error marshaling 'customLabel': %w", err) + } + } + + if t.IgnoreFailure != nil { + object["ignoreFailure"], err = json.Marshal(t.IgnoreFailure) + if err != nil { + return nil, fmt.Errorf("error marshaling 'ignoreFailure': %w", err) + } + } + + if t.MetricChecks != nil { + object["metricChecks"], err = json.Marshal(t.MetricChecks) + if err != nil { + return nil, fmt.Errorf("error marshaling 'metricChecks': %w", err) + } + } + + if t.MetricQueries != nil { + object["metricQueries"], err = json.Marshal(t.MetricQueries) + if err != nil { + return nil, fmt.Errorf("error marshaling 'metricQueries': %w", err) + } + } + + if t.Parameters != nil { + object["parameters"], err = json.Marshal(t.Parameters) + if err != nil { + return nil, fmt.Errorf("error marshaling 'parameters': %w", err) + } + } + + object["type"], err = json.Marshal(t.Type) + if err != nil { + return nil, fmt.Errorf("error marshaling 'type': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *BaseExperimentStepAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["customLabel"]; found { + err = json.Unmarshal(raw, &t.CustomLabel) + if err != nil { + return fmt.Errorf("error reading 'customLabel': %w", err) + } + } + + if raw, found := object["ignoreFailure"]; found { + err = json.Unmarshal(raw, &t.IgnoreFailure) + if err != nil { + return fmt.Errorf("error reading 'ignoreFailure': %w", err) + } + } + + if raw, found := object["metricChecks"]; found { + err = json.Unmarshal(raw, &t.MetricChecks) + if err != nil { + return fmt.Errorf("error reading 'metricChecks': %w", err) + } + } + + if raw, found := object["metricQueries"]; found { + err = json.Unmarshal(raw, &t.MetricQueries) + if err != nil { + return fmt.Errorf("error reading 'metricQueries': %w", err) + } + } + + if raw, found := object["parameters"]; found { + err = json.Unmarshal(raw, &t.Parameters) + if err != nil { + return fmt.Errorf("error reading 'parameters': %w", err) + } + } + + if raw, found := object["type"]; found { + err = json.Unmarshal(raw, &t.Type) + if err != nil { + return fmt.Errorf("error reading 'type': %w", err) + } + } + + return err +} + +// AsExperimentExecutionStepActionAO returns the union data inside the ExperimentExecutionStepServiceValidationAO as a ExperimentExecutionStepActionAO +func (t ExperimentExecutionStepServiceValidationAO) AsExperimentExecutionStepActionAO() (ExperimentExecutionStepActionAO, error) { + var body ExperimentExecutionStepActionAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentExecutionStepActionAO overwrites any union data inside the ExperimentExecutionStepServiceValidationAO as the provided ExperimentExecutionStepActionAO +func (t *ExperimentExecutionStepServiceValidationAO) FromExperimentExecutionStepActionAO(v ExperimentExecutionStepActionAO) error { + t.StepType = "ACTION" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"ACTION"}`)) + t.union = b + return err +} + +// MergeExperimentExecutionStepActionAO performs a merge with any union data inside the ExperimentExecutionStepServiceValidationAO, using the provided ExperimentExecutionStepActionAO +func (t *ExperimentExecutionStepServiceValidationAO) MergeExperimentExecutionStepActionAO(v ExperimentExecutionStepActionAO) error { + t.StepType = "ACTION" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"ACTION"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentExecutionStepWaitAO returns the union data inside the ExperimentExecutionStepServiceValidationAO as a ExperimentExecutionStepWaitAO +func (t ExperimentExecutionStepServiceValidationAO) AsExperimentExecutionStepWaitAO() (ExperimentExecutionStepWaitAO, error) { + var body ExperimentExecutionStepWaitAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentExecutionStepWaitAO overwrites any union data inside the ExperimentExecutionStepServiceValidationAO as the provided ExperimentExecutionStepWaitAO +func (t *ExperimentExecutionStepServiceValidationAO) FromExperimentExecutionStepWaitAO(v ExperimentExecutionStepWaitAO) error { + t.StepType = "WAIT" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"WAIT"}`)) + t.union = b + return err +} + +// MergeExperimentExecutionStepWaitAO performs a merge with any union data inside the ExperimentExecutionStepServiceValidationAO, using the provided ExperimentExecutionStepWaitAO +func (t *ExperimentExecutionStepServiceValidationAO) MergeExperimentExecutionStepWaitAO(v ExperimentExecutionStepWaitAO) error { + t.StepType = "WAIT" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"WAIT"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentExecutionStepServiceValidationAO returns the union data inside the ExperimentExecutionStepServiceValidationAO as a ExperimentExecutionStepServiceValidationAO +func (t ExperimentExecutionStepServiceValidationAO) AsExperimentExecutionStepServiceValidationAO() (ExperimentExecutionStepServiceValidationAO, error) { + var body ExperimentExecutionStepServiceValidationAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentExecutionStepServiceValidationAO overwrites any union data inside the ExperimentExecutionStepServiceValidationAO as the provided ExperimentExecutionStepServiceValidationAO +func (t *ExperimentExecutionStepServiceValidationAO) FromExperimentExecutionStepServiceValidationAO(v ExperimentExecutionStepServiceValidationAO) error { + t.StepType = "SERVICE-VALIDATION" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"SERVICE-VALIDATION"}`)) + t.union = b + return err +} + +// MergeExperimentExecutionStepServiceValidationAO performs a merge with any union data inside the ExperimentExecutionStepServiceValidationAO, using the provided ExperimentExecutionStepServiceValidationAO +func (t *ExperimentExecutionStepServiceValidationAO) MergeExperimentExecutionStepServiceValidationAO(v ExperimentExecutionStepServiceValidationAO) error { + t.StepType = "SERVICE-VALIDATION" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"stepType":"SERVICE-VALIDATION"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t ExperimentExecutionStepServiceValidationAO) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"stepType"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t ExperimentExecutionStepServiceValidationAO) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "ACTION": + return t.AsExperimentExecutionStepActionAO() + case "SERVICE-VALIDATION": + return t.AsExperimentExecutionStepServiceValidationAO() + case "WAIT": + return t.AsExperimentExecutionStepWaitAO() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t ExperimentExecutionStepServiceValidationAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + if t.CustomLabel != nil { + object["customLabel"], err = json.Marshal(t.CustomLabel) + if err != nil { + return nil, fmt.Errorf("error marshaling 'customLabel': %w", err) + } + } + + if t.Ended != nil { + object["ended"], err = json.Marshal(t.Ended) + if err != nil { + return nil, fmt.Errorf("error marshaling 'ended': %w", err) + } + } + + if t.Id != nil { + object["id"], err = json.Marshal(t.Id) + if err != nil { + return nil, fmt.Errorf("error marshaling 'id': %w", err) + } + } + + if t.IgnoreFailure != nil { + object["ignoreFailure"], err = json.Marshal(t.IgnoreFailure) + if err != nil { + return nil, fmt.Errorf("error marshaling 'ignoreFailure': %w", err) + } + } + + if t.Parameters != nil { + object["parameters"], err = json.Marshal(t.Parameters) + if err != nil { + return nil, fmt.Errorf("error marshaling 'parameters': %w", err) + } + } + + if t.PredecessorId != nil { + object["predecessorId"], err = json.Marshal(t.PredecessorId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'predecessorId': %w", err) + } + } + + if t.Reason != nil { + object["reason"], err = json.Marshal(t.Reason) + if err != nil { + return nil, fmt.Errorf("error marshaling 'reason': %w", err) + } + } + + if t.ServiceId != nil { + object["serviceId"], err = json.Marshal(t.ServiceId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'serviceId': %w", err) + } + } + + if t.Started != nil { + object["started"], err = json.Marshal(t.Started) + if err != nil { + return nil, fmt.Errorf("error marshaling 'started': %w", err) + } + } + + if t.State != nil { + object["state"], err = json.Marshal(t.State) + if err != nil { + return nil, fmt.Errorf("error marshaling 'state': %w", err) + } + } + + object["stepType"], err = json.Marshal(t.StepType) + if err != nil { + return nil, fmt.Errorf("error marshaling 'stepType': %w", err) + } + + if t.Validations != nil { + object["validations"], err = json.Marshal(t.Validations) + if err != nil { + return nil, fmt.Errorf("error marshaling 'validations': %w", err) + } + } + b, err = json.Marshal(object) + return b, err +} + +func (t *ExperimentExecutionStepServiceValidationAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["customLabel"]; found { + err = json.Unmarshal(raw, &t.CustomLabel) + if err != nil { + return fmt.Errorf("error reading 'customLabel': %w", err) + } + } + + if raw, found := object["ended"]; found { + err = json.Unmarshal(raw, &t.Ended) + if err != nil { + return fmt.Errorf("error reading 'ended': %w", err) + } + } + + if raw, found := object["id"]; found { + err = json.Unmarshal(raw, &t.Id) + if err != nil { + return fmt.Errorf("error reading 'id': %w", err) + } + } + + if raw, found := object["ignoreFailure"]; found { + err = json.Unmarshal(raw, &t.IgnoreFailure) + if err != nil { + return fmt.Errorf("error reading 'ignoreFailure': %w", err) + } + } + + if raw, found := object["parameters"]; found { + err = json.Unmarshal(raw, &t.Parameters) + if err != nil { + return fmt.Errorf("error reading 'parameters': %w", err) + } + } + + if raw, found := object["predecessorId"]; found { + err = json.Unmarshal(raw, &t.PredecessorId) + if err != nil { + return fmt.Errorf("error reading 'predecessorId': %w", err) + } + } + + if raw, found := object["reason"]; found { + err = json.Unmarshal(raw, &t.Reason) + if err != nil { + return fmt.Errorf("error reading 'reason': %w", err) + } + } + + if raw, found := object["serviceId"]; found { + err = json.Unmarshal(raw, &t.ServiceId) + if err != nil { + return fmt.Errorf("error reading 'serviceId': %w", err) + } + } + + if raw, found := object["started"]; found { + err = json.Unmarshal(raw, &t.Started) + if err != nil { + return fmt.Errorf("error reading 'started': %w", err) + } + } + + if raw, found := object["state"]; found { + err = json.Unmarshal(raw, &t.State) + if err != nil { + return fmt.Errorf("error reading 'state': %w", err) + } + } + + if raw, found := object["stepType"]; found { + err = json.Unmarshal(raw, &t.StepType) + if err != nil { + return fmt.Errorf("error reading 'stepType': %w", err) + } + } + + if raw, found := object["validations"]; found { + err = json.Unmarshal(raw, &t.Validations) + if err != nil { + return fmt.Errorf("error reading 'validations': %w", err) + } + } + + return err +} + +// AsExperimentExecutionVariableAOValue0 returns the union data inside the ExperimentExecutionVariableAO_Value as a ExperimentExecutionVariableAOValue0 +func (t ExperimentExecutionVariableAO_Value) AsExperimentExecutionVariableAOValue0() (ExperimentExecutionVariableAOValue0, error) { + var body ExperimentExecutionVariableAOValue0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentExecutionVariableAOValue0 overwrites any union data inside the ExperimentExecutionVariableAO_Value as the provided ExperimentExecutionVariableAOValue0 +func (t *ExperimentExecutionVariableAO_Value) FromExperimentExecutionVariableAOValue0(v ExperimentExecutionVariableAOValue0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeExperimentExecutionVariableAOValue0 performs a merge with any union data inside the ExperimentExecutionVariableAO_Value, using the provided ExperimentExecutionVariableAOValue0 +func (t *ExperimentExecutionVariableAO_Value) MergeExperimentExecutionVariableAOValue0(v ExperimentExecutionVariableAOValue0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentExecutionVariableAOValue1 returns the union data inside the ExperimentExecutionVariableAO_Value as a ExperimentExecutionVariableAOValue1 +func (t ExperimentExecutionVariableAO_Value) AsExperimentExecutionVariableAOValue1() (ExperimentExecutionVariableAOValue1, error) { + var body ExperimentExecutionVariableAOValue1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentExecutionVariableAOValue1 overwrites any union data inside the ExperimentExecutionVariableAO_Value as the provided ExperimentExecutionVariableAOValue1 +func (t *ExperimentExecutionVariableAO_Value) FromExperimentExecutionVariableAOValue1(v ExperimentExecutionVariableAOValue1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeExperimentExecutionVariableAOValue1 performs a merge with any union data inside the ExperimentExecutionVariableAO_Value, using the provided ExperimentExecutionVariableAOValue1 +func (t *ExperimentExecutionVariableAO_Value) MergeExperimentExecutionVariableAOValue1(v ExperimentExecutionVariableAOValue1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t ExperimentExecutionVariableAO_Value) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *ExperimentExecutionVariableAO_Value) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsExperimentStepActionAO returns the union data inside the ExperimentStepActionAO as a ExperimentStepActionAO +func (t ExperimentStepActionAO) AsExperimentStepActionAO() (ExperimentStepActionAO, error) { + var body ExperimentStepActionAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepActionAO overwrites any union data inside the ExperimentStepActionAO as the provided ExperimentStepActionAO +func (t *ExperimentStepActionAO) FromExperimentStepActionAO(v ExperimentStepActionAO) error { + t.Type = "action" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"action"}`)) + t.union = b + return err +} + +// MergeExperimentStepActionAO performs a merge with any union data inside the ExperimentStepActionAO, using the provided ExperimentStepActionAO +func (t *ExperimentStepActionAO) MergeExperimentStepActionAO(v ExperimentStepActionAO) error { + t.Type = "action" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"action"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentStepWaitAO returns the union data inside the ExperimentStepActionAO as a ExperimentStepWaitAO +func (t ExperimentStepActionAO) AsExperimentStepWaitAO() (ExperimentStepWaitAO, error) { + var body ExperimentStepWaitAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepWaitAO overwrites any union data inside the ExperimentStepActionAO as the provided ExperimentStepWaitAO +func (t *ExperimentStepActionAO) FromExperimentStepWaitAO(v ExperimentStepWaitAO) error { + t.Type = "wait" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"wait"}`)) + t.union = b + return err +} + +// MergeExperimentStepWaitAO performs a merge with any union data inside the ExperimentStepActionAO, using the provided ExperimentStepWaitAO +func (t *ExperimentStepActionAO) MergeExperimentStepWaitAO(v ExperimentStepWaitAO) error { + t.Type = "wait" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"wait"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentStepServiceValidationAO returns the union data inside the ExperimentStepActionAO as a ExperimentStepServiceValidationAO +func (t ExperimentStepActionAO) AsExperimentStepServiceValidationAO() (ExperimentStepServiceValidationAO, error) { + var body ExperimentStepServiceValidationAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepServiceValidationAO overwrites any union data inside the ExperimentStepActionAO as the provided ExperimentStepServiceValidationAO +func (t *ExperimentStepActionAO) FromExperimentStepServiceValidationAO(v ExperimentStepServiceValidationAO) error { + t.Type = "service-validation" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"service-validation"}`)) + t.union = b + return err +} + +// MergeExperimentStepServiceValidationAO performs a merge with any union data inside the ExperimentStepActionAO, using the provided ExperimentStepServiceValidationAO +func (t *ExperimentStepActionAO) MergeExperimentStepServiceValidationAO(v ExperimentStepServiceValidationAO) error { + t.Type = "service-validation" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"service-validation"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t ExperimentStepActionAO) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t ExperimentStepActionAO) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "action": + return t.AsExperimentStepActionAO() + case "service-validation": + return t.AsExperimentStepServiceValidationAO() + case "wait": + return t.AsExperimentStepWaitAO() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t ExperimentStepActionAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["actionType"], err = json.Marshal(t.ActionType) + if err != nil { + return nil, fmt.Errorf("error marshaling 'actionType': %w", err) + } + + if t.CustomLabel != nil { + object["customLabel"], err = json.Marshal(t.CustomLabel) + if err != nil { + return nil, fmt.Errorf("error marshaling 'customLabel': %w", err) + } + } + + if t.IgnoreFailure != nil { + object["ignoreFailure"], err = json.Marshal(t.IgnoreFailure) + if err != nil { + return nil, fmt.Errorf("error marshaling 'ignoreFailure': %w", err) + } + } + + if t.MetricChecks != nil { + object["metricChecks"], err = json.Marshal(t.MetricChecks) + if err != nil { + return nil, fmt.Errorf("error marshaling 'metricChecks': %w", err) + } + } + + if t.MetricQueries != nil { + object["metricQueries"], err = json.Marshal(t.MetricQueries) + if err != nil { + return nil, fmt.Errorf("error marshaling 'metricQueries': %w", err) + } + } + + if t.Parameters != nil { + object["parameters"], err = json.Marshal(t.Parameters) + if err != nil { + return nil, fmt.Errorf("error marshaling 'parameters': %w", err) + } + } + + if t.Radius != nil { + object["radius"], err = json.Marshal(t.Radius) + if err != nil { + return nil, fmt.Errorf("error marshaling 'radius': %w", err) + } + } + + object["type"], err = json.Marshal(t.Type) + if err != nil { + return nil, fmt.Errorf("error marshaling 'type': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *ExperimentStepActionAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["actionType"]; found { + err = json.Unmarshal(raw, &t.ActionType) + if err != nil { + return fmt.Errorf("error reading 'actionType': %w", err) + } + } + + if raw, found := object["customLabel"]; found { + err = json.Unmarshal(raw, &t.CustomLabel) + if err != nil { + return fmt.Errorf("error reading 'customLabel': %w", err) + } + } + + if raw, found := object["ignoreFailure"]; found { + err = json.Unmarshal(raw, &t.IgnoreFailure) + if err != nil { + return fmt.Errorf("error reading 'ignoreFailure': %w", err) + } + } + + if raw, found := object["metricChecks"]; found { + err = json.Unmarshal(raw, &t.MetricChecks) + if err != nil { + return fmt.Errorf("error reading 'metricChecks': %w", err) + } + } + + if raw, found := object["metricQueries"]; found { + err = json.Unmarshal(raw, &t.MetricQueries) + if err != nil { + return fmt.Errorf("error reading 'metricQueries': %w", err) + } + } + + if raw, found := object["parameters"]; found { + err = json.Unmarshal(raw, &t.Parameters) + if err != nil { + return fmt.Errorf("error reading 'parameters': %w", err) + } + } + + if raw, found := object["radius"]; found { + err = json.Unmarshal(raw, &t.Radius) + if err != nil { + return fmt.Errorf("error reading 'radius': %w", err) + } + } + + if raw, found := object["type"]; found { + err = json.Unmarshal(raw, &t.Type) + if err != nil { + return fmt.Errorf("error reading 'type': %w", err) + } + } + + return err +} + +// AsExperimentStepActionAO returns the union data inside the ExperimentStepServiceValidationAO as a ExperimentStepActionAO +func (t ExperimentStepServiceValidationAO) AsExperimentStepActionAO() (ExperimentStepActionAO, error) { + var body ExperimentStepActionAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepActionAO overwrites any union data inside the ExperimentStepServiceValidationAO as the provided ExperimentStepActionAO +func (t *ExperimentStepServiceValidationAO) FromExperimentStepActionAO(v ExperimentStepActionAO) error { + t.Type = "action" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"action"}`)) + t.union = b + return err +} + +// MergeExperimentStepActionAO performs a merge with any union data inside the ExperimentStepServiceValidationAO, using the provided ExperimentStepActionAO +func (t *ExperimentStepServiceValidationAO) MergeExperimentStepActionAO(v ExperimentStepActionAO) error { + t.Type = "action" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"action"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentStepWaitAO returns the union data inside the ExperimentStepServiceValidationAO as a ExperimentStepWaitAO +func (t ExperimentStepServiceValidationAO) AsExperimentStepWaitAO() (ExperimentStepWaitAO, error) { + var body ExperimentStepWaitAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepWaitAO overwrites any union data inside the ExperimentStepServiceValidationAO as the provided ExperimentStepWaitAO +func (t *ExperimentStepServiceValidationAO) FromExperimentStepWaitAO(v ExperimentStepWaitAO) error { + t.Type = "wait" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"wait"}`)) + t.union = b + return err +} + +// MergeExperimentStepWaitAO performs a merge with any union data inside the ExperimentStepServiceValidationAO, using the provided ExperimentStepWaitAO +func (t *ExperimentStepServiceValidationAO) MergeExperimentStepWaitAO(v ExperimentStepWaitAO) error { + t.Type = "wait" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"wait"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExperimentStepServiceValidationAO returns the union data inside the ExperimentStepServiceValidationAO as a ExperimentStepServiceValidationAO +func (t ExperimentStepServiceValidationAO) AsExperimentStepServiceValidationAO() (ExperimentStepServiceValidationAO, error) { + var body ExperimentStepServiceValidationAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExperimentStepServiceValidationAO overwrites any union data inside the ExperimentStepServiceValidationAO as the provided ExperimentStepServiceValidationAO +func (t *ExperimentStepServiceValidationAO) FromExperimentStepServiceValidationAO(v ExperimentStepServiceValidationAO) error { + t.Type = "service-validation" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"service-validation"}`)) + t.union = b + return err +} + +// MergeExperimentStepServiceValidationAO performs a merge with any union data inside the ExperimentStepServiceValidationAO, using the provided ExperimentStepServiceValidationAO +func (t *ExperimentStepServiceValidationAO) MergeExperimentStepServiceValidationAO(v ExperimentStepServiceValidationAO) error { + t.Type = "service-validation" + b, err := json.Marshal(v) + if err != nil { + return err + } + b, err = runtime.JSONMerge(b, []byte(`{"type":"service-validation"}`)) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t ExperimentStepServiceValidationAO) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t ExperimentStepServiceValidationAO) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "action": + return t.AsExperimentStepActionAO() + case "service-validation": + return t.AsExperimentStepServiceValidationAO() + case "wait": + return t.AsExperimentStepWaitAO() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t ExperimentStepServiceValidationAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + if t.CustomLabel != nil { + object["customLabel"], err = json.Marshal(t.CustomLabel) + if err != nil { + return nil, fmt.Errorf("error marshaling 'customLabel': %w", err) + } + } + + if t.IgnoreFailure != nil { + object["ignoreFailure"], err = json.Marshal(t.IgnoreFailure) + if err != nil { + return nil, fmt.Errorf("error marshaling 'ignoreFailure': %w", err) + } + } + + if t.MetricChecks != nil { + object["metricChecks"], err = json.Marshal(t.MetricChecks) + if err != nil { + return nil, fmt.Errorf("error marshaling 'metricChecks': %w", err) + } + } + + if t.MetricQueries != nil { + object["metricQueries"], err = json.Marshal(t.MetricQueries) + if err != nil { + return nil, fmt.Errorf("error marshaling 'metricQueries': %w", err) + } + } + + if t.Parameters != nil { + object["parameters"], err = json.Marshal(t.Parameters) + if err != nil { + return nil, fmt.Errorf("error marshaling 'parameters': %w", err) + } + } + + object["serviceId"], err = json.Marshal(t.ServiceId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'serviceId': %w", err) + } + + object["type"], err = json.Marshal(t.Type) + if err != nil { + return nil, fmt.Errorf("error marshaling 'type': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *ExperimentStepServiceValidationAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["customLabel"]; found { + err = json.Unmarshal(raw, &t.CustomLabel) + if err != nil { + return fmt.Errorf("error reading 'customLabel': %w", err) + } + } + + if raw, found := object["ignoreFailure"]; found { + err = json.Unmarshal(raw, &t.IgnoreFailure) + if err != nil { + return fmt.Errorf("error reading 'ignoreFailure': %w", err) + } + } + + if raw, found := object["metricChecks"]; found { + err = json.Unmarshal(raw, &t.MetricChecks) + if err != nil { + return fmt.Errorf("error reading 'metricChecks': %w", err) + } + } + + if raw, found := object["metricQueries"]; found { + err = json.Unmarshal(raw, &t.MetricQueries) + if err != nil { + return fmt.Errorf("error reading 'metricQueries': %w", err) + } + } + + if raw, found := object["parameters"]; found { + err = json.Unmarshal(raw, &t.Parameters) + if err != nil { + return fmt.Errorf("error reading 'parameters': %w", err) + } + } + + if raw, found := object["serviceId"]; found { + err = json.Unmarshal(raw, &t.ServiceId) + if err != nil { + return fmt.Errorf("error reading 'serviceId': %w", err) + } + } + + if raw, found := object["type"]; found { + err = json.Unmarshal(raw, &t.Type) + if err != nil { + return fmt.Errorf("error reading 'type': %w", err) + } + } + + return err +} + +// AsMetricValueAO returns the union data inside the MetricCheckAO_B as a MetricValueAO +func (t MetricCheckAO_B) AsMetricValueAO() (MetricValueAO, error) { + var body MetricValueAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromMetricValueAO overwrites any union data inside the MetricCheckAO_B as the provided MetricValueAO +func (t *MetricCheckAO_B) FromMetricValueAO(v MetricValueAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeMetricValueAO performs a merge with any union data inside the MetricCheckAO_B, using the provided MetricValueAO +func (t *MetricCheckAO_B) MergeMetricValueAO(v MetricValueAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsScalarValueAO returns the union data inside the MetricCheckAO_B as a ScalarValueAO +func (t MetricCheckAO_B) AsScalarValueAO() (ScalarValueAO, error) { + var body ScalarValueAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromScalarValueAO overwrites any union data inside the MetricCheckAO_B as the provided ScalarValueAO +func (t *MetricCheckAO_B) FromScalarValueAO(v ScalarValueAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeScalarValueAO performs a merge with any union data inside the MetricCheckAO_B, using the provided ScalarValueAO +func (t *MetricCheckAO_B) MergeScalarValueAO(v ScalarValueAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsVariableValueAO returns the union data inside the MetricCheckAO_B as a VariableValueAO +func (t MetricCheckAO_B) AsVariableValueAO() (VariableValueAO, error) { + var body VariableValueAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromVariableValueAO overwrites any union data inside the MetricCheckAO_B as the provided VariableValueAO +func (t *MetricCheckAO_B) FromVariableValueAO(v VariableValueAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeVariableValueAO performs a merge with any union data inside the MetricCheckAO_B, using the provided VariableValueAO +func (t *MetricCheckAO_B) MergeVariableValueAO(v VariableValueAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t MetricCheckAO_B) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *MetricCheckAO_B) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the NegationTargetPredicateAO as a NegationTargetPredicateAO +func (t NegationTargetPredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided NegationTargetPredicateAO +func (t *NegationTargetPredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided NegationTargetPredicateAO +func (t *NegationTargetPredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the NegationTargetPredicateAO as a QueryLanguagePredicateAO +func (t NegationTargetPredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided QueryLanguagePredicateAO +func (t *NegationTargetPredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided QueryLanguagePredicateAO +func (t *NegationTargetPredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the NegationTargetPredicateAO as a TargetAgentIdPredicateAO +func (t NegationTargetPredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided TargetAgentIdPredicateAO +func (t *NegationTargetPredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided TargetAgentIdPredicateAO +func (t *NegationTargetPredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the NegationTargetPredicateAO as a TargetAttributeKeyCountPredicateAO +func (t NegationTargetPredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *NegationTargetPredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *NegationTargetPredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the NegationTargetPredicateAO as a TargetAttributeKeyPredicateAO +func (t NegationTargetPredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *NegationTargetPredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *NegationTargetPredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the NegationTargetPredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t NegationTargetPredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *NegationTargetPredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *NegationTargetPredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the NegationTargetPredicateAO as a TargetAttributeKeyValuePredicateAO +func (t NegationTargetPredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *NegationTargetPredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *NegationTargetPredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the NegationTargetPredicateAO as a TargetNamePredicateAO +func (t NegationTargetPredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided TargetNamePredicateAO +func (t *NegationTargetPredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided TargetNamePredicateAO +func (t *NegationTargetPredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the NegationTargetPredicateAO as a TargetTypePredicateAO +func (t NegationTargetPredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the NegationTargetPredicateAO as the provided TargetTypePredicateAO +func (t *NegationTargetPredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the NegationTargetPredicateAO, using the provided TargetTypePredicateAO +func (t *NegationTargetPredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t NegationTargetPredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + if t.Not != nil { + object["not"], err = json.Marshal(t.Not) + if err != nil { + return nil, fmt.Errorf("error marshaling 'not': %w", err) + } + } + b, err = json.Marshal(object) + return b, err +} + +func (t *NegationTargetPredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["not"]; found { + err = json.Unmarshal(raw, &t.Not) + if err != nil { + return fmt.Errorf("error reading 'not': %w", err) + } + } + + return err +} + +// AsAccessTokenPrincipalAL returns the union data inside the PrincipalAL as a AccessTokenPrincipalAL +func (t PrincipalAL) AsAccessTokenPrincipalAL() (AccessTokenPrincipalAL, error) { + var body AccessTokenPrincipalAL + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromAccessTokenPrincipalAL overwrites any union data inside the PrincipalAL as the provided AccessTokenPrincipalAL +func (t *PrincipalAL) FromAccessTokenPrincipalAL(v AccessTokenPrincipalAL) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeAccessTokenPrincipalAL performs a merge with any union data inside the PrincipalAL, using the provided AccessTokenPrincipalAL +func (t *PrincipalAL) MergeAccessTokenPrincipalAL(v AccessTokenPrincipalAL) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsBatchPrincipalAL returns the union data inside the PrincipalAL as a BatchPrincipalAL +func (t PrincipalAL) AsBatchPrincipalAL() (BatchPrincipalAL, error) { + var body BatchPrincipalAL + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromBatchPrincipalAL overwrites any union data inside the PrincipalAL as the provided BatchPrincipalAL +func (t *PrincipalAL) FromBatchPrincipalAL(v BatchPrincipalAL) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeBatchPrincipalAL performs a merge with any union data inside the PrincipalAL, using the provided BatchPrincipalAL +func (t *PrincipalAL) MergeBatchPrincipalAL(v BatchPrincipalAL) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsUserPrincipalAL returns the union data inside the PrincipalAL as a UserPrincipalAL +func (t PrincipalAL) AsUserPrincipalAL() (UserPrincipalAL, error) { + var body UserPrincipalAL + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromUserPrincipalAL overwrites any union data inside the PrincipalAL as the provided UserPrincipalAL +func (t *PrincipalAL) FromUserPrincipalAL(v UserPrincipalAL) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeUserPrincipalAL performs a merge with any union data inside the PrincipalAL, using the provided UserPrincipalAL +func (t *PrincipalAL) MergeUserPrincipalAL(v UserPrincipalAL) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t PrincipalAL) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["principalType"], err = json.Marshal(t.PrincipalType) + if err != nil { + return nil, fmt.Errorf("error marshaling 'principalType': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *PrincipalAL) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["principalType"]; found { + err = json.Unmarshal(raw, &t.PrincipalType) + if err != nil { + return fmt.Errorf("error reading 'principalType': %w", err) + } + } + + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the QueryLanguagePredicateAO as a NegationTargetPredicateAO +func (t QueryLanguagePredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided NegationTargetPredicateAO +func (t *QueryLanguagePredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided NegationTargetPredicateAO +func (t *QueryLanguagePredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the QueryLanguagePredicateAO as a QueryLanguagePredicateAO +func (t QueryLanguagePredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided QueryLanguagePredicateAO +func (t *QueryLanguagePredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided QueryLanguagePredicateAO +func (t *QueryLanguagePredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the QueryLanguagePredicateAO as a TargetAgentIdPredicateAO +func (t QueryLanguagePredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided TargetAgentIdPredicateAO +func (t *QueryLanguagePredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided TargetAgentIdPredicateAO +func (t *QueryLanguagePredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the QueryLanguagePredicateAO as a TargetAttributeKeyCountPredicateAO +func (t QueryLanguagePredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *QueryLanguagePredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *QueryLanguagePredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the QueryLanguagePredicateAO as a TargetAttributeKeyPredicateAO +func (t QueryLanguagePredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *QueryLanguagePredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *QueryLanguagePredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the QueryLanguagePredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t QueryLanguagePredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *QueryLanguagePredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *QueryLanguagePredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the QueryLanguagePredicateAO as a TargetAttributeKeyValuePredicateAO +func (t QueryLanguagePredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *QueryLanguagePredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *QueryLanguagePredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the QueryLanguagePredicateAO as a TargetNamePredicateAO +func (t QueryLanguagePredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided TargetNamePredicateAO +func (t *QueryLanguagePredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided TargetNamePredicateAO +func (t *QueryLanguagePredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the QueryLanguagePredicateAO as a TargetTypePredicateAO +func (t QueryLanguagePredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the QueryLanguagePredicateAO as the provided TargetTypePredicateAO +func (t *QueryLanguagePredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the QueryLanguagePredicateAO, using the provided TargetTypePredicateAO +func (t *QueryLanguagePredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t QueryLanguagePredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["query"], err = json.Marshal(t.Query) + if err != nil { + return nil, fmt.Errorf("error marshaling 'query': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *QueryLanguagePredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["query"]; found { + err = json.Unmarshal(raw, &t.Query) + if err != nil { + return fmt.Errorf("error reading 'query': %w", err) + } + } + + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the TargetAgentIdPredicateAO as a NegationTargetPredicateAO +func (t TargetAgentIdPredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided NegationTargetPredicateAO +func (t *TargetAgentIdPredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided NegationTargetPredicateAO +func (t *TargetAgentIdPredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the TargetAgentIdPredicateAO as a QueryLanguagePredicateAO +func (t TargetAgentIdPredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided QueryLanguagePredicateAO +func (t *TargetAgentIdPredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided QueryLanguagePredicateAO +func (t *TargetAgentIdPredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the TargetAgentIdPredicateAO as a TargetAgentIdPredicateAO +func (t TargetAgentIdPredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided TargetAgentIdPredicateAO +func (t *TargetAgentIdPredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided TargetAgentIdPredicateAO +func (t *TargetAgentIdPredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the TargetAgentIdPredicateAO as a TargetAttributeKeyCountPredicateAO +func (t TargetAgentIdPredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAgentIdPredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAgentIdPredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the TargetAgentIdPredicateAO as a TargetAttributeKeyPredicateAO +func (t TargetAgentIdPredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *TargetAgentIdPredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *TargetAgentIdPredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the TargetAgentIdPredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t TargetAgentIdPredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAgentIdPredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAgentIdPredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the TargetAgentIdPredicateAO as a TargetAttributeKeyValuePredicateAO +func (t TargetAgentIdPredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAgentIdPredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAgentIdPredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the TargetAgentIdPredicateAO as a TargetNamePredicateAO +func (t TargetAgentIdPredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided TargetNamePredicateAO +func (t *TargetAgentIdPredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided TargetNamePredicateAO +func (t *TargetAgentIdPredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the TargetAgentIdPredicateAO as a TargetTypePredicateAO +func (t TargetAgentIdPredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the TargetAgentIdPredicateAO as the provided TargetTypePredicateAO +func (t *TargetAgentIdPredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the TargetAgentIdPredicateAO, using the provided TargetTypePredicateAO +func (t *TargetAgentIdPredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t TargetAgentIdPredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["agentId"], err = json.Marshal(t.AgentId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'agentId': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *TargetAgentIdPredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["agentId"]; found { + err = json.Unmarshal(raw, &t.AgentId) + if err != nil { + return fmt.Errorf("error reading 'agentId': %w", err) + } + } + + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a NegationTargetPredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided NegationTargetPredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided NegationTargetPredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a QueryLanguagePredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided QueryLanguagePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided QueryLanguagePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a TargetAgentIdPredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided TargetAgentIdPredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided TargetAgentIdPredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a TargetAttributeKeyCountPredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a TargetAttributeKeyPredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a TargetAttributeKeyValuePredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a TargetNamePredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided TargetNamePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided TargetNamePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the TargetAttributeKeyCountPredicateAO as a TargetTypePredicateAO +func (t TargetAttributeKeyCountPredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the TargetAttributeKeyCountPredicateAO as the provided TargetTypePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the TargetAttributeKeyCountPredicateAO, using the provided TargetTypePredicateAO +func (t *TargetAttributeKeyCountPredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t TargetAttributeKeyCountPredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["key"], err = json.Marshal(t.Key) + if err != nil { + return nil, fmt.Errorf("error marshaling 'key': %w", err) + } + + object["value"], err = json.Marshal(t.Value) + if err != nil { + return nil, fmt.Errorf("error marshaling 'value': %w", err) + } + + object["valueCountOperator"], err = json.Marshal(t.ValueCountOperator) + if err != nil { + return nil, fmt.Errorf("error marshaling 'valueCountOperator': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *TargetAttributeKeyCountPredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["key"]; found { + err = json.Unmarshal(raw, &t.Key) + if err != nil { + return fmt.Errorf("error reading 'key': %w", err) + } + } + + if raw, found := object["value"]; found { + err = json.Unmarshal(raw, &t.Value) + if err != nil { + return fmt.Errorf("error reading 'value': %w", err) + } + } + + if raw, found := object["valueCountOperator"]; found { + err = json.Unmarshal(raw, &t.ValueCountOperator) + if err != nil { + return fmt.Errorf("error reading 'valueCountOperator': %w", err) + } + } + + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a NegationTargetPredicateAO +func (t TargetAttributeKeyPredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided NegationTargetPredicateAO +func (t *TargetAttributeKeyPredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided NegationTargetPredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a QueryLanguagePredicateAO +func (t TargetAttributeKeyPredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided QueryLanguagePredicateAO +func (t *TargetAttributeKeyPredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided QueryLanguagePredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a TargetAgentIdPredicateAO +func (t TargetAttributeKeyPredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided TargetAgentIdPredicateAO +func (t *TargetAttributeKeyPredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided TargetAgentIdPredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a TargetAttributeKeyCountPredicateAO +func (t TargetAttributeKeyPredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAttributeKeyPredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a TargetAttributeKeyPredicateAO +func (t TargetAttributeKeyPredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *TargetAttributeKeyPredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t TargetAttributeKeyPredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAttributeKeyPredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a TargetAttributeKeyValuePredicateAO +func (t TargetAttributeKeyPredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAttributeKeyPredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a TargetNamePredicateAO +func (t TargetAttributeKeyPredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided TargetNamePredicateAO +func (t *TargetAttributeKeyPredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided TargetNamePredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the TargetAttributeKeyPredicateAO as a TargetTypePredicateAO +func (t TargetAttributeKeyPredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the TargetAttributeKeyPredicateAO as the provided TargetTypePredicateAO +func (t *TargetAttributeKeyPredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the TargetAttributeKeyPredicateAO, using the provided TargetTypePredicateAO +func (t *TargetAttributeKeyPredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t TargetAttributeKeyPredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["key"], err = json.Marshal(t.Key) + if err != nil { + return nil, fmt.Errorf("error marshaling 'key': %w", err) + } + + object["operator"], err = json.Marshal(t.Operator) + if err != nil { + return nil, fmt.Errorf("error marshaling 'operator': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *TargetAttributeKeyPredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["key"]; found { + err = json.Unmarshal(raw, &t.Key) + if err != nil { + return fmt.Errorf("error reading 'key': %w", err) + } + } + + if raw, found := object["operator"]; found { + err = json.Unmarshal(raw, &t.Operator) + if err != nil { + return fmt.Errorf("error reading 'operator': %w", err) + } + } + + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a NegationTargetPredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided NegationTargetPredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided NegationTargetPredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a QueryLanguagePredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided QueryLanguagePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided QueryLanguagePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a TargetAgentIdPredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided TargetAgentIdPredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided TargetAgentIdPredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a TargetAttributeKeyCountPredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a TargetAttributeKeyPredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a TargetAttributeKeyValuePredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a TargetNamePredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided TargetNamePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided TargetNamePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the TargetAttributeKeyPresencePredicateAO as a TargetTypePredicateAO +func (t TargetAttributeKeyPresencePredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the TargetAttributeKeyPresencePredicateAO as the provided TargetTypePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the TargetAttributeKeyPresencePredicateAO, using the provided TargetTypePredicateAO +func (t *TargetAttributeKeyPresencePredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t TargetAttributeKeyPresencePredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["key"], err = json.Marshal(t.Key) + if err != nil { + return nil, fmt.Errorf("error marshaling 'key': %w", err) + } + + object["presenceOperator"], err = json.Marshal(t.PresenceOperator) + if err != nil { + return nil, fmt.Errorf("error marshaling 'presenceOperator': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *TargetAttributeKeyPresencePredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["key"]; found { + err = json.Unmarshal(raw, &t.Key) + if err != nil { + return fmt.Errorf("error reading 'key': %w", err) + } + } + + if raw, found := object["presenceOperator"]; found { + err = json.Unmarshal(raw, &t.PresenceOperator) + if err != nil { + return fmt.Errorf("error reading 'presenceOperator': %w", err) + } + } + + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a NegationTargetPredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided NegationTargetPredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided NegationTargetPredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a QueryLanguagePredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided QueryLanguagePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided QueryLanguagePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a TargetAgentIdPredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided TargetAgentIdPredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided TargetAgentIdPredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a TargetAttributeKeyCountPredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a TargetAttributeKeyPredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a TargetAttributeKeyValuePredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a TargetNamePredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided TargetNamePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided TargetNamePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the TargetAttributeKeyValuePredicateAO as a TargetTypePredicateAO +func (t TargetAttributeKeyValuePredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the TargetAttributeKeyValuePredicateAO as the provided TargetTypePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the TargetAttributeKeyValuePredicateAO, using the provided TargetTypePredicateAO +func (t *TargetAttributeKeyValuePredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t TargetAttributeKeyValuePredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["key"], err = json.Marshal(t.Key) + if err != nil { + return nil, fmt.Errorf("error marshaling 'key': %w", err) + } + + object["operator"], err = json.Marshal(t.Operator) + if err != nil { + return nil, fmt.Errorf("error marshaling 'operator': %w", err) + } + + if t.Values != nil { + object["values"], err = json.Marshal(t.Values) + if err != nil { + return nil, fmt.Errorf("error marshaling 'values': %w", err) + } + } + b, err = json.Marshal(object) + return b, err +} + +func (t *TargetAttributeKeyValuePredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["key"]; found { + err = json.Unmarshal(raw, &t.Key) + if err != nil { + return fmt.Errorf("error reading 'key': %w", err) + } + } + + if raw, found := object["operator"]; found { + err = json.Unmarshal(raw, &t.Operator) + if err != nil { + return fmt.Errorf("error reading 'operator': %w", err) + } + } + + if raw, found := object["values"]; found { + err = json.Unmarshal(raw, &t.Values) + if err != nil { + return fmt.Errorf("error reading 'values': %w", err) + } + } + + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the TargetNamePredicateAO as a NegationTargetPredicateAO +func (t TargetNamePredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided NegationTargetPredicateAO +func (t *TargetNamePredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided NegationTargetPredicateAO +func (t *TargetNamePredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the TargetNamePredicateAO as a QueryLanguagePredicateAO +func (t TargetNamePredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided QueryLanguagePredicateAO +func (t *TargetNamePredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided QueryLanguagePredicateAO +func (t *TargetNamePredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the TargetNamePredicateAO as a TargetAgentIdPredicateAO +func (t TargetNamePredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided TargetAgentIdPredicateAO +func (t *TargetNamePredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided TargetAgentIdPredicateAO +func (t *TargetNamePredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the TargetNamePredicateAO as a TargetAttributeKeyCountPredicateAO +func (t TargetNamePredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *TargetNamePredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *TargetNamePredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the TargetNamePredicateAO as a TargetAttributeKeyPredicateAO +func (t TargetNamePredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *TargetNamePredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *TargetNamePredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the TargetNamePredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t TargetNamePredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetNamePredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetNamePredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the TargetNamePredicateAO as a TargetAttributeKeyValuePredicateAO +func (t TargetNamePredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *TargetNamePredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *TargetNamePredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the TargetNamePredicateAO as a TargetNamePredicateAO +func (t TargetNamePredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided TargetNamePredicateAO +func (t *TargetNamePredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided TargetNamePredicateAO +func (t *TargetNamePredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the TargetNamePredicateAO as a TargetTypePredicateAO +func (t TargetNamePredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the TargetNamePredicateAO as the provided TargetTypePredicateAO +func (t *TargetNamePredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the TargetNamePredicateAO, using the provided TargetTypePredicateAO +func (t *TargetNamePredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t TargetNamePredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + object["name"], err = json.Marshal(t.Name) + if err != nil { + return nil, fmt.Errorf("error marshaling 'name': %w", err) + } + + b, err = json.Marshal(object) + return b, err +} + +func (t *TargetNamePredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["name"]; found { + err = json.Unmarshal(raw, &t.Name) + if err != nil { + return fmt.Errorf("error reading 'name': %w", err) + } + } + + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the TargetPredicateAO as a NegationTargetPredicateAO +func (t TargetPredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the TargetPredicateAO as the provided NegationTargetPredicateAO +func (t *TargetPredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided NegationTargetPredicateAO +func (t *TargetPredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the TargetPredicateAO as a QueryLanguagePredicateAO +func (t TargetPredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the TargetPredicateAO as the provided QueryLanguagePredicateAO +func (t *TargetPredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided QueryLanguagePredicateAO +func (t *TargetPredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the TargetPredicateAO as a TargetAgentIdPredicateAO +func (t TargetPredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the TargetPredicateAO as the provided TargetAgentIdPredicateAO +func (t *TargetPredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided TargetAgentIdPredicateAO +func (t *TargetPredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the TargetPredicateAO as a TargetAttributeKeyCountPredicateAO +func (t TargetPredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the TargetPredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *TargetPredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *TargetPredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the TargetPredicateAO as a TargetAttributeKeyPredicateAO +func (t TargetPredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the TargetPredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *TargetPredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *TargetPredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the TargetPredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t TargetPredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the TargetPredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetPredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetPredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the TargetPredicateAO as a TargetAttributeKeyValuePredicateAO +func (t TargetPredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the TargetPredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *TargetPredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *TargetPredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the TargetPredicateAO as a TargetNamePredicateAO +func (t TargetPredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the TargetPredicateAO as the provided TargetNamePredicateAO +func (t *TargetPredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided TargetNamePredicateAO +func (t *TargetPredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the TargetPredicateAO as a TargetTypePredicateAO +func (t TargetPredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the TargetPredicateAO as the provided TargetTypePredicateAO +func (t *TargetPredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the TargetPredicateAO, using the provided TargetTypePredicateAO +func (t *TargetPredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t TargetPredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *TargetPredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// AsNegationTargetPredicateAO returns the union data inside the TargetTypePredicateAO as a NegationTargetPredicateAO +func (t TargetTypePredicateAO) AsNegationTargetPredicateAO() (NegationTargetPredicateAO, error) { + var body NegationTargetPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromNegationTargetPredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided NegationTargetPredicateAO +func (t *TargetTypePredicateAO) FromNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeNegationTargetPredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided NegationTargetPredicateAO +func (t *TargetTypePredicateAO) MergeNegationTargetPredicateAO(v NegationTargetPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsQueryLanguagePredicateAO returns the union data inside the TargetTypePredicateAO as a QueryLanguagePredicateAO +func (t TargetTypePredicateAO) AsQueryLanguagePredicateAO() (QueryLanguagePredicateAO, error) { + var body QueryLanguagePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromQueryLanguagePredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided QueryLanguagePredicateAO +func (t *TargetTypePredicateAO) FromQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeQueryLanguagePredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided QueryLanguagePredicateAO +func (t *TargetTypePredicateAO) MergeQueryLanguagePredicateAO(v QueryLanguagePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAgentIdPredicateAO returns the union data inside the TargetTypePredicateAO as a TargetAgentIdPredicateAO +func (t TargetTypePredicateAO) AsTargetAgentIdPredicateAO() (TargetAgentIdPredicateAO, error) { + var body TargetAgentIdPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAgentIdPredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided TargetAgentIdPredicateAO +func (t *TargetTypePredicateAO) FromTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAgentIdPredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided TargetAgentIdPredicateAO +func (t *TargetTypePredicateAO) MergeTargetAgentIdPredicateAO(v TargetAgentIdPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyCountPredicateAO returns the union data inside the TargetTypePredicateAO as a TargetAttributeKeyCountPredicateAO +func (t TargetTypePredicateAO) AsTargetAttributeKeyCountPredicateAO() (TargetAttributeKeyCountPredicateAO, error) { + var body TargetAttributeKeyCountPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyCountPredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided TargetAttributeKeyCountPredicateAO +func (t *TargetTypePredicateAO) FromTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyCountPredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided TargetAttributeKeyCountPredicateAO +func (t *TargetTypePredicateAO) MergeTargetAttributeKeyCountPredicateAO(v TargetAttributeKeyCountPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPredicateAO returns the union data inside the TargetTypePredicateAO as a TargetAttributeKeyPredicateAO +func (t TargetTypePredicateAO) AsTargetAttributeKeyPredicateAO() (TargetAttributeKeyPredicateAO, error) { + var body TargetAttributeKeyPredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided TargetAttributeKeyPredicateAO +func (t *TargetTypePredicateAO) FromTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided TargetAttributeKeyPredicateAO +func (t *TargetTypePredicateAO) MergeTargetAttributeKeyPredicateAO(v TargetAttributeKeyPredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyPresencePredicateAO returns the union data inside the TargetTypePredicateAO as a TargetAttributeKeyPresencePredicateAO +func (t TargetTypePredicateAO) AsTargetAttributeKeyPresencePredicateAO() (TargetAttributeKeyPresencePredicateAO, error) { + var body TargetAttributeKeyPresencePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyPresencePredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetTypePredicateAO) FromTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyPresencePredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided TargetAttributeKeyPresencePredicateAO +func (t *TargetTypePredicateAO) MergeTargetAttributeKeyPresencePredicateAO(v TargetAttributeKeyPresencePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetAttributeKeyValuePredicateAO returns the union data inside the TargetTypePredicateAO as a TargetAttributeKeyValuePredicateAO +func (t TargetTypePredicateAO) AsTargetAttributeKeyValuePredicateAO() (TargetAttributeKeyValuePredicateAO, error) { + var body TargetAttributeKeyValuePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetAttributeKeyValuePredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided TargetAttributeKeyValuePredicateAO +func (t *TargetTypePredicateAO) FromTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetAttributeKeyValuePredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided TargetAttributeKeyValuePredicateAO +func (t *TargetTypePredicateAO) MergeTargetAttributeKeyValuePredicateAO(v TargetAttributeKeyValuePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetNamePredicateAO returns the union data inside the TargetTypePredicateAO as a TargetNamePredicateAO +func (t TargetTypePredicateAO) AsTargetNamePredicateAO() (TargetNamePredicateAO, error) { + var body TargetNamePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetNamePredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided TargetNamePredicateAO +func (t *TargetTypePredicateAO) FromTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetNamePredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided TargetNamePredicateAO +func (t *TargetTypePredicateAO) MergeTargetNamePredicateAO(v TargetNamePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsTargetTypePredicateAO returns the union data inside the TargetTypePredicateAO as a TargetTypePredicateAO +func (t TargetTypePredicateAO) AsTargetTypePredicateAO() (TargetTypePredicateAO, error) { + var body TargetTypePredicateAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromTargetTypePredicateAO overwrites any union data inside the TargetTypePredicateAO as the provided TargetTypePredicateAO +func (t *TargetTypePredicateAO) FromTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeTargetTypePredicateAO performs a merge with any union data inside the TargetTypePredicateAO, using the provided TargetTypePredicateAO +func (t *TargetTypePredicateAO) MergeTargetTypePredicateAO(v TargetTypePredicateAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t TargetTypePredicateAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + if t.Types != nil { + object["types"], err = json.Marshal(t.Types) + if err != nil { + return nil, fmt.Errorf("error marshaling 'types': %w", err) + } + } + b, err = json.Marshal(object) + return b, err +} + +func (t *TargetTypePredicateAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["types"]; found { + err = json.Unmarshal(raw, &t.Types) + if err != nil { + return fmt.Errorf("error reading 'types': %w", err) + } + } + + return err +} + +// AsVariableExpressionAO0 returns the union data inside the VariableExpressionAO as a VariableExpressionAO0 +func (t VariableExpressionAO) AsVariableExpressionAO0() (VariableExpressionAO0, error) { + var body VariableExpressionAO0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromVariableExpressionAO0 overwrites any union data inside the VariableExpressionAO as the provided VariableExpressionAO0 +func (t *VariableExpressionAO) FromVariableExpressionAO0(v VariableExpressionAO0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeVariableExpressionAO0 performs a merge with any union data inside the VariableExpressionAO, using the provided VariableExpressionAO0 +func (t *VariableExpressionAO) MergeVariableExpressionAO0(v VariableExpressionAO0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsVariableExpressionAO1 returns the union data inside the VariableExpressionAO as a VariableExpressionAO1 +func (t VariableExpressionAO) AsVariableExpressionAO1() (VariableExpressionAO1, error) { + var body VariableExpressionAO1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromVariableExpressionAO1 overwrites any union data inside the VariableExpressionAO as the provided VariableExpressionAO1 +func (t *VariableExpressionAO) FromVariableExpressionAO1(v VariableExpressionAO1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeVariableExpressionAO1 performs a merge with any union data inside the VariableExpressionAO, using the provided VariableExpressionAO1 +func (t *VariableExpressionAO) MergeVariableExpressionAO1(v VariableExpressionAO1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsSelectExpressionAO returns the union data inside the VariableExpressionAO as a SelectExpressionAO +func (t VariableExpressionAO) AsSelectExpressionAO() (SelectExpressionAO, error) { + var body SelectExpressionAO + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromSelectExpressionAO overwrites any union data inside the VariableExpressionAO as the provided SelectExpressionAO +func (t *VariableExpressionAO) FromSelectExpressionAO(v SelectExpressionAO) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeSelectExpressionAO performs a merge with any union data inside the VariableExpressionAO, using the provided SelectExpressionAO +func (t *VariableExpressionAO) MergeSelectExpressionAO(v SelectExpressionAO) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t VariableExpressionAO) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *VariableExpressionAO) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + + // GetAccessTokens Get access token list + // + // Deprecated, use v2 instead. Get a list of all access tokens. The access token itself is abbreviated for security reasons. Access tokens with v2 features are not returned, as they can not be represented cleanly in the old format. + // + // Corresponds with GET /api/access-tokens (the `GetAccessTokens` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + GetAccessTokens(ctx context.Context, params *GetAccessTokensParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateAccessTokenWithBody Add a access token + // + // Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/access-tokens (the `CreateAccessToken` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + CreateAccessTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateAccessToken Add a access token + // + // Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/access-tokens (the `CreateAccessToken` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + CreateAccessToken(ctx context.Context, body CreateAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetAccessTokens1 Get access token list + // + // Get a list of all access tokens. The access token itself is abbreviated for security reasons. + // + // Corresponds with GET /api/access-tokens/v2 (the `GetAccessTokens1` operationId). + GetAccessTokens1(ctx context.Context, params *GetAccessTokens1Params, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateAccessToken1WithBody Create an access token + // + // Generate a new access token. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/access-tokens/v2 (the `CreateAccessToken1` operationId). + CreateAccessToken1WithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateAccessToken1 Create an access token + // + // Generate a new access token. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/access-tokens/v2 (the `CreateAccessToken1` operationId). + CreateAccessToken1(ctx context.Context, body CreateAccessToken1JSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteAccessToken1 Delete access token + // + // Remove the access token. After that, the access token can't be used anymore. + // + // Corresponds with DELETE /api/access-tokens/v2/{id} (the `DeleteAccessToken1` operationId). + DeleteAccessToken1(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RecreateAccessTokenWithBody Recreate an access token + // + // Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/access-tokens/v2/{id}/recreate (the `RecreateAccessToken` operationId). + RecreateAccessTokenWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RecreateAccessToken Recreate an access token + // + // Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/access-tokens/v2/{id}/recreate (the `RecreateAccessToken` operationId). + RecreateAccessToken(ctx context.Context, id string, body RecreateAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteAccessToken Delete access token + // + // Remove the access token associated. After that, the access token can't be used anymore for e.g. creating a new experiment or running an experiment. + // + // Corresponds with DELETE /api/access-tokens/{id} (the `DeleteAccessToken` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + DeleteAccessToken(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // FindAllActions Get all actions. + // + // Corresponds with GET /api/actions (the `FindAllActions` operationId). + FindAllActions(ctx context.Context, params *FindAllActionsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetAction Fetch a single action description + // + // Get action including their parameters. + // + // Corresponds with GET /api/actions/{actionId} (the `GetAction` operationId). + GetAction(ctx context.Context, actionId string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTargetAdviceSummaryWithBody Get all currently active advice for a given environment and query. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/advice (the `GetTargetAdviceSummary` operationId). + GetTargetAdviceSummaryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTargetAdviceSummary Get all currently active advice for a given environment and query. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/advice (the `GetTargetAdviceSummary` operationId). + GetTargetAdviceSummary(ctx context.Context, body GetTargetAdviceSummaryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // Find Get all audit log entries + // + // Retrieve all audit logs in the given time-frame.
This endpoint requires an admin-token and can't be used with a team-based token. + // + // Corresponds with GET /api/audit-log (the `Find` operationId). + Find(ctx context.Context, params *FindParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ForwardToPlatform Forward to Steadybit platform to either create an experiment associated to the `tag` or forward to the experiments linked already to the `tag` + // + // This endpoint can be used as a link for the badge of the `/api/badges/linked-badge.svg` API to either create a new experiment or show the linked experiments in Steadybit. This will help to link it correctly e.g. in your CMS-systems. + // + // Corresponds with GET /api/badges/link (the `ForwardToPlatform` operationId). + ForwardToPlatform(ctx context.Context, params *ForwardToPlatformParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetLinkedBadge Get badge for create experiment or run status as SVG image + // + // Creates an image badge that is either for creating a new experiment linked to an `externalReference` or - if an experiment with the given `externalReference` already exists - a badge showing the run status of the experiment. The badge is return as SVG to integrate it nicely e.g. into your CMS-systems. You can use the `/api/badges/link` endpoint to link it appropriately + // + // Corresponds with GET /api/badges/linked-badge.svg (the `GetLinkedBadge` operationId). + GetLinkedBadge(ctx context.Context, params *GetLinkedBadgeParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetEnvironments Fetch a list of all environments + // + // Get a list of all environments that exist. + // + // Corresponds with GET /api/environments (the `GetEnvironments` operationId). + GetEnvironments(ctx context.Context, params *GetEnvironmentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertEnvironmentWithBody Create or update an environment + // + // Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/environments (the `UpsertEnvironment` operationId). + UpsertEnvironmentWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertEnvironment Create or update an environment + // + // Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/environments (the `UpsertEnvironment` operationId). + UpsertEnvironment(ctx context.Context, body UpsertEnvironmentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteEnvironment Delete environment + // + // Remove the given environment from the Steadybit platform. + // + // Corresponds with DELETE /api/environments/{id} (the `DeleteEnvironment` operationId). + DeleteEnvironment(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetEnvironment Fetch a single environment + // + // Get all details of a single existing environment. + // + // Corresponds with GET /api/environments/{id} (the `GetEnvironment` operationId). + GetEnvironment(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetEnvironmentVariables Get environment variables + // + // Get all environment variables associated to a single environment. + // + // Corresponds with GET /api/environments/{id}/variables (the `GetEnvironmentVariables` operationId). + GetEnvironmentVariables(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetEnvironmentVariablesWithBody Replace all environment variables + // + // All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/environments/{id}/variables (the `SetEnvironmentVariables` operationId). + SetEnvironmentVariablesWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetEnvironmentVariables Replace all environment variables + // + // All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/environments/{id}/variables (the `SetEnvironmentVariables` operationId). + SetEnvironmentVariables(ctx context.Context, id openapi_types.UUID, body SetEnvironmentVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateEnvironmentVariablesWithBody Add / merge all environment variables + // + // All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /api/environments/{id}/variables (the `UpdateEnvironmentVariables` operationId). + UpdateEnvironmentVariablesWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateEnvironmentVariables Add / merge all environment variables + // + // All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /api/environments/{id}/variables (the `UpdateEnvironmentVariables` operationId). + UpdateEnvironmentVariables(ctx context.Context, id openapi_types.UUID, body UpdateEnvironmentVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperiments Fetch a list of all experiments + // + // Get a list of all experiments that exist. + // + // Corresponds with GET /api/experiments (the `GetExperiments` operationId). + GetExperiments(ctx context.Context, params *GetExperimentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateOrUpdateExperimentWithBody Create or update an experiment + // + // Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments (the `CreateOrUpdateExperiment` operationId). + CreateOrUpdateExperimentWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateOrUpdateExperiment Create or update an experiment + // + // Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments (the `CreateOrUpdateExperiment` operationId). + CreateOrUpdateExperiment(ctx context.Context, body CreateOrUpdateExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SaveAndRunWithBody Save and run experiment + // + // Save the given experiment and immediately run it. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/execute (the `SaveAndRun` operationId). + SaveAndRunWithBody(ctx context.Context, params *SaveAndRunParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SaveAndRun Save and run experiment + // + // Save the given experiment and immediately run it. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/execute (the `SaveAndRun` operationId). + SaveAndRun(ctx context.Context, params *SaveAndRunParams, body SaveAndRunJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentExecutions1 Fetch a list of all experiment executions + // + // Get a list of all experiment executions that exist. + // + // Corresponds with GET /api/experiments/executions (the `GetExperimentExecutions1` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + GetExperimentExecutions1(ctx context.Context, params *GetExperimentExecutions1Params, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentExecutions2WithBody Fetch a list of experiment executions + // + // Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/executions (the `GetExperimentExecutions2` operationId). + GetExperimentExecutions2WithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentExecutions2 Fetch a list of experiment executions + // + // Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/executions (the `GetExperimentExecutions2` operationId). + GetExperimentExecutions2(ctx context.Context, body GetExperimentExecutions2JSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentExecution Fetch a single experiment executions of a single experiment + // + // Get a single experiment execution that was performed for a specific experiment. + // + // Corresponds with GET /api/experiments/executions/{id} (the `GetExperimentExecution` operationId). + GetExperimentExecution(ctx context.Context, id int64, params *GetExperimentExecutionParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetArtifact performs a GET /api/experiments/executions/{id}/artifacts/{targetExecutionId}/{artifactId} (the `GetArtifact` operationId) request. + GetArtifact(ctx context.Context, id int64, targetExecutionId string, artifactId string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CancelExperimentExecution Cancel a running experiment execution of a single experiment + // + // Cancels a currently running experiment execution to be stopped as soon as possible. + // + // Corresponds with POST /api/experiments/executions/{id}/cancel (the `CancelExperimentExecution` operationId). + CancelExperimentExecution(ctx context.Context, id int64, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateExecutionPropertiesWithBody Update properties of an experiment execution + // + // Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/executions/{id}/properties (the `UpdateExecutionProperties` operationId). + UpdateExecutionPropertiesWithBody(ctx context.Context, id int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateExecutionProperties Update properties of an experiment execution + // + // Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/executions/{id}/properties (the `UpdateExecutionProperties` operationId). + UpdateExecutionProperties(ctx context.Context, id int64, body UpdateExecutionPropertiesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // AddExecutionPropertyValueWithBody Add a single value to a list property of an experiment execution. + // + // This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/executions/{id}/properties/{key}/add (the `AddExecutionPropertyValue` operationId). + AddExecutionPropertyValueWithBody(ctx context.Context, id int64, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // AddExecutionPropertyValue Add a single value to a list property of an experiment execution. + // + // This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/executions/{id}/properties/{key}/add (the `AddExecutionPropertyValue` operationId). + AddExecutionPropertyValue(ctx context.Context, id int64, key string, body AddExecutionPropertyValueJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetExecutionPropertyValueWithBody Set the value of a property of an experiment execution. + // + // Only properties with `editableInExecution` set to `true` can be modified. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/executions/{id}/properties/{key}/set (the `SetExecutionPropertyValue` operationId). + SetExecutionPropertyValueWithBody(ctx context.Context, id int64, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetExecutionPropertyValue Set the value of a property of an experiment execution. + // + // Only properties with `editableInExecution` set to `true` can be modified. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/executions/{id}/properties/{key}/set (the `SetExecutionPropertyValue` operationId). + SetExecutionPropertyValue(ctx context.Context, id int64, key string, body SetExecutionPropertyValueJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertScheduleWithBody Create or update an experiment schedule + // + // Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/schedules (the `UpsertSchedule` operationId). + UpsertScheduleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertSchedule Create or update an experiment schedule + // + // Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/schedules (the `UpsertSchedule` operationId). + UpsertSchedule(ctx context.Context, body UpsertScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetAllSchedulesV2 Get all current experiment schedule configurations + // + // Corresponds with GET /api/experiments/schedules/v2 (the `GetAllSchedulesV2` operationId). + GetAllSchedulesV2(ctx context.Context, params *GetAllSchedulesV2Params, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RemoveExperimentScheduleById Remove an existing experiment schedule + // + // Corresponds with DELETE /api/experiments/schedules/{id} (the `RemoveExperimentScheduleById` operationId). + RemoveExperimentScheduleById(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetSchedules Get experiment schedules for a specific experiment schedule id + // + // Corresponds with GET /api/experiments/schedules/{id} (the `GetSchedules` operationId). + GetSchedules(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PatchScheduleWithBody Partially update an experiment schedule + // + // Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PATCH /api/experiments/schedules/{id} (the `PatchSchedule` operationId). + PatchScheduleWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PatchSchedule Partially update an experiment schedule + // + // Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PATCH /api/experiments/schedules/{id} (the `PatchSchedule` operationId). + PatchSchedule(ctx context.Context, id string, body PatchScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentTemplates Fetch a list of all templates + // + // Get a list of all templates that exist. + // + // Corresponds with GET /api/experiments/templates (the `GetExperimentTemplates` operationId). + GetExperimentTemplates(ctx context.Context, params *GetExperimentTemplatesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertExperimentTemplateWithBody Create or update an experiment template + // + // Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/templates (the `UpsertExperimentTemplate` operationId). + UpsertExperimentTemplateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertExperimentTemplate Create or update an experiment template + // + // Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/templates (the `UpsertExperimentTemplate` operationId). + UpsertExperimentTemplate(ctx context.Context, body UpsertExperimentTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ImportFromHubWithBody Import experiment templates + // + // Import experiment templates with given IDs from linked hub. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/templates/imports (the `ImportFromHub` operationId). + ImportFromHubWithBody(ctx context.Context, params *ImportFromHubParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ImportFromHub Import experiment templates + // + // Import experiment templates with given IDs from linked hub. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/templates/imports (the `ImportFromHub` operationId). + ImportFromHub(ctx context.Context, params *ImportFromHubParams, body ImportFromHubJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteExperimentTemplate Delete experiment template + // + // Remove the given experiment template from the Steadybit platform. If this template is used in a service profile, it will be removed from the profile and all provided service experiments will get deleted. + // + // Corresponds with DELETE /api/experiments/templates/{id} (the `DeleteExperimentTemplate` operationId). + DeleteExperimentTemplate(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentTemplate Fetch a single experiment template + // + // Get all details of a single existing experiment template. + // + // Corresponds with GET /api/experiments/templates/{id} (the `GetExperimentTemplate` operationId). + GetExperimentTemplate(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateExperimentByTemplateWithBody Create an experiment based on an experiment template + // + // Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-create (the `CreateExperimentByTemplate` operationId). + CreateExperimentByTemplateWithBody(ctx context.Context, id openapi_types.UUID, params *CreateExperimentByTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateExperimentByTemplate Create an experiment based on an experiment template + // + // Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-create (the `CreateExperimentByTemplate` operationId). + CreateExperimentByTemplate(ctx context.Context, id openapi_types.UUID, params *CreateExperimentByTemplateParams, body CreateExperimentByTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SaveAndRunFromTemplateWithBody Create an experiment based on an experiment template and run experiment + // + // Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-execute (the `SaveAndRunFromTemplate` operationId). + SaveAndRunFromTemplateWithBody(ctx context.Context, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SaveAndRunFromTemplate Create an experiment based on an experiment template and run experiment + // + // Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-execute (the `SaveAndRunFromTemplate` operationId). + SaveAndRunFromTemplate(ctx context.Context, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, body SaveAndRunFromTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateExperimentByTemplateWithBody Update an existing experiment based on a template + // + // Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-update/{key} (the `UpdateExperimentByTemplate` operationId). + UpdateExperimentByTemplateWithBody(ctx context.Context, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateExperimentByTemplate Update an existing experiment based on a template + // + // Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-update/{key} (the `UpdateExperimentByTemplate` operationId). + UpdateExperimentByTemplate(ctx context.Context, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, body UpdateExperimentByTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteExperiment Delete experiment + // + // Remove the given experiment. The associated number is still reserved afterwards and will not be reused. + // + // Corresponds with DELETE /api/experiments/{key} (the `DeleteExperiment` operationId). + DeleteExperiment(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperiment Fetch a single experiment + // + // Get all details of a single existing experiment. + // + // Corresponds with GET /api/experiments/{key} (the `GetExperiment` operationId). + GetExperiment(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateExperimentWithBody Update an experiment + // + // Update the experiment identified by the experiment `key`. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/{key} (the `UpdateExperiment` operationId). + UpdateExperimentWithBody(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateExperiment Update an experiment + // + // Update the experiment identified by the experiment `key`. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/{key} (the `UpdateExperiment` operationId). + UpdateExperiment(ctx context.Context, key string, body UpdateExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentBadge Get experiment run status as SVG image + // + // Get the status of the latest experiment run of the associated experiment as SVG to integrate it nicely e.g. into your CMS-systems. + // + // Corresponds with GET /api/experiments/{key}/badge.svg (the `GetExperimentBadge` operationId). + GetExperimentBadge(ctx context.Context, key string, params *GetExperimentBadgeParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ExecuteExperimentWithBody Execute an experiment + // + // Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. + // + // Examples: + // - Override environment from the experiment for a single run: + // ``` + // { + // "environment": "Shop Stage" + // } + // ``` + // - Override the variables for a single execution: + // ``` + // { + // "variables": { + // "httpEndpoint": "http://dev.shop.products.internal" + // } + // } + // ``` + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/experiments/{key}/execute (the `ExecuteExperiment` operationId). + ExecuteExperimentWithBody(ctx context.Context, key string, params *ExecuteExperimentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ExecuteExperiment Execute an experiment + // + // Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. + // + // Examples: + // - Override environment from the experiment for a single run: + // ``` + // { + // "environment": "Shop Stage" + // } + // ``` + // - Override the variables for a single execution: + // ``` + // { + // "variables": { + // "httpEndpoint": "http://dev.shop.products.internal" + // } + // } + // ``` + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/experiments/{key}/execute (the `ExecuteExperiment` operationId). + ExecuteExperiment(ctx context.Context, key string, params *ExecuteExperimentParams, body ExecuteExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentExecutions3 Fetch a list of all experiment executions of a single experiment + // + // Get a list of all experiment executions that were performed for a specific experiment. + // + // Corresponds with GET /api/experiments/{key}/executions (the `GetExperimentExecutions3` operationId). + GetExperimentExecutions3(ctx context.Context, key string, params *GetExperimentExecutions3Params, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetLandscapeViews Fetch all saved landscape views of a team + // + // Get a list of all saved explorer landscape views that belong to the given team. + // + // Corresponds with GET /api/explore/landscape/views (the `GetLandscapeViews` operationId). + GetLandscapeViews(ctx context.Context, params *GetLandscapeViewsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateLandscapeViewWithBody Create a saved landscape view + // + // Create a new saved explorer landscape view for a team. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/explore/landscape/views (the `CreateLandscapeView` operationId). + CreateLandscapeViewWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CreateLandscapeView Create a saved landscape view + // + // Create a new saved explorer landscape view for a team. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/explore/landscape/views (the `CreateLandscapeView` operationId). + CreateLandscapeView(ctx context.Context, body CreateLandscapeViewJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteLandscapeView Delete a saved landscape view + // + // Remove the given saved explorer landscape view from the Steadybit platform. + // + // Corresponds with DELETE /api/explore/landscape/views/{id} (the `DeleteLandscapeView` operationId). + DeleteLandscapeView(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetLandscapeView Fetch a single saved landscape view + // + // Get all details of a single saved explorer landscape view. + // + // Corresponds with GET /api/explore/landscape/views/{id} (the `GetLandscapeView` operationId). + GetLandscapeView(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateLandscapeViewWithBody Update a saved landscape view + // + // Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /api/explore/landscape/views/{id} (the `UpdateLandscapeView` operationId). + UpdateLandscapeViewWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateLandscapeView Update a saved landscape view + // + // Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /api/explore/landscape/views/{id} (the `UpdateLandscapeView` operationId). + UpdateLandscapeView(ctx context.Context, id openapi_types.UUID, body UpdateLandscapeViewJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // Health performs a GET /api/health (the `Health` operationId) request. + Health(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // Liveness performs a GET /api/health/liveness (the `Liveness` operationId) request. + Liveness(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // Readiness performs a GET /api/health/readiness (the `Readiness` operationId) request. + Readiness(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetHubs Fetch a list of all hubs + // + // Get a list of all hubs that are currently connected. + // + // Corresponds with GET /api/hubs (the `GetHubs` operationId). + GetHubs(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertHubWithBody Create or update a hub + // + // Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/hubs (the `UpsertHub` operationId). + UpsertHubWithBody(ctx context.Context, params *UpsertHubParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertHub Create or update a hub + // + // Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/hubs (the `UpsertHub` operationId). + UpsertHub(ctx context.Context, params *UpsertHubParams, body UpsertHubJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ConnectionCheckWithBody Check a hub connection + // + // Check if the given hub connection details point to a valid hub. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/hubs/connection-check (the `ConnectionCheck` operationId). + ConnectionCheckWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ConnectionCheck Check a hub connection + // + // Check if the given hub connection details point to a valid hub. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/hubs/connection-check (the `ConnectionCheck` operationId). + ConnectionCheck(ctx context.Context, body ConnectionCheckJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteHub Delete a hub + // + // Remove the given hub. + // + // Corresponds with DELETE /api/hubs/{id} (the `DeleteHub` operationId). + DeleteHub(ctx context.Context, id openapi_types.UUID, params *DeleteHubParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetHubById Fetch a single hub + // + // Get all details of a single hub. + // + // Corresponds with GET /api/hubs/{id} (the `GetHubById` operationId). + GetHubById(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ResyncHub Re-synchronize a hub + // + // Fetch the latest hub definition based on `hubRepository`. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. + // + // Corresponds with POST /api/hubs/{id}/resync (the `ResyncHub` operationId). + ResyncHub(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetPreflightWebhooks Fetch a list of preflight webhooks + // + // Get a list of all existing preflight webhooks. + // + // Corresponds with GET /api/integrations/preflight (the `GetPreflightWebhooks` operationId). + GetPreflightWebhooks(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertPreflightWebhookWithBody Create or update a preflight webhook + // + // Insert or update a preflight webhook.
Experiment runs that were not executed due to engaged / active kill switch will not be automatically executed, they need to be triggered again. + // + // Corresponds with DELETE /api/killswitch (the `DisengageKillswitch` operationId). + DisengageKillswitch(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetKillswitch Get the current status of the kill switch + // + // Determines the current status of the kill switch without changing it. + // + // Corresponds with GET /api/killswitch (the `GetKillswitch` operationId). + GetKillswitch(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // EngageKillswitch Activate / engage the kill switch + // + // Activates / engages the kill switch to cancel all experiments running at the moment and prevent execution of new experiments until the kill switch is disengaged / deactivated again. + // + // Corresponds with POST /api/killswitch (the `EngageKillswitch` operationId). + EngageKillswitch(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetLicenseSummary Get license summary. + // + // Corresponds with GET /api/license (the `GetLicenseSummary` operationId). + GetLicenseSummary(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetReport Get license report. + // + // Corresponds with GET /api/license/report (the `GetReport` operationId). + GetReport(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetPreflightActionSummary Get all preflight actions. + // + // Corresponds with GET /api/preflight/actions (the `GetPreflightActionSummary` operationId). + GetPreflightActionSummary(ctx context.Context, params *GetPreflightActionSummaryParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetAssociations Get all current associations. + // + // Corresponds with GET /api/properties/associations (the `GetAssociations` operationId). + GetAssociations(ctx context.Context, params *GetAssociationsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertPropertyAssociationWithBody Create or update a property association + // + // Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. + // + // Examples: + // - Assign the property `RESULT_COLOR` to all experiment designs: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": false, + // "required": true + // } + // ``` + // - Assign the property `RESULT_COLOR` to the design ADM-15: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": false, + // "experimentKey": "ADM-15", + // "required": true + // } + // ``` + // - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": true, + // "experimentKey": "ADM-15", + // "required": false + // } + // ``` + // - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: + // ``` + // { + // "key": "RESULT_COLOR", + // "associationType": "SERVICE", + // "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", + // "required": false + // } + // ``` + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/properties/associations (the `UpsertPropertyAssociation` operationId). + UpsertPropertyAssociationWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertPropertyAssociation Create or update a property association + // + // Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. + // + // Examples: + // - Assign the property `RESULT_COLOR` to all experiment designs: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": false, + // "required": true + // } + // ``` + // - Assign the property `RESULT_COLOR` to the design ADM-15: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": false, + // "experimentKey": "ADM-15", + // "required": true + // } + // ``` + // - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": true, + // "experimentKey": "ADM-15", + // "required": false + // } + // ``` + // - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: + // ``` + // { + // "key": "RESULT_COLOR", + // "associationType": "SERVICE", + // "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", + // "required": false + // } + // ``` + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/properties/associations (the `UpsertPropertyAssociation` operationId). + UpsertPropertyAssociation(ctx context.Context, body UpsertPropertyAssociationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeletePropertyAssociation Remove an existing property association. + // + // Corresponds with DELETE /api/properties/associations/{id} (the `DeletePropertyAssociation` operationId). + DeletePropertyAssociation(ctx context.Context, id openapi_types.UUID, params *DeletePropertyAssociationParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetPropertyDefinition1 Get property association by a given id. + // + // Corresponds with GET /api/properties/associations/{id} (the `GetPropertyDefinition1` operationId). + GetPropertyDefinition1(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetPropertyDefinitions performs a GET /api/properties/definitions (the `GetPropertyDefinitions` operationId) request. + GetPropertyDefinitions(ctx context.Context, params *GetPropertyDefinitionsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertPropertyDefinitionWithBody Create or update property definition + // + // Insert or update the property definition specified by the given `key`. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/properties/definitions (the `UpsertPropertyDefinition` operationId). + UpsertPropertyDefinitionWithBody(ctx context.Context, params *UpsertPropertyDefinitionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertPropertyDefinition Create or update property definition + // + // Insert or update the property definition specified by the given `key`. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/properties/definitions (the `UpsertPropertyDefinition` operationId). + UpsertPropertyDefinition(ctx context.Context, params *UpsertPropertyDefinitionParams, body UpsertPropertyDefinitionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeletePropertyDefinition Remove an existing property definition + // + // Corresponds with DELETE /api/properties/definitions/{key} (the `DeletePropertyDefinition` operationId). + DeletePropertyDefinition(ctx context.Context, key string, params *DeletePropertyDefinitionParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetPropertyDefinition Get property definition for a specific property definition key. + // + // Corresponds with GET /api/properties/definitions/{key} (the `GetPropertyDefinition` operationId). + GetPropertyDefinition(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetEnvironmentCountsWithBody Get environment counts over time + // + // Returns the number of environments in the tenant aggregated into time buckets. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/reports/environments (the `GetEnvironmentCounts` operationId). + GetEnvironmentCountsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetEnvironmentCounts Get environment counts over time + // + // Returns the number of environments in the tenant aggregated into time buckets. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/reports/environments (the `GetEnvironmentCounts` operationId). + GetEnvironmentCounts(ctx context.Context, body GetEnvironmentCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentCreationsWithBody Get experiment creation counts over time + // + // Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/reports/experiments/created (the `GetExperimentCreations` operationId). + GetExperimentCreationsWithBody(ctx context.Context, params *GetExperimentCreationsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentCreations Get experiment creation counts over time + // + // Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/reports/experiments/created (the `GetExperimentCreations` operationId). + GetExperimentCreations(ctx context.Context, params *GetExperimentCreationsParams, body GetExperimentCreationsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentExecutionsWithBody Get experiment execution counts over time + // + // Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/reports/experiments/executed (the `GetExperimentExecutions` operationId). + GetExperimentExecutionsWithBody(ctx context.Context, params *GetExperimentExecutionsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetExperimentExecutions Get experiment execution counts over time + // + // Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/reports/experiments/executed (the `GetExperimentExecutions` operationId). + GetExperimentExecutions(ctx context.Context, params *GetExperimentExecutionsParams, body GetExperimentExecutionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetAverageRiskWithBody Get average service risk over time + // + // Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/reports/services/average (the `GetAverageRisk` operationId). + GetAverageRiskWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetAverageRisk Get average service risk over time + // + // Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/reports/services/average (the `GetAverageRisk` operationId). + GetAverageRisk(ctx context.Context, body GetAverageRiskJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetRiskByCategoryWithBody Get average service risk grouped by category over time + // + // Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/reports/services/by-category (the `GetRiskByCategory` operationId). + GetRiskByCategoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetRiskByCategory Get average service risk grouped by category over time + // + // Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/reports/services/by-category (the `GetRiskByCategory` operationId). + GetRiskByCategory(ctx context.Context, body GetRiskByCategoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetRiskDistributionWithBody Get service risk level distribution over time + // + // Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/reports/services/distribution (the `GetRiskDistribution` operationId). + GetRiskDistributionWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetRiskDistribution Get service risk level distribution over time + // + // Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/reports/services/distribution (the `GetRiskDistribution` operationId). + GetRiskDistribution(ctx context.Context, body GetRiskDistributionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTeamCountsWithBody Get team counts over time + // + // Returns the number of teams in the tenant aggregated into time buckets. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/reports/teams (the `GetTeamCounts` operationId). + GetTeamCountsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTeamCounts Get team counts over time + // + // Returns the number of teams in the tenant aggregated into time buckets. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/reports/teams (the `GetTeamCounts` operationId). + GetTeamCounts(ctx context.Context, body GetTeamCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetUserCountsWithBody Get user counts over time + // + // Returns the number of users in the tenant aggregated into time buckets. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/reports/users (the `GetUserCounts` operationId). + GetUserCountsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetUserCounts Get user counts over time + // + // Returns the number of users in the tenant aggregated into time buckets. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/reports/users (the `GetUserCounts` operationId). + GetUserCounts(ctx context.Context, body GetUserCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetServiceList Fetch a list of services + // + // Corresponds with GET /api/services (the `GetServiceList` operationId). + GetServiceList(ctx context.Context, params *GetServiceListParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertServiceWithBody Create or update service + // + // Insert or update the service specified by the given `id`. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/services (the `UpsertService` operationId). + UpsertServiceWithBody(ctx context.Context, params *UpsertServiceParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertService Create or update service + // + // Insert or update the service specified by the given `id`. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/services (the `UpsertService` operationId). + UpsertService(ctx context.Context, params *UpsertServiceParams, body UpsertServiceJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetProfiles Fetch a list of service profiles + // + // Corresponds with GET /api/services/profiles (the `GetProfiles` operationId). + GetProfiles(ctx context.Context, params *GetProfilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertProfileWithBody Create or update service profile + // + // Insert or update the service profile specified by the given `id`. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/services/profiles (the `UpsertProfile` operationId). + UpsertProfileWithBody(ctx context.Context, params *UpsertProfileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertProfile Create or update service profile + // + // Insert or update the service profile specified by the given `id`. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/services/profiles (the `UpsertProfile` operationId). + UpsertProfile(ctx context.Context, params *UpsertProfileParams, body UpsertProfileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteProfile Delete an existing service profile + // + // Corresponds with DELETE /api/services/profiles/{id} (the `DeleteProfile` operationId). + DeleteProfile(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetProfile Get service profile by id. + // + // Corresponds with GET /api/services/profiles/{id} (the `GetProfile` operationId). + GetProfile(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteService Delete an existing service + // + // Corresponds with DELETE /api/services/{id} (the `DeleteService` operationId). + DeleteService(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetService Get service by id. + // + // Corresponds with GET /api/services/{id} (the `GetService` operationId). + GetService(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetServiceExperiments Get experiments associated to an service. + // + // Corresponds with GET /api/services/{id}/experiments (the `GetServiceExperiments` operationId). + GetServiceExperiments(ctx context.Context, id openapi_types.UUID, params *GetServiceExperimentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UnlinkCustomExperiment Remove a linked custom experiment from a service. + // + // Corresponds with DELETE /api/services/{id}/experiments/custom (the `UnlinkCustomExperiment` operationId). + UnlinkCustomExperiment(ctx context.Context, id openapi_types.UUID, params *UnlinkCustomExperimentParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // LinkCustomExperimentWithBody Link a custom experiment to a service. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/services/{id}/experiments/custom (the `LinkCustomExperiment` operationId). + LinkCustomExperimentWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // LinkCustomExperiment Link a custom experiment to a service. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/services/{id}/experiments/custom (the `LinkCustomExperiment` operationId). + LinkCustomExperiment(ctx context.Context, id openapi_types.UUID, body LinkCustomExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertProvidedExperimentWithBody Create or update a provided experiment. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/services/{id}/experiments/provided (the `UpsertProvidedExperiment` operationId). + UpsertProvidedExperimentWithBody(ctx context.Context, id openapi_types.UUID, params *UpsertProvidedExperimentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertProvidedExperiment Create or update a provided experiment. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/services/{id}/experiments/provided (the `UpsertProvidedExperiment` operationId). + UpsertProvidedExperiment(ctx context.Context, id openapi_types.UUID, params *UpsertProvidedExperimentParams, body UpsertProvidedExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetRisk Get the risk score for a service + // + // Corresponds with GET /api/services/{id}/risk (the `GetRisk` operationId). + GetRisk(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetServiceVariables Get service variables + // + // Get all variables owned by the service. + // + // Corresponds with GET /api/services/{id}/variables (the `GetServiceVariables` operationId). + GetServiceVariables(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // MergeServiceVariablesWithBody Add / merge service variables + // + // All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PATCH /api/services/{id}/variables (the `MergeServiceVariables` operationId). + MergeServiceVariablesWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // MergeServiceVariables Add / merge service variables + // + // All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PATCH /api/services/{id}/variables (the `MergeServiceVariables` operationId). + MergeServiceVariables(ctx context.Context, id openapi_types.UUID, body MergeServiceVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetServiceVariablesWithBody Replace all service variables + // + // All provided variables will be associated with the given service and existing ones removed. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /api/services/{id}/variables (the `SetServiceVariables` operationId). + SetServiceVariablesWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetServiceVariables Replace all service variables + // + // All provided variables will be associated with the given service and existing ones removed. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /api/services/{id}/variables (the `SetServiceVariables` operationId). + SetServiceVariables(ctx context.Context, id openapi_types.UUID, body SetServiceVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTargetsStats Gather target statistics without any filters + // + // Corresponds with GET /api/target-stats (the `GetTargetsStats` operationId). + GetTargetsStats(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTargetsStats1WithBody Gather target statistics for a given predicate or query + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/target-stats (the `GetTargetsStats1` operationId). + GetTargetsStats1WithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTargetsStats1 Gather target statistics for a given predicate or query + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/target-stats (the `GetTargetsStats1` operationId). + GetTargetsStats1(ctx context.Context, body GetTargetsStats1JSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTargets Get targets + // + // Get targets. + // + // Corresponds with GET /api/targets (the `GetTargets` operationId). + GetTargets(ctx context.Context, params *GetTargetsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTargetAttributeKeys Get attribute key + // + // Get all available attribute keys for a specific target type in a given environment. + // + // Corresponds with GET /api/targets/attributes/keys (the `GetTargetAttributeKeys` operationId). + GetTargetAttributeKeys(ctx context.Context, params *GetTargetAttributeKeysParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTargetAttributeValues Get attribute values + // + // Get all available attribute values for a specific attribute and target type in a given environment. + // + // Corresponds with GET /api/targets/attributes/values (the `GetTargetAttributeValues` operationId). + GetTargetAttributeValues(ctx context.Context, params *GetTargetAttributeValuesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTeams Fetch a list of all teams + // + // Get a list of all teams that exist.
If used with a team-associated `accessToken` and `onlyAccessible` is set to `true` you only get the team of the `accessToken`. + // + // Corresponds with GET /api/teams (the `GetTeams` operationId). + GetTeams(ctx context.Context, params *GetTeamsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertTeamWithBody Create or update a team + // + // Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/teams (the `UpsertTeam` operationId). + UpsertTeamWithBody(ctx context.Context, params *UpsertTeamParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpsertTeam Create or update a team + // + // Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/teams (the `UpsertTeam` operationId). + UpsertTeam(ctx context.Context, params *UpsertTeamParams, body UpsertTeamJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteTeam Delete team + // + // Remove the given team from the Steadybit platform. This will only work, if there are no experiments running at the moment. + // + // Corresponds with DELETE /api/teams/{key} (the `DeleteTeam` operationId). + DeleteTeam(ctx context.Context, key string, params *DeleteTeamParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTeam Fetch a single team + // + // Get all details of a single existing teams. + // + // Corresponds with GET /api/teams/{key} (the `GetTeam` operationId). + GetTeam(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTeamEnvironments Get all environments assigned to the team + // + // Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). + // + // Corresponds with GET /api/teams/{key}/environments (the `GetTeamEnvironments` operationId). + GetTeamEnvironments(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetTeamEnvironmentsWithBody Update the environments of a specific team + // + // The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /api/teams/{key}/environments (the `SetTeamEnvironments` operationId). + SetTeamEnvironmentsWithBody(ctx context.Context, key string, params *SetTeamEnvironmentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetTeamEnvironments Update the environments of a specific team + // + // The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /api/teams/{key}/environments (the `SetTeamEnvironments` operationId). + SetTeamEnvironments(ctx context.Context, key string, params *SetTeamEnvironmentsParams, body SetTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // AddTeamEnvironmentsWithBody Add an allowed environment to a team + // + // The given environments will be added to the specified team. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/teams/{key}/environments/add (the `AddTeamEnvironments` operationId). + AddTeamEnvironmentsWithBody(ctx context.Context, key string, params *AddTeamEnvironmentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // AddTeamEnvironments Add an allowed environment to a team + // + // The given environments will be added to the specified team. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/teams/{key}/environments/add (the `AddTeamEnvironments` operationId). + AddTeamEnvironments(ctx context.Context, key string, params *AddTeamEnvironmentsParams, body AddTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RemoveTeamEnvironmentsWithBody Remove allowed environment from a team + // + // The given environments will be removed from the specified team. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/teams/{key}/environments/remove (the `RemoveTeamEnvironments` operationId). + RemoveTeamEnvironmentsWithBody(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RemoveTeamEnvironments Remove allowed environment from a team + // + // The given environments will be removed from the specified team. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/teams/{key}/environments/remove (the `RemoveTeamEnvironments` operationId). + RemoveTeamEnvironments(ctx context.Context, key string, body RemoveTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTeamMembers Get all members being part of the team + // + // Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). + // + // Corresponds with GET /api/teams/{key}/members (the `GetTeamMembers` operationId). + GetTeamMembers(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetTeamMembersWithBody Update the members of a specific team + // + // The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. + // + // Takes any type of body and a specified content type. + // + // Corresponds with PUT /api/teams/{key}/members (the `SetTeamMembers` operationId). + SetTeamMembersWithBody(ctx context.Context, key string, params *SetTeamMembersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetTeamMembers Update the members of a specific team + // + // The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with PUT /api/teams/{key}/members (the `SetTeamMembers` operationId). + SetTeamMembers(ctx context.Context, key string, params *SetTeamMembersParams, body SetTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // AddTeamMembersWithBody Add team members to a team + // + // The given members will be added to the specified team. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/teams/{key}/members/add (the `AddTeamMembers` operationId). + AddTeamMembersWithBody(ctx context.Context, key string, params *AddTeamMembersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // AddTeamMembers Add team members to a team + // + // The given members will be added to the specified team. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/teams/{key}/members/add (the `AddTeamMembers` operationId). + AddTeamMembers(ctx context.Context, key string, params *AddTeamMembersParams, body AddTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RemoveTeamMembersWithBody Remove team members from a team + // + // The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/teams/{key}/members/remove (the `RemoveTeamMembers` operationId). + RemoveTeamMembersWithBody(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RemoveTeamMembers Remove team members from a team + // + // The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/teams/{key}/members/remove (the `RemoveTeamMembers` operationId). + RemoveTeamMembers(ctx context.Context, key string, body RemoveTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // InviteUserWithBody Invite users to a tenant + // + // Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. + // + // Takes any type of body and a specified content type. + // + // Corresponds with POST /api/users/invite (the `InviteUser` operationId). + InviteUserWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // InviteUser Invite users to a tenant + // + // Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. + // + // Takes a body of the `application/json` content type. + // + // Corresponds with POST /api/users/invite (the `InviteUser` operationId). + InviteUser(ctx context.Context, body InviteUserJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +// GetAccessTokens Get access token list +// +// Deprecated, use v2 instead. Get a list of all access tokens. The access token itself is abbreviated for security reasons. Access tokens with v2 features are not returned, as they can not be represented cleanly in the old format. +// +// Corresponds with GET /api/access-tokens (the `GetAccessTokens` operationId). +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *Client) GetAccessTokens(ctx context.Context, params *GetAccessTokensParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetAccessTokensRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateAccessTokenWithBody Add a access token +// +// Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/access-tokens (the `CreateAccessToken` operationId). +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *Client) CreateAccessTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateAccessTokenRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateAccessToken Add a access token +// +// Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/access-tokens (the `CreateAccessToken` operationId). +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *Client) CreateAccessToken(ctx context.Context, body CreateAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateAccessTokenRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetAccessTokens1 Get access token list +// +// Get a list of all access tokens. The access token itself is abbreviated for security reasons. +// +// Corresponds with GET /api/access-tokens/v2 (the `GetAccessTokens1` operationId). +func (c *Client) GetAccessTokens1(ctx context.Context, params *GetAccessTokens1Params, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetAccessTokens1Request(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateAccessToken1WithBody Create an access token +// +// Generate a new access token. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/access-tokens/v2 (the `CreateAccessToken1` operationId). +func (c *Client) CreateAccessToken1WithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateAccessToken1RequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateAccessToken1 Create an access token +// +// Generate a new access token. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/access-tokens/v2 (the `CreateAccessToken1` operationId). +func (c *Client) CreateAccessToken1(ctx context.Context, body CreateAccessToken1JSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateAccessToken1Request(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteAccessToken1 Delete access token +// +// Remove the access token. After that, the access token can't be used anymore. +// +// Corresponds with DELETE /api/access-tokens/v2/{id} (the `DeleteAccessToken1` operationId). +func (c *Client) DeleteAccessToken1(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteAccessToken1Request(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// RecreateAccessTokenWithBody Recreate an access token +// +// Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/access-tokens/v2/{id}/recreate (the `RecreateAccessToken` operationId). +func (c *Client) RecreateAccessTokenWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRecreateAccessTokenRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// RecreateAccessToken Recreate an access token +// +// Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/access-tokens/v2/{id}/recreate (the `RecreateAccessToken` operationId). +func (c *Client) RecreateAccessToken(ctx context.Context, id string, body RecreateAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRecreateAccessTokenRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteAccessToken Delete access token +// +// Remove the access token associated. After that, the access token can't be used anymore for e.g. creating a new experiment or running an experiment. +// +// Corresponds with DELETE /api/access-tokens/{id} (the `DeleteAccessToken` operationId). +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *Client) DeleteAccessToken(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteAccessTokenRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// FindAllActions Get all actions. +// +// Corresponds with GET /api/actions (the `FindAllActions` operationId). +func (c *Client) FindAllActions(ctx context.Context, params *FindAllActionsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewFindAllActionsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetAction Fetch a single action description +// +// Get action including their parameters. +// +// Corresponds with GET /api/actions/{actionId} (the `GetAction` operationId). +func (c *Client) GetAction(ctx context.Context, actionId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetActionRequest(c.Server, actionId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTargetAdviceSummaryWithBody Get all currently active advice for a given environment and query. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/advice (the `GetTargetAdviceSummary` operationId). +func (c *Client) GetTargetAdviceSummaryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTargetAdviceSummaryRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTargetAdviceSummary Get all currently active advice for a given environment and query. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/advice (the `GetTargetAdviceSummary` operationId). +func (c *Client) GetTargetAdviceSummary(ctx context.Context, body GetTargetAdviceSummaryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTargetAdviceSummaryRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// Find Get all audit log entries +// +// Retrieve all audit logs in the given time-frame.
This endpoint requires an admin-token and can't be used with a team-based token. +// +// Corresponds with GET /api/audit-log (the `Find` operationId). +func (c *Client) Find(ctx context.Context, params *FindParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewFindRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// ForwardToPlatform Forward to Steadybit platform to either create an experiment associated to the `tag` or forward to the experiments linked already to the `tag` +// +// This endpoint can be used as a link for the badge of the `/api/badges/linked-badge.svg` API to either create a new experiment or show the linked experiments in Steadybit. This will help to link it correctly e.g. in your CMS-systems. +// +// Corresponds with GET /api/badges/link (the `ForwardToPlatform` operationId). +func (c *Client) ForwardToPlatform(ctx context.Context, params *ForwardToPlatformParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewForwardToPlatformRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetLinkedBadge Get badge for create experiment or run status as SVG image +// +// Creates an image badge that is either for creating a new experiment linked to an `externalReference` or - if an experiment with the given `externalReference` already exists - a badge showing the run status of the experiment. The badge is return as SVG to integrate it nicely e.g. into your CMS-systems. You can use the `/api/badges/link` endpoint to link it appropriately +// +// Corresponds with GET /api/badges/linked-badge.svg (the `GetLinkedBadge` operationId). +func (c *Client) GetLinkedBadge(ctx context.Context, params *GetLinkedBadgeParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetLinkedBadgeRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetEnvironments Fetch a list of all environments +// +// Get a list of all environments that exist. +// +// Corresponds with GET /api/environments (the `GetEnvironments` operationId). +func (c *Client) GetEnvironments(ctx context.Context, params *GetEnvironmentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetEnvironmentsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertEnvironmentWithBody Create or update an environment +// +// Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/environments (the `UpsertEnvironment` operationId). +func (c *Client) UpsertEnvironmentWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertEnvironmentRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertEnvironment Create or update an environment +// +// Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/environments (the `UpsertEnvironment` operationId). +func (c *Client) UpsertEnvironment(ctx context.Context, body UpsertEnvironmentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertEnvironmentRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteEnvironment Delete environment +// +// Remove the given environment from the Steadybit platform. +// +// Corresponds with DELETE /api/environments/{id} (the `DeleteEnvironment` operationId). +func (c *Client) DeleteEnvironment(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteEnvironmentRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetEnvironment Fetch a single environment +// +// Get all details of a single existing environment. +// +// Corresponds with GET /api/environments/{id} (the `GetEnvironment` operationId). +func (c *Client) GetEnvironment(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetEnvironmentRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetEnvironmentVariables Get environment variables +// +// Get all environment variables associated to a single environment. +// +// Corresponds with GET /api/environments/{id}/variables (the `GetEnvironmentVariables` operationId). +func (c *Client) GetEnvironmentVariables(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetEnvironmentVariablesRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetEnvironmentVariablesWithBody Replace all environment variables +// +// All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/environments/{id}/variables (the `SetEnvironmentVariables` operationId). +func (c *Client) SetEnvironmentVariablesWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetEnvironmentVariablesRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetEnvironmentVariables Replace all environment variables +// +// All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/environments/{id}/variables (the `SetEnvironmentVariables` operationId). +func (c *Client) SetEnvironmentVariables(ctx context.Context, id openapi_types.UUID, body SetEnvironmentVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetEnvironmentVariablesRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateEnvironmentVariablesWithBody Add / merge all environment variables +// +// All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /api/environments/{id}/variables (the `UpdateEnvironmentVariables` operationId). +func (c *Client) UpdateEnvironmentVariablesWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateEnvironmentVariablesRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateEnvironmentVariables Add / merge all environment variables +// +// All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /api/environments/{id}/variables (the `UpdateEnvironmentVariables` operationId). +func (c *Client) UpdateEnvironmentVariables(ctx context.Context, id openapi_types.UUID, body UpdateEnvironmentVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateEnvironmentVariablesRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperiments Fetch a list of all experiments +// +// Get a list of all experiments that exist. +// +// Corresponds with GET /api/experiments (the `GetExperiments` operationId). +func (c *Client) GetExperiments(ctx context.Context, params *GetExperimentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateOrUpdateExperimentWithBody Create or update an experiment +// +// Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments (the `CreateOrUpdateExperiment` operationId). +func (c *Client) CreateOrUpdateExperimentWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateOrUpdateExperimentRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateOrUpdateExperiment Create or update an experiment +// +// Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments (the `CreateOrUpdateExperiment` operationId). +func (c *Client) CreateOrUpdateExperiment(ctx context.Context, body CreateOrUpdateExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateOrUpdateExperimentRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SaveAndRunWithBody Save and run experiment +// +// Save the given experiment and immediately run it. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/execute (the `SaveAndRun` operationId). +func (c *Client) SaveAndRunWithBody(ctx context.Context, params *SaveAndRunParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSaveAndRunRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SaveAndRun Save and run experiment +// +// Save the given experiment and immediately run it. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/execute (the `SaveAndRun` operationId). +func (c *Client) SaveAndRun(ctx context.Context, params *SaveAndRunParams, body SaveAndRunJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSaveAndRunRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentExecutions1 Fetch a list of all experiment executions +// +// Get a list of all experiment executions that exist. +// +// Corresponds with GET /api/experiments/executions (the `GetExperimentExecutions1` operationId). +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *Client) GetExperimentExecutions1(ctx context.Context, params *GetExperimentExecutions1Params, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentExecutions1Request(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentExecutions2WithBody Fetch a list of experiment executions +// +// Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/executions (the `GetExperimentExecutions2` operationId). +func (c *Client) GetExperimentExecutions2WithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentExecutions2RequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentExecutions2 Fetch a list of experiment executions +// +// Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/executions (the `GetExperimentExecutions2` operationId). +func (c *Client) GetExperimentExecutions2(ctx context.Context, body GetExperimentExecutions2JSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentExecutions2Request(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentExecution Fetch a single experiment executions of a single experiment +// +// Get a single experiment execution that was performed for a specific experiment. +// +// Corresponds with GET /api/experiments/executions/{id} (the `GetExperimentExecution` operationId). +func (c *Client) GetExperimentExecution(ctx context.Context, id int64, params *GetExperimentExecutionParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentExecutionRequest(c.Server, id, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetArtifact performs a GET /api/experiments/executions/{id}/artifacts/{targetExecutionId}/{artifactId} (the `GetArtifact` operationId) request. +func (c *Client) GetArtifact(ctx context.Context, id int64, targetExecutionId string, artifactId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetArtifactRequest(c.Server, id, targetExecutionId, artifactId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CancelExperimentExecution Cancel a running experiment execution of a single experiment +// +// Cancels a currently running experiment execution to be stopped as soon as possible. +// +// Corresponds with POST /api/experiments/executions/{id}/cancel (the `CancelExperimentExecution` operationId). +func (c *Client) CancelExperimentExecution(ctx context.Context, id int64, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCancelExperimentExecutionRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateExecutionPropertiesWithBody Update properties of an experiment execution +// +// Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/executions/{id}/properties (the `UpdateExecutionProperties` operationId). +func (c *Client) UpdateExecutionPropertiesWithBody(ctx context.Context, id int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateExecutionPropertiesRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateExecutionProperties Update properties of an experiment execution +// +// Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/executions/{id}/properties (the `UpdateExecutionProperties` operationId). +func (c *Client) UpdateExecutionProperties(ctx context.Context, id int64, body UpdateExecutionPropertiesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateExecutionPropertiesRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// AddExecutionPropertyValueWithBody Add a single value to a list property of an experiment execution. +// +// This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/executions/{id}/properties/{key}/add (the `AddExecutionPropertyValue` operationId). +func (c *Client) AddExecutionPropertyValueWithBody(ctx context.Context, id int64, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewAddExecutionPropertyValueRequestWithBody(c.Server, id, key, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// AddExecutionPropertyValue Add a single value to a list property of an experiment execution. +// +// This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/executions/{id}/properties/{key}/add (the `AddExecutionPropertyValue` operationId). +func (c *Client) AddExecutionPropertyValue(ctx context.Context, id int64, key string, body AddExecutionPropertyValueJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewAddExecutionPropertyValueRequest(c.Server, id, key, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetExecutionPropertyValueWithBody Set the value of a property of an experiment execution. +// +// Only properties with `editableInExecution` set to `true` can be modified. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/executions/{id}/properties/{key}/set (the `SetExecutionPropertyValue` operationId). +func (c *Client) SetExecutionPropertyValueWithBody(ctx context.Context, id int64, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetExecutionPropertyValueRequestWithBody(c.Server, id, key, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetExecutionPropertyValue Set the value of a property of an experiment execution. +// +// Only properties with `editableInExecution` set to `true` can be modified. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/executions/{id}/properties/{key}/set (the `SetExecutionPropertyValue` operationId). +func (c *Client) SetExecutionPropertyValue(ctx context.Context, id int64, key string, body SetExecutionPropertyValueJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetExecutionPropertyValueRequest(c.Server, id, key, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertScheduleWithBody Create or update an experiment schedule +// +// Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/schedules (the `UpsertSchedule` operationId). +func (c *Client) UpsertScheduleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertScheduleRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertSchedule Create or update an experiment schedule +// +// Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/schedules (the `UpsertSchedule` operationId). +func (c *Client) UpsertSchedule(ctx context.Context, body UpsertScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertScheduleRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetAllSchedulesV2 Get all current experiment schedule configurations +// +// Corresponds with GET /api/experiments/schedules/v2 (the `GetAllSchedulesV2` operationId). +func (c *Client) GetAllSchedulesV2(ctx context.Context, params *GetAllSchedulesV2Params, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetAllSchedulesV2Request(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// RemoveExperimentScheduleById Remove an existing experiment schedule +// +// Corresponds with DELETE /api/experiments/schedules/{id} (the `RemoveExperimentScheduleById` operationId). +func (c *Client) RemoveExperimentScheduleById(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRemoveExperimentScheduleByIdRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetSchedules Get experiment schedules for a specific experiment schedule id +// +// Corresponds with GET /api/experiments/schedules/{id} (the `GetSchedules` operationId). +func (c *Client) GetSchedules(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetSchedulesRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// PatchScheduleWithBody Partially update an experiment schedule +// +// Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PATCH /api/experiments/schedules/{id} (the `PatchSchedule` operationId). +func (c *Client) PatchScheduleWithBody(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchScheduleRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// PatchSchedule Partially update an experiment schedule +// +// Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PATCH /api/experiments/schedules/{id} (the `PatchSchedule` operationId). +func (c *Client) PatchSchedule(ctx context.Context, id string, body PatchScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchScheduleRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentTemplates Fetch a list of all templates +// +// Get a list of all templates that exist. +// +// Corresponds with GET /api/experiments/templates (the `GetExperimentTemplates` operationId). +func (c *Client) GetExperimentTemplates(ctx context.Context, params *GetExperimentTemplatesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentTemplatesRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertExperimentTemplateWithBody Create or update an experiment template +// +// Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/templates (the `UpsertExperimentTemplate` operationId). +func (c *Client) UpsertExperimentTemplateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertExperimentTemplateRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertExperimentTemplate Create or update an experiment template +// +// Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/templates (the `UpsertExperimentTemplate` operationId). +func (c *Client) UpsertExperimentTemplate(ctx context.Context, body UpsertExperimentTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertExperimentTemplateRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// ImportFromHubWithBody Import experiment templates +// +// Import experiment templates with given IDs from linked hub. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/templates/imports (the `ImportFromHub` operationId). +func (c *Client) ImportFromHubWithBody(ctx context.Context, params *ImportFromHubParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewImportFromHubRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// ImportFromHub Import experiment templates +// +// Import experiment templates with given IDs from linked hub. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/templates/imports (the `ImportFromHub` operationId). +func (c *Client) ImportFromHub(ctx context.Context, params *ImportFromHubParams, body ImportFromHubJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewImportFromHubRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteExperimentTemplate Delete experiment template +// +// Remove the given experiment template from the Steadybit platform. If this template is used in a service profile, it will be removed from the profile and all provided service experiments will get deleted. +// +// Corresponds with DELETE /api/experiments/templates/{id} (the `DeleteExperimentTemplate` operationId). +func (c *Client) DeleteExperimentTemplate(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteExperimentTemplateRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentTemplate Fetch a single experiment template +// +// Get all details of a single existing experiment template. +// +// Corresponds with GET /api/experiments/templates/{id} (the `GetExperimentTemplate` operationId). +func (c *Client) GetExperimentTemplate(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentTemplateRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateExperimentByTemplateWithBody Create an experiment based on an experiment template +// +// Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-create (the `CreateExperimentByTemplate` operationId). +func (c *Client) CreateExperimentByTemplateWithBody(ctx context.Context, id openapi_types.UUID, params *CreateExperimentByTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateExperimentByTemplateRequestWithBody(c.Server, id, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateExperimentByTemplate Create an experiment based on an experiment template +// +// Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-create (the `CreateExperimentByTemplate` operationId). +func (c *Client) CreateExperimentByTemplate(ctx context.Context, id openapi_types.UUID, params *CreateExperimentByTemplateParams, body CreateExperimentByTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateExperimentByTemplateRequest(c.Server, id, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SaveAndRunFromTemplateWithBody Create an experiment based on an experiment template and run experiment +// +// Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-execute (the `SaveAndRunFromTemplate` operationId). +func (c *Client) SaveAndRunFromTemplateWithBody(ctx context.Context, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSaveAndRunFromTemplateRequestWithBody(c.Server, id, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SaveAndRunFromTemplate Create an experiment based on an experiment template and run experiment +// +// Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-execute (the `SaveAndRunFromTemplate` operationId). +func (c *Client) SaveAndRunFromTemplate(ctx context.Context, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, body SaveAndRunFromTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSaveAndRunFromTemplateRequest(c.Server, id, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateExperimentByTemplateWithBody Update an existing experiment based on a template +// +// Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-update/{key} (the `UpdateExperimentByTemplate` operationId). +func (c *Client) UpdateExperimentByTemplateWithBody(ctx context.Context, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateExperimentByTemplateRequestWithBody(c.Server, id, key, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateExperimentByTemplate Update an existing experiment based on a template +// +// Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-update/{key} (the `UpdateExperimentByTemplate` operationId). +func (c *Client) UpdateExperimentByTemplate(ctx context.Context, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, body UpdateExperimentByTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateExperimentByTemplateRequest(c.Server, id, key, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteExperiment Delete experiment +// +// Remove the given experiment. The associated number is still reserved afterwards and will not be reused. +// +// Corresponds with DELETE /api/experiments/{key} (the `DeleteExperiment` operationId). +func (c *Client) DeleteExperiment(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteExperimentRequest(c.Server, key) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperiment Fetch a single experiment +// +// Get all details of a single existing experiment. +// +// Corresponds with GET /api/experiments/{key} (the `GetExperiment` operationId). +func (c *Client) GetExperiment(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentRequest(c.Server, key) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateExperimentWithBody Update an experiment +// +// Update the experiment identified by the experiment `key`. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/{key} (the `UpdateExperiment` operationId). +func (c *Client) UpdateExperimentWithBody(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateExperimentRequestWithBody(c.Server, key, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateExperiment Update an experiment +// +// Update the experiment identified by the experiment `key`. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/{key} (the `UpdateExperiment` operationId). +func (c *Client) UpdateExperiment(ctx context.Context, key string, body UpdateExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateExperimentRequest(c.Server, key, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentBadge Get experiment run status as SVG image +// +// Get the status of the latest experiment run of the associated experiment as SVG to integrate it nicely e.g. into your CMS-systems. +// +// Corresponds with GET /api/experiments/{key}/badge.svg (the `GetExperimentBadge` operationId). +func (c *Client) GetExperimentBadge(ctx context.Context, key string, params *GetExperimentBadgeParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentBadgeRequest(c.Server, key, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// ExecuteExperimentWithBody Execute an experiment +// +// Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. +// +// Examples: +// - Override environment from the experiment for a single run: +// ``` +// { +// "environment": "Shop Stage" +// } +// ``` +// - Override the variables for a single execution: +// ``` +// { +// "variables": { +// "httpEndpoint": "http://dev.shop.products.internal" +// } +// } +// ``` +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/experiments/{key}/execute (the `ExecuteExperiment` operationId). +func (c *Client) ExecuteExperimentWithBody(ctx context.Context, key string, params *ExecuteExperimentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewExecuteExperimentRequestWithBody(c.Server, key, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// ExecuteExperiment Execute an experiment +// +// Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. +// +// Examples: +// - Override environment from the experiment for a single run: +// ``` +// { +// "environment": "Shop Stage" +// } +// ``` +// - Override the variables for a single execution: +// ``` +// { +// "variables": { +// "httpEndpoint": "http://dev.shop.products.internal" +// } +// } +// ``` +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/experiments/{key}/execute (the `ExecuteExperiment` operationId). +func (c *Client) ExecuteExperiment(ctx context.Context, key string, params *ExecuteExperimentParams, body ExecuteExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewExecuteExperimentRequest(c.Server, key, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentExecutions3 Fetch a list of all experiment executions of a single experiment +// +// Get a list of all experiment executions that were performed for a specific experiment. +// +// Corresponds with GET /api/experiments/{key}/executions (the `GetExperimentExecutions3` operationId). +func (c *Client) GetExperimentExecutions3(ctx context.Context, key string, params *GetExperimentExecutions3Params, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentExecutions3Request(c.Server, key, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetLandscapeViews Fetch all saved landscape views of a team +// +// Get a list of all saved explorer landscape views that belong to the given team. +// +// Corresponds with GET /api/explore/landscape/views (the `GetLandscapeViews` operationId). +func (c *Client) GetLandscapeViews(ctx context.Context, params *GetLandscapeViewsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetLandscapeViewsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateLandscapeViewWithBody Create a saved landscape view +// +// Create a new saved explorer landscape view for a team. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/explore/landscape/views (the `CreateLandscapeView` operationId). +func (c *Client) CreateLandscapeViewWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateLandscapeViewRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// CreateLandscapeView Create a saved landscape view +// +// Create a new saved explorer landscape view for a team. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/explore/landscape/views (the `CreateLandscapeView` operationId). +func (c *Client) CreateLandscapeView(ctx context.Context, body CreateLandscapeViewJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateLandscapeViewRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteLandscapeView Delete a saved landscape view +// +// Remove the given saved explorer landscape view from the Steadybit platform. +// +// Corresponds with DELETE /api/explore/landscape/views/{id} (the `DeleteLandscapeView` operationId). +func (c *Client) DeleteLandscapeView(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteLandscapeViewRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetLandscapeView Fetch a single saved landscape view +// +// Get all details of a single saved explorer landscape view. +// +// Corresponds with GET /api/explore/landscape/views/{id} (the `GetLandscapeView` operationId). +func (c *Client) GetLandscapeView(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetLandscapeViewRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateLandscapeViewWithBody Update a saved landscape view +// +// Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /api/explore/landscape/views/{id} (the `UpdateLandscapeView` operationId). +func (c *Client) UpdateLandscapeViewWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateLandscapeViewRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpdateLandscapeView Update a saved landscape view +// +// Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /api/explore/landscape/views/{id} (the `UpdateLandscapeView` operationId). +func (c *Client) UpdateLandscapeView(ctx context.Context, id openapi_types.UUID, body UpdateLandscapeViewJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateLandscapeViewRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// Health performs a GET /api/health (the `Health` operationId) request. +func (c *Client) Health(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHealthRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// Liveness performs a GET /api/health/liveness (the `Liveness` operationId) request. +func (c *Client) Liveness(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewLivenessRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// Readiness performs a GET /api/health/readiness (the `Readiness` operationId) request. +func (c *Client) Readiness(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewReadinessRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetHubs Fetch a list of all hubs +// +// Get a list of all hubs that are currently connected. +// +// Corresponds with GET /api/hubs (the `GetHubs` operationId). +func (c *Client) GetHubs(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHubsRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertHubWithBody Create or update a hub +// +// Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/hubs (the `UpsertHub` operationId). +func (c *Client) UpsertHubWithBody(ctx context.Context, params *UpsertHubParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertHubRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertHub Create or update a hub +// +// Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/hubs (the `UpsertHub` operationId). +func (c *Client) UpsertHub(ctx context.Context, params *UpsertHubParams, body UpsertHubJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertHubRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// ConnectionCheckWithBody Check a hub connection +// +// Check if the given hub connection details point to a valid hub. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/hubs/connection-check (the `ConnectionCheck` operationId). +func (c *Client) ConnectionCheckWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewConnectionCheckRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// ConnectionCheck Check a hub connection +// +// Check if the given hub connection details point to a valid hub. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/hubs/connection-check (the `ConnectionCheck` operationId). +func (c *Client) ConnectionCheck(ctx context.Context, body ConnectionCheckJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewConnectionCheckRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteHub Delete a hub +// +// Remove the given hub. +// +// Corresponds with DELETE /api/hubs/{id} (the `DeleteHub` operationId). +func (c *Client) DeleteHub(ctx context.Context, id openapi_types.UUID, params *DeleteHubParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteHubRequest(c.Server, id, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetHubById Fetch a single hub +// +// Get all details of a single hub. +// +// Corresponds with GET /api/hubs/{id} (the `GetHubById` operationId). +func (c *Client) GetHubById(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHubByIdRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// ResyncHub Re-synchronize a hub +// +// Fetch the latest hub definition based on `hubRepository`. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. +// +// Corresponds with POST /api/hubs/{id}/resync (the `ResyncHub` operationId). +func (c *Client) ResyncHub(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewResyncHubRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetPreflightWebhooks Fetch a list of preflight webhooks +// +// Get a list of all existing preflight webhooks. +// +// Corresponds with GET /api/integrations/preflight (the `GetPreflightWebhooks` operationId). +func (c *Client) GetPreflightWebhooks(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetPreflightWebhooksRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertPreflightWebhookWithBody Create or update a preflight webhook +// +// Insert or update a preflight webhook.
Experiment runs that were not executed due to engaged / active kill switch will not be automatically executed, they need to be triggered again. +// +// Corresponds with DELETE /api/killswitch (the `DisengageKillswitch` operationId). +func (c *Client) DisengageKillswitch(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDisengageKillswitchRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetKillswitch Get the current status of the kill switch +// +// Determines the current status of the kill switch without changing it. +// +// Corresponds with GET /api/killswitch (the `GetKillswitch` operationId). +func (c *Client) GetKillswitch(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetKillswitchRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// EngageKillswitch Activate / engage the kill switch +// +// Activates / engages the kill switch to cancel all experiments running at the moment and prevent execution of new experiments until the kill switch is disengaged / deactivated again. +// +// Corresponds with POST /api/killswitch (the `EngageKillswitch` operationId). +func (c *Client) EngageKillswitch(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewEngageKillswitchRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetLicenseSummary Get license summary. +// +// Corresponds with GET /api/license (the `GetLicenseSummary` operationId). +func (c *Client) GetLicenseSummary(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetLicenseSummaryRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetReport Get license report. +// +// Corresponds with GET /api/license/report (the `GetReport` operationId). +func (c *Client) GetReport(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetReportRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetPreflightActionSummary Get all preflight actions. +// +// Corresponds with GET /api/preflight/actions (the `GetPreflightActionSummary` operationId). +func (c *Client) GetPreflightActionSummary(ctx context.Context, params *GetPreflightActionSummaryParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetPreflightActionSummaryRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetAssociations Get all current associations. +// +// Corresponds with GET /api/properties/associations (the `GetAssociations` operationId). +func (c *Client) GetAssociations(ctx context.Context, params *GetAssociationsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetAssociationsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertPropertyAssociationWithBody Create or update a property association +// +// Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. +// +// Examples: +// - Assign the property `RESULT_COLOR` to all experiment designs: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": false, +// "required": true +// } +// ``` +// - Assign the property `RESULT_COLOR` to the design ADM-15: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": false, +// "experimentKey": "ADM-15", +// "required": true +// } +// ``` +// - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": true, +// "experimentKey": "ADM-15", +// "required": false +// } +// ``` +// - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: +// ``` +// { +// "key": "RESULT_COLOR", +// "associationType": "SERVICE", +// "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", +// "required": false +// } +// ``` +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/properties/associations (the `UpsertPropertyAssociation` operationId). +func (c *Client) UpsertPropertyAssociationWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertPropertyAssociationRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertPropertyAssociation Create or update a property association +// +// Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. +// +// Examples: +// - Assign the property `RESULT_COLOR` to all experiment designs: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": false, +// "required": true +// } +// ``` +// - Assign the property `RESULT_COLOR` to the design ADM-15: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": false, +// "experimentKey": "ADM-15", +// "required": true +// } +// ``` +// - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": true, +// "experimentKey": "ADM-15", +// "required": false +// } +// ``` +// - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: +// ``` +// { +// "key": "RESULT_COLOR", +// "associationType": "SERVICE", +// "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", +// "required": false +// } +// ``` +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/properties/associations (the `UpsertPropertyAssociation` operationId). +func (c *Client) UpsertPropertyAssociation(ctx context.Context, body UpsertPropertyAssociationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertPropertyAssociationRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeletePropertyAssociation Remove an existing property association. +// +// Corresponds with DELETE /api/properties/associations/{id} (the `DeletePropertyAssociation` operationId). +func (c *Client) DeletePropertyAssociation(ctx context.Context, id openapi_types.UUID, params *DeletePropertyAssociationParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeletePropertyAssociationRequest(c.Server, id, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetPropertyDefinition1 Get property association by a given id. +// +// Corresponds with GET /api/properties/associations/{id} (the `GetPropertyDefinition1` operationId). +func (c *Client) GetPropertyDefinition1(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetPropertyDefinition1Request(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetPropertyDefinitions performs a GET /api/properties/definitions (the `GetPropertyDefinitions` operationId) request. +func (c *Client) GetPropertyDefinitions(ctx context.Context, params *GetPropertyDefinitionsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetPropertyDefinitionsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertPropertyDefinitionWithBody Create or update property definition +// +// Insert or update the property definition specified by the given `key`. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/properties/definitions (the `UpsertPropertyDefinition` operationId). +func (c *Client) UpsertPropertyDefinitionWithBody(ctx context.Context, params *UpsertPropertyDefinitionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertPropertyDefinitionRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertPropertyDefinition Create or update property definition +// +// Insert or update the property definition specified by the given `key`. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/properties/definitions (the `UpsertPropertyDefinition` operationId). +func (c *Client) UpsertPropertyDefinition(ctx context.Context, params *UpsertPropertyDefinitionParams, body UpsertPropertyDefinitionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertPropertyDefinitionRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeletePropertyDefinition Remove an existing property definition +// +// Corresponds with DELETE /api/properties/definitions/{key} (the `DeletePropertyDefinition` operationId). +func (c *Client) DeletePropertyDefinition(ctx context.Context, key string, params *DeletePropertyDefinitionParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeletePropertyDefinitionRequest(c.Server, key, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetPropertyDefinition Get property definition for a specific property definition key. +// +// Corresponds with GET /api/properties/definitions/{key} (the `GetPropertyDefinition` operationId). +func (c *Client) GetPropertyDefinition(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetPropertyDefinitionRequest(c.Server, key) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetEnvironmentCountsWithBody Get environment counts over time +// +// Returns the number of environments in the tenant aggregated into time buckets. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/reports/environments (the `GetEnvironmentCounts` operationId). +func (c *Client) GetEnvironmentCountsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetEnvironmentCountsRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetEnvironmentCounts Get environment counts over time +// +// Returns the number of environments in the tenant aggregated into time buckets. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/reports/environments (the `GetEnvironmentCounts` operationId). +func (c *Client) GetEnvironmentCounts(ctx context.Context, body GetEnvironmentCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetEnvironmentCountsRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentCreationsWithBody Get experiment creation counts over time +// +// Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/reports/experiments/created (the `GetExperimentCreations` operationId). +func (c *Client) GetExperimentCreationsWithBody(ctx context.Context, params *GetExperimentCreationsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentCreationsRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentCreations Get experiment creation counts over time +// +// Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/reports/experiments/created (the `GetExperimentCreations` operationId). +func (c *Client) GetExperimentCreations(ctx context.Context, params *GetExperimentCreationsParams, body GetExperimentCreationsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentCreationsRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentExecutionsWithBody Get experiment execution counts over time +// +// Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/reports/experiments/executed (the `GetExperimentExecutions` operationId). +func (c *Client) GetExperimentExecutionsWithBody(ctx context.Context, params *GetExperimentExecutionsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentExecutionsRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetExperimentExecutions Get experiment execution counts over time +// +// Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/reports/experiments/executed (the `GetExperimentExecutions` operationId). +func (c *Client) GetExperimentExecutions(ctx context.Context, params *GetExperimentExecutionsParams, body GetExperimentExecutionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetExperimentExecutionsRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetAverageRiskWithBody Get average service risk over time +// +// Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/reports/services/average (the `GetAverageRisk` operationId). +func (c *Client) GetAverageRiskWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetAverageRiskRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetAverageRisk Get average service risk over time +// +// Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/reports/services/average (the `GetAverageRisk` operationId). +func (c *Client) GetAverageRisk(ctx context.Context, body GetAverageRiskJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetAverageRiskRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetRiskByCategoryWithBody Get average service risk grouped by category over time +// +// Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/reports/services/by-category (the `GetRiskByCategory` operationId). +func (c *Client) GetRiskByCategoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetRiskByCategoryRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetRiskByCategory Get average service risk grouped by category over time +// +// Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/reports/services/by-category (the `GetRiskByCategory` operationId). +func (c *Client) GetRiskByCategory(ctx context.Context, body GetRiskByCategoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetRiskByCategoryRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetRiskDistributionWithBody Get service risk level distribution over time +// +// Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/reports/services/distribution (the `GetRiskDistribution` operationId). +func (c *Client) GetRiskDistributionWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetRiskDistributionRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetRiskDistribution Get service risk level distribution over time +// +// Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/reports/services/distribution (the `GetRiskDistribution` operationId). +func (c *Client) GetRiskDistribution(ctx context.Context, body GetRiskDistributionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetRiskDistributionRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTeamCountsWithBody Get team counts over time +// +// Returns the number of teams in the tenant aggregated into time buckets. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/reports/teams (the `GetTeamCounts` operationId). +func (c *Client) GetTeamCountsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTeamCountsRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTeamCounts Get team counts over time +// +// Returns the number of teams in the tenant aggregated into time buckets. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/reports/teams (the `GetTeamCounts` operationId). +func (c *Client) GetTeamCounts(ctx context.Context, body GetTeamCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTeamCountsRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetUserCountsWithBody Get user counts over time +// +// Returns the number of users in the tenant aggregated into time buckets. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/reports/users (the `GetUserCounts` operationId). +func (c *Client) GetUserCountsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetUserCountsRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetUserCounts Get user counts over time +// +// Returns the number of users in the tenant aggregated into time buckets. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/reports/users (the `GetUserCounts` operationId). +func (c *Client) GetUserCounts(ctx context.Context, body GetUserCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetUserCountsRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetServiceList Fetch a list of services +// +// Corresponds with GET /api/services (the `GetServiceList` operationId). +func (c *Client) GetServiceList(ctx context.Context, params *GetServiceListParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetServiceListRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertServiceWithBody Create or update service +// +// Insert or update the service specified by the given `id`. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/services (the `UpsertService` operationId). +func (c *Client) UpsertServiceWithBody(ctx context.Context, params *UpsertServiceParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertServiceRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertService Create or update service +// +// Insert or update the service specified by the given `id`. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/services (the `UpsertService` operationId). +func (c *Client) UpsertService(ctx context.Context, params *UpsertServiceParams, body UpsertServiceJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertServiceRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetProfiles Fetch a list of service profiles +// +// Corresponds with GET /api/services/profiles (the `GetProfiles` operationId). +func (c *Client) GetProfiles(ctx context.Context, params *GetProfilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetProfilesRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertProfileWithBody Create or update service profile +// +// Insert or update the service profile specified by the given `id`. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/services/profiles (the `UpsertProfile` operationId). +func (c *Client) UpsertProfileWithBody(ctx context.Context, params *UpsertProfileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertProfileRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertProfile Create or update service profile +// +// Insert or update the service profile specified by the given `id`. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/services/profiles (the `UpsertProfile` operationId). +func (c *Client) UpsertProfile(ctx context.Context, params *UpsertProfileParams, body UpsertProfileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertProfileRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteProfile Delete an existing service profile +// +// Corresponds with DELETE /api/services/profiles/{id} (the `DeleteProfile` operationId). +func (c *Client) DeleteProfile(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteProfileRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetProfile Get service profile by id. +// +// Corresponds with GET /api/services/profiles/{id} (the `GetProfile` operationId). +func (c *Client) GetProfile(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetProfileRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteService Delete an existing service +// +// Corresponds with DELETE /api/services/{id} (the `DeleteService` operationId). +func (c *Client) DeleteService(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteServiceRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetService Get service by id. +// +// Corresponds with GET /api/services/{id} (the `GetService` operationId). +func (c *Client) GetService(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetServiceRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetServiceExperiments Get experiments associated to an service. +// +// Corresponds with GET /api/services/{id}/experiments (the `GetServiceExperiments` operationId). +func (c *Client) GetServiceExperiments(ctx context.Context, id openapi_types.UUID, params *GetServiceExperimentsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetServiceExperimentsRequest(c.Server, id, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UnlinkCustomExperiment Remove a linked custom experiment from a service. +// +// Corresponds with DELETE /api/services/{id}/experiments/custom (the `UnlinkCustomExperiment` operationId). +func (c *Client) UnlinkCustomExperiment(ctx context.Context, id openapi_types.UUID, params *UnlinkCustomExperimentParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUnlinkCustomExperimentRequest(c.Server, id, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// LinkCustomExperimentWithBody Link a custom experiment to a service. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/services/{id}/experiments/custom (the `LinkCustomExperiment` operationId). +func (c *Client) LinkCustomExperimentWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewLinkCustomExperimentRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// LinkCustomExperiment Link a custom experiment to a service. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/services/{id}/experiments/custom (the `LinkCustomExperiment` operationId). +func (c *Client) LinkCustomExperiment(ctx context.Context, id openapi_types.UUID, body LinkCustomExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewLinkCustomExperimentRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertProvidedExperimentWithBody Create or update a provided experiment. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/services/{id}/experiments/provided (the `UpsertProvidedExperiment` operationId). +func (c *Client) UpsertProvidedExperimentWithBody(ctx context.Context, id openapi_types.UUID, params *UpsertProvidedExperimentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertProvidedExperimentRequestWithBody(c.Server, id, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertProvidedExperiment Create or update a provided experiment. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/services/{id}/experiments/provided (the `UpsertProvidedExperiment` operationId). +func (c *Client) UpsertProvidedExperiment(ctx context.Context, id openapi_types.UUID, params *UpsertProvidedExperimentParams, body UpsertProvidedExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertProvidedExperimentRequest(c.Server, id, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetRisk Get the risk score for a service +// +// Corresponds with GET /api/services/{id}/risk (the `GetRisk` operationId). +func (c *Client) GetRisk(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetRiskRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetServiceVariables Get service variables +// +// Get all variables owned by the service. +// +// Corresponds with GET /api/services/{id}/variables (the `GetServiceVariables` operationId). +func (c *Client) GetServiceVariables(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetServiceVariablesRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// MergeServiceVariablesWithBody Add / merge service variables +// +// All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PATCH /api/services/{id}/variables (the `MergeServiceVariables` operationId). +func (c *Client) MergeServiceVariablesWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMergeServiceVariablesRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// MergeServiceVariables Add / merge service variables +// +// All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PATCH /api/services/{id}/variables (the `MergeServiceVariables` operationId). +func (c *Client) MergeServiceVariables(ctx context.Context, id openapi_types.UUID, body MergeServiceVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMergeServiceVariablesRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetServiceVariablesWithBody Replace all service variables +// +// All provided variables will be associated with the given service and existing ones removed. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /api/services/{id}/variables (the `SetServiceVariables` operationId). +func (c *Client) SetServiceVariablesWithBody(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetServiceVariablesRequestWithBody(c.Server, id, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetServiceVariables Replace all service variables +// +// All provided variables will be associated with the given service and existing ones removed. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /api/services/{id}/variables (the `SetServiceVariables` operationId). +func (c *Client) SetServiceVariables(ctx context.Context, id openapi_types.UUID, body SetServiceVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetServiceVariablesRequest(c.Server, id, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTargetsStats Gather target statistics without any filters +// +// Corresponds with GET /api/target-stats (the `GetTargetsStats` operationId). +func (c *Client) GetTargetsStats(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTargetsStatsRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTargetsStats1WithBody Gather target statistics for a given predicate or query +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/target-stats (the `GetTargetsStats1` operationId). +func (c *Client) GetTargetsStats1WithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTargetsStats1RequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTargetsStats1 Gather target statistics for a given predicate or query +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/target-stats (the `GetTargetsStats1` operationId). +func (c *Client) GetTargetsStats1(ctx context.Context, body GetTargetsStats1JSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTargetsStats1Request(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTargets Get targets +// +// Get targets. +// +// Corresponds with GET /api/targets (the `GetTargets` operationId). +func (c *Client) GetTargets(ctx context.Context, params *GetTargetsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTargetsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTargetAttributeKeys Get attribute key +// +// Get all available attribute keys for a specific target type in a given environment. +// +// Corresponds with GET /api/targets/attributes/keys (the `GetTargetAttributeKeys` operationId). +func (c *Client) GetTargetAttributeKeys(ctx context.Context, params *GetTargetAttributeKeysParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTargetAttributeKeysRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTargetAttributeValues Get attribute values +// +// Get all available attribute values for a specific attribute and target type in a given environment. +// +// Corresponds with GET /api/targets/attributes/values (the `GetTargetAttributeValues` operationId). +func (c *Client) GetTargetAttributeValues(ctx context.Context, params *GetTargetAttributeValuesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTargetAttributeValuesRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTeams Fetch a list of all teams +// +// Get a list of all teams that exist.
If used with a team-associated `accessToken` and `onlyAccessible` is set to `true` you only get the team of the `accessToken`. +// +// Corresponds with GET /api/teams (the `GetTeams` operationId). +func (c *Client) GetTeams(ctx context.Context, params *GetTeamsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTeamsRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertTeamWithBody Create or update a team +// +// Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/teams (the `UpsertTeam` operationId). +func (c *Client) UpsertTeamWithBody(ctx context.Context, params *UpsertTeamParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertTeamRequestWithBody(c.Server, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// UpsertTeam Create or update a team +// +// Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/teams (the `UpsertTeam` operationId). +func (c *Client) UpsertTeam(ctx context.Context, params *UpsertTeamParams, body UpsertTeamJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpsertTeamRequest(c.Server, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// DeleteTeam Delete team +// +// Remove the given team from the Steadybit platform. This will only work, if there are no experiments running at the moment. +// +// Corresponds with DELETE /api/teams/{key} (the `DeleteTeam` operationId). +func (c *Client) DeleteTeam(ctx context.Context, key string, params *DeleteTeamParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteTeamRequest(c.Server, key, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTeam Fetch a single team +// +// Get all details of a single existing teams. +// +// Corresponds with GET /api/teams/{key} (the `GetTeam` operationId). +func (c *Client) GetTeam(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTeamRequest(c.Server, key) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTeamEnvironments Get all environments assigned to the team +// +// Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). +// +// Corresponds with GET /api/teams/{key}/environments (the `GetTeamEnvironments` operationId). +func (c *Client) GetTeamEnvironments(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTeamEnvironmentsRequest(c.Server, key) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetTeamEnvironmentsWithBody Update the environments of a specific team +// +// The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /api/teams/{key}/environments (the `SetTeamEnvironments` operationId). +func (c *Client) SetTeamEnvironmentsWithBody(ctx context.Context, key string, params *SetTeamEnvironmentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetTeamEnvironmentsRequestWithBody(c.Server, key, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetTeamEnvironments Update the environments of a specific team +// +// The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /api/teams/{key}/environments (the `SetTeamEnvironments` operationId). +func (c *Client) SetTeamEnvironments(ctx context.Context, key string, params *SetTeamEnvironmentsParams, body SetTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetTeamEnvironmentsRequest(c.Server, key, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// AddTeamEnvironmentsWithBody Add an allowed environment to a team +// +// The given environments will be added to the specified team. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/teams/{key}/environments/add (the `AddTeamEnvironments` operationId). +func (c *Client) AddTeamEnvironmentsWithBody(ctx context.Context, key string, params *AddTeamEnvironmentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewAddTeamEnvironmentsRequestWithBody(c.Server, key, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// AddTeamEnvironments Add an allowed environment to a team +// +// The given environments will be added to the specified team. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/teams/{key}/environments/add (the `AddTeamEnvironments` operationId). +func (c *Client) AddTeamEnvironments(ctx context.Context, key string, params *AddTeamEnvironmentsParams, body AddTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewAddTeamEnvironmentsRequest(c.Server, key, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// RemoveTeamEnvironmentsWithBody Remove allowed environment from a team +// +// The given environments will be removed from the specified team. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/teams/{key}/environments/remove (the `RemoveTeamEnvironments` operationId). +func (c *Client) RemoveTeamEnvironmentsWithBody(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRemoveTeamEnvironmentsRequestWithBody(c.Server, key, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// RemoveTeamEnvironments Remove allowed environment from a team +// +// The given environments will be removed from the specified team. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/teams/{key}/environments/remove (the `RemoveTeamEnvironments` operationId). +func (c *Client) RemoveTeamEnvironments(ctx context.Context, key string, body RemoveTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRemoveTeamEnvironmentsRequest(c.Server, key, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// GetTeamMembers Get all members being part of the team +// +// Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). +// +// Corresponds with GET /api/teams/{key}/members (the `GetTeamMembers` operationId). +func (c *Client) GetTeamMembers(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTeamMembersRequest(c.Server, key) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetTeamMembersWithBody Update the members of a specific team +// +// The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. +// +// Takes any type of body and a specified content type. +// +// Corresponds with PUT /api/teams/{key}/members (the `SetTeamMembers` operationId). +func (c *Client) SetTeamMembersWithBody(ctx context.Context, key string, params *SetTeamMembersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetTeamMembersRequestWithBody(c.Server, key, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// SetTeamMembers Update the members of a specific team +// +// The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with PUT /api/teams/{key}/members (the `SetTeamMembers` operationId). +func (c *Client) SetTeamMembers(ctx context.Context, key string, params *SetTeamMembersParams, body SetTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetTeamMembersRequest(c.Server, key, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// AddTeamMembersWithBody Add team members to a team +// +// The given members will be added to the specified team. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/teams/{key}/members/add (the `AddTeamMembers` operationId). +func (c *Client) AddTeamMembersWithBody(ctx context.Context, key string, params *AddTeamMembersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewAddTeamMembersRequestWithBody(c.Server, key, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// AddTeamMembers Add team members to a team +// +// The given members will be added to the specified team. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/teams/{key}/members/add (the `AddTeamMembers` operationId). +func (c *Client) AddTeamMembers(ctx context.Context, key string, params *AddTeamMembersParams, body AddTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewAddTeamMembersRequest(c.Server, key, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// RemoveTeamMembersWithBody Remove team members from a team +// +// The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/teams/{key}/members/remove (the `RemoveTeamMembers` operationId). +func (c *Client) RemoveTeamMembersWithBody(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRemoveTeamMembersRequestWithBody(c.Server, key, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// RemoveTeamMembers Remove team members from a team +// +// The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/teams/{key}/members/remove (the `RemoveTeamMembers` operationId). +func (c *Client) RemoveTeamMembers(ctx context.Context, key string, body RemoveTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRemoveTeamMembersRequest(c.Server, key, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// InviteUserWithBody Invite users to a tenant +// +// Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. +// +// Takes any type of body and a specified content type. +// +// Corresponds with POST /api/users/invite (the `InviteUser` operationId). +func (c *Client) InviteUserWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewInviteUserRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// InviteUser Invite users to a tenant +// +// Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. +// +// Takes a body of the `application/json` content type. +// +// Corresponds with POST /api/users/invite (the `InviteUser` operationId). +func (c *Client) InviteUser(ctx context.Context, body InviteUserJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewInviteUserRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewGetAccessTokensRequest constructs an http.Request for the GetAccessTokens method +func NewGetAccessTokensRequest(server string, params *GetAccessTokensParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/access-tokens") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Type != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "type", *params.Type, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Team != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "team", *params.Team, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "pageRequest", params.PageRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "object", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCreateAccessTokenRequest calls the generic CreateAccessToken builder with application/json body +func NewCreateAccessTokenRequest(server string, body CreateAccessTokenJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateAccessTokenRequestWithBody(server, "application/json", bodyReader) +} + +// NewCreateAccessTokenRequestWithBody constructs an http.Request for the CreateAccessToken method, with any body, and a specified content type +func NewCreateAccessTokenRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/access-tokens") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetAccessTokens1Request constructs an http.Request for the GetAccessTokens1 method +func NewGetAccessTokens1Request(server string, params *GetAccessTokens1Params) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/access-tokens/v2") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Name != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "name", *params.Name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.CreatedBy != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "createdBy", *params.CreatedBy, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Type != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "type", *params.Type, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Teams != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "teams", *params.Teams, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Expired != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "expired", *params.Expired, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "pageRequest", params.PageRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "object", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCreateAccessToken1Request calls the generic CreateAccessToken1 builder with application/json body +func NewCreateAccessToken1Request(server string, body CreateAccessToken1JSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateAccessToken1RequestWithBody(server, "application/json", bodyReader) +} + +// NewCreateAccessToken1RequestWithBody constructs an http.Request for the CreateAccessToken1 method, with any body, and a specified content type +func NewCreateAccessToken1RequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/access-tokens/v2") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteAccessToken1Request constructs an http.Request for the DeleteAccessToken1 method +func NewDeleteAccessToken1Request(server string, id string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/access-tokens/v2/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewRecreateAccessTokenRequest calls the generic RecreateAccessToken builder with application/json body +func NewRecreateAccessTokenRequest(server string, id string, body RecreateAccessTokenJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRecreateAccessTokenRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewRecreateAccessTokenRequestWithBody constructs an http.Request for the RecreateAccessToken method, with any body, and a specified content type +func NewRecreateAccessTokenRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/access-tokens/v2/%s/recreate", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteAccessTokenRequest constructs an http.Request for the DeleteAccessToken method +func NewDeleteAccessTokenRequest(server string, id string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/access-tokens/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewFindAllActionsRequest constructs an http.Request for the FindAllActions method +func NewFindAllActionsRequest(server string, params *FindAllActionsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/actions") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Page != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", *params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Size != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "size", *params.Size, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetActionRequest constructs an http.Request for the GetAction method +func NewGetActionRequest(server string, actionId string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "actionId", actionId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/actions/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTargetAdviceSummaryRequest calls the generic GetTargetAdviceSummary builder with application/json body +func NewGetTargetAdviceSummaryRequest(server string, body GetTargetAdviceSummaryJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetTargetAdviceSummaryRequestWithBody(server, "application/json", bodyReader) +} + +// NewGetTargetAdviceSummaryRequestWithBody constructs an http.Request for the GetTargetAdviceSummary method, with any body, and a specified content type +func NewGetTargetAdviceSummaryRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/advice") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewFindRequest constructs an http.Request for the Find method +func NewFindRequest(server string, params *FindParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/audit-log") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.From != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "from", *params.From, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.To != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "to", *params.To, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewForwardToPlatformRequest constructs an http.Request for the ForwardToPlatform method +func NewForwardToPlatformRequest(server string, params *ForwardToPlatformParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/badges/link") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "tenantKey", params.TenantKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if params.Tag != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "tag", *params.Tag, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ExternalReference != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "externalReference", *params.ExternalReference, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetLinkedBadgeRequest constructs an http.Request for the GetLinkedBadge method +func NewGetLinkedBadgeRequest(server string, params *GetLinkedBadgeParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/badges/linked-badge.svg") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "tenantKey", params.TenantKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if params.ExternalReference != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "externalReference", *params.ExternalReference, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Tag != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "tag", *params.Tag, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.CreateCaption != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "createCaption", *params.CreateCaption, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Scale != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "scale", *params.Scale, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetEnvironmentsRequest constructs an http.Request for the GetEnvironments method +func NewGetEnvironmentsRequest(server string, params *GetEnvironmentsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/environments") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Search != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "search", *params.Search, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertEnvironmentRequest calls the generic UpsertEnvironment builder with application/json body +func NewUpsertEnvironmentRequest(server string, body UpsertEnvironmentJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertEnvironmentRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpsertEnvironmentRequestWithBody constructs an http.Request for the UpsertEnvironment method, with any body, and a specified content type +func NewUpsertEnvironmentRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/environments") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteEnvironmentRequest constructs an http.Request for the DeleteEnvironment method +func NewDeleteEnvironmentRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/environments/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetEnvironmentRequest constructs an http.Request for the GetEnvironment method +func NewGetEnvironmentRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/environments/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetEnvironmentVariablesRequest constructs an http.Request for the GetEnvironmentVariables method +func NewGetEnvironmentVariablesRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/environments/%s/variables", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewSetEnvironmentVariablesRequest calls the generic SetEnvironmentVariables builder with application/json body +func NewSetEnvironmentVariablesRequest(server string, id openapi_types.UUID, body SetEnvironmentVariablesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSetEnvironmentVariablesRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewSetEnvironmentVariablesRequestWithBody constructs an http.Request for the SetEnvironmentVariables method, with any body, and a specified content type +func NewSetEnvironmentVariablesRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/environments/%s/variables", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewUpdateEnvironmentVariablesRequest calls the generic UpdateEnvironmentVariables builder with application/json body +func NewUpdateEnvironmentVariablesRequest(server string, id openapi_types.UUID, body UpdateEnvironmentVariablesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateEnvironmentVariablesRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewUpdateEnvironmentVariablesRequestWithBody constructs an http.Request for the UpdateEnvironmentVariables method, with any body, and a specified content type +func NewUpdateEnvironmentVariablesRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/environments/%s/variables", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetExperimentsRequest constructs an http.Request for the GetExperiments method +func NewGetExperimentsRequest(server string, params *GetExperimentsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Runnable != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "runnable", *params.Runnable, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Name != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "name", *params.Name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Team != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "team", *params.Team, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.TeamSharedWith != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "teamSharedWith", *params.TeamSharedWith, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Key != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "key", *params.Key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ExternalId != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "externalId", *params.ExternalId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.TargetType != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "targetType", *params.TargetType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Action != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "action", *params.Action, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Tag != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "tag", *params.Tag, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Kind != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "kind", *params.Kind, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Service != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "service", *params.Service, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.FreeTextPhrases != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "freeTextPhrases", *params.FreeTextPhrases, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Properties != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "properties", *params.Properties, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCreateOrUpdateExperimentRequest calls the generic CreateOrUpdateExperiment builder with application/json body +func NewCreateOrUpdateExperimentRequest(server string, body CreateOrUpdateExperimentJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateOrUpdateExperimentRequestWithBody(server, "application/json", bodyReader) +} + +// NewCreateOrUpdateExperimentRequestWithBody constructs an http.Request for the CreateOrUpdateExperiment method, with any body, and a specified content type +func NewCreateOrUpdateExperimentRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewSaveAndRunRequest calls the generic SaveAndRun builder with application/json body +func NewSaveAndRunRequest(server string, params *SaveAndRunParams, body SaveAndRunJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSaveAndRunRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewSaveAndRunRequestWithBody constructs an http.Request for the SaveAndRun method, with any body, and a specified content type +func NewSaveAndRunRequestWithBody(server string, params *SaveAndRunParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/execute") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.AllowParallel != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "allowParallel", *params.AllowParallel, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ForcePersist != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "forcePersist", *params.ForcePersist, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetExperimentExecutions1Request constructs an http.Request for the GetExperimentExecutions1 method +func NewGetExperimentExecutions1Request(server string, params *GetExperimentExecutions1Params) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/executions") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Name != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "name", *params.Name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Team != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "team", *params.Team, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.State != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "state", *params.State, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetExperimentExecutions2Request calls the generic GetExperimentExecutions2 builder with application/json body +func NewGetExperimentExecutions2Request(server string, body GetExperimentExecutions2JSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetExperimentExecutions2RequestWithBody(server, "application/json", bodyReader) +} + +// NewGetExperimentExecutions2RequestWithBody constructs an http.Request for the GetExperimentExecutions2 method, with any body, and a specified content type +func NewGetExperimentExecutions2RequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/executions") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetExperimentExecutionRequest constructs an http.Request for the GetExperimentExecution method +func NewGetExperimentExecutionRequest(server string, id int64, params *GetExperimentExecutionParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "integer", Format: "int64"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/executions/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Fields != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "fields", *params.Fields, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetArtifactRequest constructs an http.Request for the GetArtifact method +func NewGetArtifactRequest(server string, id int64, targetExecutionId string, artifactId string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "integer", Format: "int64"}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "targetExecutionId", targetExecutionId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + var pathParam2 string + + pathParam2, err = runtime.StyleParamWithOptions("simple", false, "artifactId", artifactId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/executions/%s/artifacts/%s/%s", pathParam0, pathParam1, pathParam2) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCancelExperimentExecutionRequest constructs an http.Request for the CancelExperimentExecution method +func NewCancelExperimentExecutionRequest(server string, id int64) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "integer", Format: "int64"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/executions/%s/cancel", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpdateExecutionPropertiesRequest calls the generic UpdateExecutionProperties builder with application/json body +func NewUpdateExecutionPropertiesRequest(server string, id int64, body UpdateExecutionPropertiesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateExecutionPropertiesRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewUpdateExecutionPropertiesRequestWithBody constructs an http.Request for the UpdateExecutionProperties method, with any body, and a specified content type +func NewUpdateExecutionPropertiesRequestWithBody(server string, id int64, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "integer", Format: "int64"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/executions/%s/properties", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewAddExecutionPropertyValueRequest calls the generic AddExecutionPropertyValue builder with application/json body +func NewAddExecutionPropertyValueRequest(server string, id int64, key string, body AddExecutionPropertyValueJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewAddExecutionPropertyValueRequestWithBody(server, id, key, "application/json", bodyReader) +} + +// NewAddExecutionPropertyValueRequestWithBody constructs an http.Request for the AddExecutionPropertyValue method, with any body, and a specified content type +func NewAddExecutionPropertyValueRequestWithBody(server string, id int64, key string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "integer", Format: "int64"}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/executions/%s/properties/%s/add", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewSetExecutionPropertyValueRequest calls the generic SetExecutionPropertyValue builder with application/json body +func NewSetExecutionPropertyValueRequest(server string, id int64, key string, body SetExecutionPropertyValueJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSetExecutionPropertyValueRequestWithBody(server, id, key, "application/json", bodyReader) +} + +// NewSetExecutionPropertyValueRequestWithBody constructs an http.Request for the SetExecutionPropertyValue method, with any body, and a specified content type +func NewSetExecutionPropertyValueRequestWithBody(server string, id int64, key string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "integer", Format: "int64"}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/executions/%s/properties/%s/set", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewUpsertScheduleRequest calls the generic UpsertSchedule builder with application/json body +func NewUpsertScheduleRequest(server string, body UpsertScheduleJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertScheduleRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpsertScheduleRequestWithBody constructs an http.Request for the UpsertSchedule method, with any body, and a specified content type +func NewUpsertScheduleRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/schedules") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetAllSchedulesV2Request constructs an http.Request for the GetAllSchedulesV2 method +func NewGetAllSchedulesV2Request(server string, params *GetAllSchedulesV2Params) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/schedules/v2") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Team != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "team", *params.Team, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Experiment != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "experiment", *params.Experiment, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewRemoveExperimentScheduleByIdRequest constructs an http.Request for the RemoveExperimentScheduleById method +func NewRemoveExperimentScheduleByIdRequest(server string, id string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/schedules/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetSchedulesRequest constructs an http.Request for the GetSchedules method +func NewGetSchedulesRequest(server string, id string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/schedules/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPatchScheduleRequest calls the generic PatchSchedule builder with application/json body +func NewPatchScheduleRequest(server string, id string, body PatchScheduleJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPatchScheduleRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewPatchScheduleRequestWithBody constructs an http.Request for the PatchSchedule method, with any body, and a specified content type +func NewPatchScheduleRequestWithBody(server string, id string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/schedules/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetExperimentTemplatesRequest constructs an http.Request for the GetExperimentTemplates method +func NewGetExperimentTemplatesRequest(server string, params *GetExperimentTemplatesParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/templates") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Tag != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "tag", *params.Tag, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.TargetType != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "targetType", *params.TargetType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Action != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "action", *params.Action, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.FreeTextPhrases != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "freeTextPhrases", *params.FreeTextPhrases, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.IncludeHidden != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "includeHidden", *params.IncludeHidden, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.IncludeNonAvailable != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "includeNonAvailable", *params.IncludeNonAvailable, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertExperimentTemplateRequest calls the generic UpsertExperimentTemplate builder with application/json body +func NewUpsertExperimentTemplateRequest(server string, body UpsertExperimentTemplateJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertExperimentTemplateRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpsertExperimentTemplateRequestWithBody constructs an http.Request for the UpsertExperimentTemplate method, with any body, and a specified content type +func NewUpsertExperimentTemplateRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/templates") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewImportFromHubRequest calls the generic ImportFromHub builder with application/json body +func NewImportFromHubRequest(server string, params *ImportFromHubParams, body ImportFromHubJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewImportFromHubRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewImportFromHubRequestWithBody constructs an http.Request for the ImportFromHub method, with any body, and a specified content type +func NewImportFromHubRequestWithBody(server string, params *ImportFromHubParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/templates/imports") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Overwrite != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "overwrite", *params.Overwrite, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteExperimentTemplateRequest constructs an http.Request for the DeleteExperimentTemplate method +func NewDeleteExperimentTemplateRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/templates/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetExperimentTemplateRequest constructs an http.Request for the GetExperimentTemplate method +func NewGetExperimentTemplateRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/templates/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCreateExperimentByTemplateRequest calls the generic CreateExperimentByTemplate builder with application/json body +func NewCreateExperimentByTemplateRequest(server string, id openapi_types.UUID, params *CreateExperimentByTemplateParams, body CreateExperimentByTemplateJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateExperimentByTemplateRequestWithBody(server, id, params, "application/json", bodyReader) +} + +// NewCreateExperimentByTemplateRequestWithBody constructs an http.Request for the CreateExperimentByTemplate method, with any body, and a specified content type +func NewCreateExperimentByTemplateRequestWithBody(server string, id openapi_types.UUID, params *CreateExperimentByTemplateParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/templates/%s/experiment-create", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ResetProperties != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "resetProperties", *params.ResetProperties, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewSaveAndRunFromTemplateRequest calls the generic SaveAndRunFromTemplate builder with application/json body +func NewSaveAndRunFromTemplateRequest(server string, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, body SaveAndRunFromTemplateJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSaveAndRunFromTemplateRequestWithBody(server, id, params, "application/json", bodyReader) +} + +// NewSaveAndRunFromTemplateRequestWithBody constructs an http.Request for the SaveAndRunFromTemplate method, with any body, and a specified content type +func NewSaveAndRunFromTemplateRequestWithBody(server string, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/templates/%s/experiment-execute", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ResetProperties != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "resetProperties", *params.ResetProperties, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.AllowParallel != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "allowParallel", *params.AllowParallel, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ForcePersist != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "forcePersist", *params.ForcePersist, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewUpdateExperimentByTemplateRequest calls the generic UpdateExperimentByTemplate builder with application/json body +func NewUpdateExperimentByTemplateRequest(server string, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, body UpdateExperimentByTemplateJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateExperimentByTemplateRequestWithBody(server, id, key, params, "application/json", bodyReader) +} + +// NewUpdateExperimentByTemplateRequestWithBody constructs an http.Request for the UpdateExperimentByTemplate method, with any body, and a specified content type +func NewUpdateExperimentByTemplateRequestWithBody(server string, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/templates/%s/experiment-update/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ResetProperties != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "resetProperties", *params.ResetProperties, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteExperimentRequest constructs an http.Request for the DeleteExperiment method +func NewDeleteExperimentRequest(server string, key string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetExperimentRequest constructs an http.Request for the GetExperiment method +func NewGetExperimentRequest(server string, key string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpdateExperimentRequest calls the generic UpdateExperiment builder with application/json body +func NewUpdateExperimentRequest(server string, key string, body UpdateExperimentJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateExperimentRequestWithBody(server, key, "application/json", bodyReader) +} + +// NewUpdateExperimentRequestWithBody constructs an http.Request for the UpdateExperiment method, with any body, and a specified content type +func NewUpdateExperimentRequestWithBody(server string, key string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetExperimentBadgeRequest constructs an http.Request for the GetExperimentBadge method +func NewGetExperimentBadgeRequest(server string, key string, params *GetExperimentBadgeParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/%s/badge.svg", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "tenantKey", params.TenantKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if params.Scale != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "scale", *params.Scale, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ColorMappingErrored != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "colorMappingErrored", *params.ColorMappingErrored, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ColorMappingFailed != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "colorMappingFailed", *params.ColorMappingFailed, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ColorMappingRequested != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "colorMappingRequested", *params.ColorMappingRequested, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ColorMappingCreated != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "colorMappingCreated", *params.ColorMappingCreated, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ColorMappingPrepared != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "colorMappingPrepared", *params.ColorMappingPrepared, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ColorMappingRunning != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "colorMappingRunning", *params.ColorMappingRunning, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ColorMappingCanceled != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "colorMappingCanceled", *params.ColorMappingCanceled, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ColorMappingCompleted != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "colorMappingCompleted", *params.ColorMappingCompleted, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewExecuteExperimentRequest calls the generic ExecuteExperiment builder with application/json body +func NewExecuteExperimentRequest(server string, key string, params *ExecuteExperimentParams, body ExecuteExperimentJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewExecuteExperimentRequestWithBody(server, key, params, "application/json", bodyReader) +} + +// NewExecuteExperimentRequestWithBody constructs an http.Request for the ExecuteExperiment method, with any body, and a specified content type +func NewExecuteExperimentRequestWithBody(server string, key string, params *ExecuteExperimentParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/%s/execute", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.AllowParallel != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "allowParallel", *params.AllowParallel, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ForcePersist != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "forcePersist", *params.ForcePersist, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetExperimentExecutions3Request constructs an http.Request for the GetExperimentExecutions3 method +func NewGetExperimentExecutions3Request(server string, key string, params *GetExperimentExecutions3Params) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/experiments/%s/executions", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.State != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "state", *params.State, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetLandscapeViewsRequest constructs an http.Request for the GetLandscapeViews method +func NewGetLandscapeViewsRequest(server string, params *GetLandscapeViewsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/explore/landscape/views") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "team", params.Team, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCreateLandscapeViewRequest calls the generic CreateLandscapeView builder with application/json body +func NewCreateLandscapeViewRequest(server string, body CreateLandscapeViewJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCreateLandscapeViewRequestWithBody(server, "application/json", bodyReader) +} + +// NewCreateLandscapeViewRequestWithBody constructs an http.Request for the CreateLandscapeView method, with any body, and a specified content type +func NewCreateLandscapeViewRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/explore/landscape/views") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteLandscapeViewRequest constructs an http.Request for the DeleteLandscapeView method +func NewDeleteLandscapeViewRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/explore/landscape/views/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetLandscapeViewRequest constructs an http.Request for the GetLandscapeView method +func NewGetLandscapeViewRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/explore/landscape/views/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpdateLandscapeViewRequest calls the generic UpdateLandscapeView builder with application/json body +func NewUpdateLandscapeViewRequest(server string, id openapi_types.UUID, body UpdateLandscapeViewJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateLandscapeViewRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewUpdateLandscapeViewRequestWithBody constructs an http.Request for the UpdateLandscapeView method, with any body, and a specified content type +func NewUpdateLandscapeViewRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/explore/landscape/views/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewHealthRequest constructs an http.Request for the Health method +func NewHealthRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/health") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewLivenessRequest constructs an http.Request for the Liveness method +func NewLivenessRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/health/liveness") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewReadinessRequest constructs an http.Request for the Readiness method +func NewReadinessRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/health/readiness") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetHubsRequest constructs an http.Request for the GetHubs method +func NewGetHubsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/hubs") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertHubRequest calls the generic UpsertHub builder with application/json body +func NewUpsertHubRequest(server string, params *UpsertHubParams, body UpsertHubJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertHubRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewUpsertHubRequestWithBody constructs an http.Request for the UpsertHub method, with any body, and a specified content type +func NewUpsertHubRequestWithBody(server string, params *UpsertHubParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/hubs") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Synchronize != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "synchronize", *params.Synchronize, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewConnectionCheckRequest calls the generic ConnectionCheck builder with application/json body +func NewConnectionCheckRequest(server string, body ConnectionCheckJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewConnectionCheckRequestWithBody(server, "application/json", bodyReader) +} + +// NewConnectionCheckRequestWithBody constructs an http.Request for the ConnectionCheck method, with any body, and a specified content type +func NewConnectionCheckRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/hubs/connection-check") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteHubRequest constructs an http.Request for the DeleteHub method +func NewDeleteHubRequest(server string, id openapi_types.UUID, params *DeleteHubParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/hubs/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.DeleteImportedTemplates != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "deleteImportedTemplates", *params.DeleteImportedTemplates, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetHubByIdRequest constructs an http.Request for the GetHubById method +func NewGetHubByIdRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/hubs/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewResyncHubRequest constructs an http.Request for the ResyncHub method +func NewResyncHubRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/hubs/%s/resync", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPreflightWebhooksRequest constructs an http.Request for the GetPreflightWebhooks method +func NewGetPreflightWebhooksRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/preflight") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertPreflightWebhookRequest calls the generic UpsertPreflightWebhook builder with application/json body +func NewUpsertPreflightWebhookRequest(server string, body UpsertPreflightWebhookJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertPreflightWebhookRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpsertPreflightWebhookRequestWithBody constructs an http.Request for the UpsertPreflightWebhook method, with any body, and a specified content type +func NewUpsertPreflightWebhookRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/preflight") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetPreflightActionIntegrationsRequest constructs an http.Request for the GetPreflightActionIntegrations method +func NewGetPreflightActionIntegrationsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/preflight-action") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertPreflightActionIntegrationRequest calls the generic UpsertPreflightActionIntegration builder with application/json body +func NewUpsertPreflightActionIntegrationRequest(server string, body UpsertPreflightActionIntegrationJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertPreflightActionIntegrationRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpsertPreflightActionIntegrationRequestWithBody constructs an http.Request for the UpsertPreflightActionIntegration method, with any body, and a specified content type +func NewUpsertPreflightActionIntegrationRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/preflight-action") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeletePreflightActionIntegrationRequest constructs an http.Request for the DeletePreflightActionIntegration method +func NewDeletePreflightActionIntegrationRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/preflight-action/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPreflightActionIntegrationRequest constructs an http.Request for the GetPreflightActionIntegration method +func NewGetPreflightActionIntegrationRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/preflight-action/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewDeletePreflightWebhookRequest constructs an http.Request for the DeletePreflightWebhook method +func NewDeletePreflightWebhookRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/preflight/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPreflightWebhookRequest constructs an http.Request for the GetPreflightWebhook method +func NewGetPreflightWebhookRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/preflight/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetSlackIntegrationsRequest constructs an http.Request for the GetSlackIntegrations method +func NewGetSlackIntegrationsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/slack") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertSlackIntegrationRequest calls the generic UpsertSlackIntegration builder with application/json body +func NewUpsertSlackIntegrationRequest(server string, body UpsertSlackIntegrationJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertSlackIntegrationRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpsertSlackIntegrationRequestWithBody constructs an http.Request for the UpsertSlackIntegration method, with any body, and a specified content type +func NewUpsertSlackIntegrationRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/slack") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteSlackIntegrationRequest constructs an http.Request for the DeleteSlackIntegration method +func NewDeleteSlackIntegrationRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/slack/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetSlackIntegrationRequest constructs an http.Request for the GetSlackIntegration method +func NewGetSlackIntegrationRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/slack/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetCustomWebhooksRequest constructs an http.Request for the GetCustomWebhooks method +func NewGetCustomWebhooksRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/webhook") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertCustomWebhookRequest calls the generic UpsertCustomWebhook builder with application/json body +func NewUpsertCustomWebhookRequest(server string, body UpsertCustomWebhookJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertCustomWebhookRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpsertCustomWebhookRequestWithBody constructs an http.Request for the UpsertCustomWebhook method, with any body, and a specified content type +func NewUpsertCustomWebhookRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/webhook") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteCustomWebhookRequest constructs an http.Request for the DeleteCustomWebhook method +func NewDeleteCustomWebhookRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/webhook/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetCustomWebhookRequest constructs an http.Request for the GetCustomWebhook method +func NewGetCustomWebhookRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/integrations/webhook/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewDisengageKillswitchRequest constructs an http.Request for the DisengageKillswitch method +func NewDisengageKillswitchRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/killswitch") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetKillswitchRequest constructs an http.Request for the GetKillswitch method +func NewGetKillswitchRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/killswitch") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewEngageKillswitchRequest constructs an http.Request for the EngageKillswitch method +func NewEngageKillswitchRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/killswitch") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetLicenseSummaryRequest constructs an http.Request for the GetLicenseSummary method +func NewGetLicenseSummaryRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/license") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetReportRequest constructs an http.Request for the GetReport method +func NewGetReportRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/license/report") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPreflightActionSummaryRequest constructs an http.Request for the GetPreflightActionSummary method +func NewGetPreflightActionSummaryRequest(server string, params *GetPreflightActionSummaryParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/preflight/actions") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetAssociationsRequest constructs an http.Request for the GetAssociations method +func NewGetAssociationsRequest(server string, params *GetAssociationsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/properties/associations") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Page != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", *params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Key != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "key", *params.Key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ExperimentKey != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "experimentKey", *params.ExperimentKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ServiceId != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "serviceId", *params.ServiceId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "uuid"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.AssociationTypeAO != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "associationTypeAO", *params.AssociationTypeAO, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertPropertyAssociationRequest calls the generic UpsertPropertyAssociation builder with application/json body +func NewUpsertPropertyAssociationRequest(server string, body UpsertPropertyAssociationJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertPropertyAssociationRequestWithBody(server, "application/json", bodyReader) +} + +// NewUpsertPropertyAssociationRequestWithBody constructs an http.Request for the UpsertPropertyAssociation method, with any body, and a specified content type +func NewUpsertPropertyAssociationRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/properties/associations") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeletePropertyAssociationRequest constructs an http.Request for the DeletePropertyAssociation method +func NewDeletePropertyAssociationRequest(server string, id openapi_types.UUID, params *DeletePropertyAssociationParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/properties/associations/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.DeleteValues != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "deleteValues", *params.DeleteValues, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPropertyDefinition1Request constructs an http.Request for the GetPropertyDefinition1 method +func NewGetPropertyDefinition1Request(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/properties/associations/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPropertyDefinitionsRequest constructs an http.Request for the GetPropertyDefinitions method +func NewGetPropertyDefinitionsRequest(server string, params *GetPropertyDefinitionsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/properties/definitions") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "object", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertPropertyDefinitionRequest calls the generic UpsertPropertyDefinition builder with application/json body +func NewUpsertPropertyDefinitionRequest(server string, params *UpsertPropertyDefinitionParams, body UpsertPropertyDefinitionJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertPropertyDefinitionRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewUpsertPropertyDefinitionRequestWithBody constructs an http.Request for the UpsertPropertyDefinition method, with any body, and a specified content type +func NewUpsertPropertyDefinitionRequestWithBody(server string, params *UpsertPropertyDefinitionParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/properties/definitions") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.DeleteValues != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "deleteValues", *params.DeleteValues, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeletePropertyDefinitionRequest constructs an http.Request for the DeletePropertyDefinition method +func NewDeletePropertyDefinitionRequest(server string, key string, params *DeletePropertyDefinitionParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/properties/definitions/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.DeleteAssociations != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "deleteAssociations", *params.DeleteAssociations, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPropertyDefinitionRequest constructs an http.Request for the GetPropertyDefinition method +func NewGetPropertyDefinitionRequest(server string, key string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/properties/definitions/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetEnvironmentCountsRequest calls the generic GetEnvironmentCounts builder with application/json body +func NewGetEnvironmentCountsRequest(server string, body GetEnvironmentCountsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetEnvironmentCountsRequestWithBody(server, "application/json", bodyReader) +} + +// NewGetEnvironmentCountsRequestWithBody constructs an http.Request for the GetEnvironmentCounts method, with any body, and a specified content type +func NewGetEnvironmentCountsRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/reports/environments") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetExperimentCreationsRequest calls the generic GetExperimentCreations builder with application/json body +func NewGetExperimentCreationsRequest(server string, params *GetExperimentCreationsParams, body GetExperimentCreationsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetExperimentCreationsRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewGetExperimentCreationsRequestWithBody constructs an http.Request for the GetExperimentCreations method, with any body, and a specified content type +func NewGetExperimentCreationsRequestWithBody(server string, params *GetExperimentCreationsParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/reports/experiments/created") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.GroupBy != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "groupBy", *params.GroupBy, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetExperimentExecutionsRequest calls the generic GetExperimentExecutions builder with application/json body +func NewGetExperimentExecutionsRequest(server string, params *GetExperimentExecutionsParams, body GetExperimentExecutionsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetExperimentExecutionsRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewGetExperimentExecutionsRequestWithBody constructs an http.Request for the GetExperimentExecutions method, with any body, and a specified content type +func NewGetExperimentExecutionsRequestWithBody(server string, params *GetExperimentExecutionsParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/reports/experiments/executed") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.GroupBy != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "groupBy", *params.GroupBy, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetAverageRiskRequest calls the generic GetAverageRisk builder with application/json body +func NewGetAverageRiskRequest(server string, body GetAverageRiskJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetAverageRiskRequestWithBody(server, "application/json", bodyReader) +} + +// NewGetAverageRiskRequestWithBody constructs an http.Request for the GetAverageRisk method, with any body, and a specified content type +func NewGetAverageRiskRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/reports/services/average") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetRiskByCategoryRequest calls the generic GetRiskByCategory builder with application/json body +func NewGetRiskByCategoryRequest(server string, body GetRiskByCategoryJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetRiskByCategoryRequestWithBody(server, "application/json", bodyReader) +} + +// NewGetRiskByCategoryRequestWithBody constructs an http.Request for the GetRiskByCategory method, with any body, and a specified content type +func NewGetRiskByCategoryRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/reports/services/by-category") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetRiskDistributionRequest calls the generic GetRiskDistribution builder with application/json body +func NewGetRiskDistributionRequest(server string, body GetRiskDistributionJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetRiskDistributionRequestWithBody(server, "application/json", bodyReader) +} + +// NewGetRiskDistributionRequestWithBody constructs an http.Request for the GetRiskDistribution method, with any body, and a specified content type +func NewGetRiskDistributionRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/reports/services/distribution") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetTeamCountsRequest calls the generic GetTeamCounts builder with application/json body +func NewGetTeamCountsRequest(server string, body GetTeamCountsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetTeamCountsRequestWithBody(server, "application/json", bodyReader) +} + +// NewGetTeamCountsRequestWithBody constructs an http.Request for the GetTeamCounts method, with any body, and a specified content type +func NewGetTeamCountsRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/reports/teams") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetUserCountsRequest calls the generic GetUserCounts builder with application/json body +func NewGetUserCountsRequest(server string, body GetUserCountsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetUserCountsRequestWithBody(server, "application/json", bodyReader) +} + +// NewGetUserCountsRequestWithBody constructs an http.Request for the GetUserCounts method, with any body, and a specified content type +func NewGetUserCountsRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/reports/users") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetServiceListRequest constructs an http.Request for the GetServiceList method +func NewGetServiceListRequest(server string, params *GetServiceListParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.TeamKey != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "teamKey", *params.TeamKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ExperimentKey != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "experimentKey", *params.ExperimentKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.EnvironmentName != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "environmentName", *params.EnvironmentName, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "object", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertServiceRequest calls the generic UpsertService builder with application/json body +func NewUpsertServiceRequest(server string, params *UpsertServiceParams, body UpsertServiceJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertServiceRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewUpsertServiceRequestWithBody constructs an http.Request for the UpsertService method, with any body, and a specified content type +func NewUpsertServiceRequestWithBody(server string, params *UpsertServiceParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.DeleteExperiments != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "deleteExperiments", *params.DeleteExperiments, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetProfilesRequest constructs an http.Request for the GetProfiles method +func NewGetProfilesRequest(server string, params *GetProfilesParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/profiles") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Name != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "name", *params.Name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Origin != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "origin", *params.Origin, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.DefaultProfile != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "defaultProfile", *params.DefaultProfile, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "object", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertProfileRequest calls the generic UpsertProfile builder with application/json body +func NewUpsertProfileRequest(server string, params *UpsertProfileParams, body UpsertProfileJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertProfileRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewUpsertProfileRequestWithBody constructs an http.Request for the UpsertProfile method, with any body, and a specified content type +func NewUpsertProfileRequestWithBody(server string, params *UpsertProfileParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/profiles") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.DeleteExperiments != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "deleteExperiments", *params.DeleteExperiments, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteProfileRequest constructs an http.Request for the DeleteProfile method +func NewDeleteProfileRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/profiles/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetProfileRequest constructs an http.Request for the GetProfile method +func NewGetProfileRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/profiles/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewDeleteServiceRequest constructs an http.Request for the DeleteService method +func NewDeleteServiceRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetServiceRequest constructs an http.Request for the GetService method +func NewGetServiceRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetServiceExperimentsRequest constructs an http.Request for the GetServiceExperiments method +func NewGetServiceExperimentsRequest(server string, id openapi_types.UUID, params *GetServiceExperimentsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s/experiments", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Category != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "category", *params.Category, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.CategoryMissing != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "categoryMissing", *params.CategoryMissing, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Type != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "type", *params.Type, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "object", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUnlinkCustomExperimentRequest constructs an http.Request for the UnlinkCustomExperiment method +func NewUnlinkCustomExperimentRequest(server string, id openapi_types.UUID, params *UnlinkCustomExperimentParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s/experiments/custom", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "experimentKey", params.ExperimentKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewLinkCustomExperimentRequest calls the generic LinkCustomExperiment builder with application/json body +func NewLinkCustomExperimentRequest(server string, id openapi_types.UUID, body LinkCustomExperimentJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewLinkCustomExperimentRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewLinkCustomExperimentRequestWithBody constructs an http.Request for the LinkCustomExperiment method, with any body, and a specified content type +func NewLinkCustomExperimentRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s/experiments/custom", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewUpsertProvidedExperimentRequest calls the generic UpsertProvidedExperiment builder with application/json body +func NewUpsertProvidedExperimentRequest(server string, id openapi_types.UUID, params *UpsertProvidedExperimentParams, body UpsertProvidedExperimentJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertProvidedExperimentRequestWithBody(server, id, params, "application/json", bodyReader) +} + +// NewUpsertProvidedExperimentRequestWithBody constructs an http.Request for the UpsertProvidedExperiment method, with any body, and a specified content type +func NewUpsertProvidedExperimentRequestWithBody(server string, id openapi_types.UUID, params *UpsertProvidedExperimentParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s/experiments/provided", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ResetProperties != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "resetProperties", *params.ResetProperties, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetRiskRequest constructs an http.Request for the GetRisk method +func NewGetRiskRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s/risk", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetServiceVariablesRequest constructs an http.Request for the GetServiceVariables method +func NewGetServiceVariablesRequest(server string, id openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s/variables", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewMergeServiceVariablesRequest calls the generic MergeServiceVariables builder with application/json body +func NewMergeServiceVariablesRequest(server string, id openapi_types.UUID, body MergeServiceVariablesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewMergeServiceVariablesRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewMergeServiceVariablesRequestWithBody constructs an http.Request for the MergeServiceVariables method, with any body, and a specified content type +func NewMergeServiceVariablesRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s/variables", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewSetServiceVariablesRequest calls the generic SetServiceVariables builder with application/json body +func NewSetServiceVariablesRequest(server string, id openapi_types.UUID, body SetServiceVariablesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSetServiceVariablesRequestWithBody(server, id, "application/json", bodyReader) +} + +// NewSetServiceVariablesRequestWithBody constructs an http.Request for the SetServiceVariables method, with any body, and a specified content type +func NewSetServiceVariablesRequestWithBody(server string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/services/%s/variables", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetTargetsStatsRequest constructs an http.Request for the GetTargetsStats method +func NewGetTargetsStatsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/target-stats") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTargetsStats1Request calls the generic GetTargetsStats1 builder with application/json body +func NewGetTargetsStats1Request(server string, body GetTargetsStats1JSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewGetTargetsStats1RequestWithBody(server, "application/json", bodyReader) +} + +// NewGetTargetsStats1RequestWithBody constructs an http.Request for the GetTargetsStats1 method, with any body, and a specified content type +func NewGetTargetsStats1RequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/target-stats") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetTargetsRequest constructs an http.Request for the GetTargets method +func NewGetTargetsRequest(server string, params *GetTargetsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/targets") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "environment", params.Environment, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if params.TargetType != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "targetType", *params.TargetType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Query != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "query", *params.Query, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Attribute != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "attribute", *params.Attribute, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Cursor != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cursor", *params.Cursor, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Size != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "size", *params.Size, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTargetAttributeKeysRequest constructs an http.Request for the GetTargetAttributeKeys method +func NewGetTargetAttributeKeysRequest(server string, params *GetTargetAttributeKeysParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/targets/attributes/keys") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "environment", params.Environment, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if params.TargetType != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "targetType", *params.TargetType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ActionId != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "actionId", *params.ActionId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Page != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", *params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Size != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "size", *params.Size, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTargetAttributeValuesRequest constructs an http.Request for the GetTargetAttributeValues method +func NewGetTargetAttributeValuesRequest(server string, params *GetTargetAttributeValuesParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/targets/attributes/values") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "environment", params.Environment, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if params.TargetType != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "targetType", *params.TargetType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ActionId != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "actionId", *params.ActionId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "attributeKey", params.AttributeKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if params.Page != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "page", *params.Page, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Size != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "size", *params.Size, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTeamsRequest constructs an http.Request for the GetTeams method +func NewGetTeamsRequest(server string, params *GetTeamsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.OnlyAccessible != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "onlyAccessible", *params.OnlyAccessible, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Search != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "search", *params.Search, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpsertTeamRequest calls the generic UpsertTeam builder with application/json body +func NewUpsertTeamRequest(server string, params *UpsertTeamParams, body UpsertTeamJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpsertTeamRequestWithBody(server, params, "application/json", bodyReader) +} + +// NewUpsertTeamRequestWithBody constructs an http.Request for the UpsertTeam method, with any body, and a specified content type +func NewUpsertTeamRequestWithBody(server string, params *UpsertTeamParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ValidateActions != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "validateActions", *params.ValidateActions, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.ValidateMembers != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "validateMembers", *params.ValidateMembers, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteTeamRequest constructs an http.Request for the DeleteTeam method +func NewDeleteTeamRequest(server string, key string, params *DeleteTeamParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "purgeIncludingExperiments", params.PurgeIncludingExperiments, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTeamRequest constructs an http.Request for the GetTeam method +func NewGetTeamRequest(server string, key string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTeamEnvironmentsRequest constructs an http.Request for the GetTeamEnvironments method +func NewGetTeamEnvironmentsRequest(server string, key string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s/environments", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewSetTeamEnvironmentsRequest calls the generic SetTeamEnvironments builder with application/json body +func NewSetTeamEnvironmentsRequest(server string, key string, params *SetTeamEnvironmentsParams, body SetTeamEnvironmentsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSetTeamEnvironmentsRequestWithBody(server, key, params, "application/json", bodyReader) +} + +// NewSetTeamEnvironmentsRequestWithBody constructs an http.Request for the SetTeamEnvironments method, with any body, and a specified content type +func NewSetTeamEnvironmentsRequestWithBody(server string, key string, params *SetTeamEnvironmentsParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s/environments", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ValidateEnvironments != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "validateEnvironments", *params.ValidateEnvironments, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewAddTeamEnvironmentsRequest calls the generic AddTeamEnvironments builder with application/json body +func NewAddTeamEnvironmentsRequest(server string, key string, params *AddTeamEnvironmentsParams, body AddTeamEnvironmentsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewAddTeamEnvironmentsRequestWithBody(server, key, params, "application/json", bodyReader) +} + +// NewAddTeamEnvironmentsRequestWithBody constructs an http.Request for the AddTeamEnvironments method, with any body, and a specified content type +func NewAddTeamEnvironmentsRequestWithBody(server string, key string, params *AddTeamEnvironmentsParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s/environments/add", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ValidateEnvironments != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "validateEnvironments", *params.ValidateEnvironments, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewRemoveTeamEnvironmentsRequest calls the generic RemoveTeamEnvironments builder with application/json body +func NewRemoveTeamEnvironmentsRequest(server string, key string, body RemoveTeamEnvironmentsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRemoveTeamEnvironmentsRequestWithBody(server, key, "application/json", bodyReader) +} + +// NewRemoveTeamEnvironmentsRequestWithBody constructs an http.Request for the RemoveTeamEnvironments method, with any body, and a specified content type +func NewRemoveTeamEnvironmentsRequestWithBody(server string, key string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s/environments/remove", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetTeamMembersRequest constructs an http.Request for the GetTeamMembers method +func NewGetTeamMembersRequest(server string, key string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s/members", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewSetTeamMembersRequest calls the generic SetTeamMembers builder with application/json body +func NewSetTeamMembersRequest(server string, key string, params *SetTeamMembersParams, body SetTeamMembersJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSetTeamMembersRequestWithBody(server, key, params, "application/json", bodyReader) +} + +// NewSetTeamMembersRequestWithBody constructs an http.Request for the SetTeamMembers method, with any body, and a specified content type +func NewSetTeamMembersRequestWithBody(server string, key string, params *SetTeamMembersParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s/members", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ValidateMembers != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "validateMembers", *params.ValidateMembers, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewAddTeamMembersRequest calls the generic AddTeamMembers builder with application/json body +func NewAddTeamMembersRequest(server string, key string, params *AddTeamMembersParams, body AddTeamMembersJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewAddTeamMembersRequestWithBody(server, key, params, "application/json", bodyReader) +} + +// NewAddTeamMembersRequestWithBody constructs an http.Request for the AddTeamMembers method, with any body, and a specified content type +func NewAddTeamMembersRequestWithBody(server string, key string, params *AddTeamMembersParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s/members/add", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.ValidateMembers != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "validateMembers", *params.ValidateMembers, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "boolean", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewRemoveTeamMembersRequest calls the generic RemoveTeamMembers builder with application/json body +func NewRemoveTeamMembersRequest(server string, key string, body RemoveTeamMembersJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRemoveTeamMembersRequestWithBody(server, key, "application/json", bodyReader) +} + +// NewRemoveTeamMembersRequestWithBody constructs an http.Request for the RemoveTeamMembers method, with any body, and a specified content type +func NewRemoveTeamMembersRequestWithBody(server string, key string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "key", key, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/teams/%s/members/remove", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewInviteUserRequest calls the generic InviteUser builder with application/json body +func NewInviteUserRequest(server string, body InviteUserJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewInviteUserRequestWithBody(server, "application/json", bodyReader) +} + +// NewInviteUserRequestWithBody constructs an http.Request for the InviteUser method, with any body, and a specified content type +func NewInviteUserRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/users/invite") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + + // GetAccessTokensWithResponse Get access token list + // + // Deprecated, use v2 instead. Get a list of all access tokens. The access token itself is abbreviated for security reasons. Access tokens with v2 features are not returned, as they can not be represented cleanly in the old format. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/access-tokens (the `GetAccessTokens` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + GetAccessTokensWithResponse(ctx context.Context, params *GetAccessTokensParams, reqEditors ...RequestEditorFn) (*GetAccessTokensResponse, error) + + // CreateAccessTokenWithBodyWithResponse Add a access token + // + // Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/access-tokens (the `CreateAccessToken` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + CreateAccessTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAccessTokenResponse, error) + + // CreateAccessTokenWithResponse Add a access token + // + // Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/access-tokens (the `CreateAccessToken` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + CreateAccessTokenWithResponse(ctx context.Context, body CreateAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateAccessTokenResponse, error) + + // GetAccessTokens1WithResponse Get access token list + // + // Get a list of all access tokens. The access token itself is abbreviated for security reasons. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/access-tokens/v2 (the `GetAccessTokens1` operationId). + GetAccessTokens1WithResponse(ctx context.Context, params *GetAccessTokens1Params, reqEditors ...RequestEditorFn) (*GetAccessTokens1Response, error) + + // CreateAccessToken1WithBodyWithResponse Create an access token + // + // Generate a new access token. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/access-tokens/v2 (the `CreateAccessToken1` operationId). + CreateAccessToken1WithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAccessToken1Response, error) + + // CreateAccessToken1WithResponse Create an access token + // + // Generate a new access token. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/access-tokens/v2 (the `CreateAccessToken1` operationId). + CreateAccessToken1WithResponse(ctx context.Context, body CreateAccessToken1JSONRequestBody, reqEditors ...RequestEditorFn) (*CreateAccessToken1Response, error) + + // DeleteAccessToken1WithResponse Delete access token + // + // Remove the access token. After that, the access token can't be used anymore. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/access-tokens/v2/{id} (the `DeleteAccessToken1` operationId). + DeleteAccessToken1WithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*DeleteAccessToken1Response, error) + + // RecreateAccessTokenWithBodyWithResponse Recreate an access token + // + // Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/access-tokens/v2/{id}/recreate (the `RecreateAccessToken` operationId). + RecreateAccessTokenWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RecreateAccessTokenResponse, error) + + // RecreateAccessTokenWithResponse Recreate an access token + // + // Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/access-tokens/v2/{id}/recreate (the `RecreateAccessToken` operationId). + RecreateAccessTokenWithResponse(ctx context.Context, id string, body RecreateAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*RecreateAccessTokenResponse, error) + + // DeleteAccessTokenWithResponse Delete access token + // + // Remove the access token associated. After that, the access token can't be used anymore for e.g. creating a new experiment or running an experiment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/access-tokens/{id} (the `DeleteAccessToken` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + DeleteAccessTokenWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*DeleteAccessTokenResponse, error) + + // FindAllActionsWithResponse Get all actions. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/actions (the `FindAllActions` operationId). + FindAllActionsWithResponse(ctx context.Context, params *FindAllActionsParams, reqEditors ...RequestEditorFn) (*FindAllActionsResponse, error) + + // GetActionWithResponse Fetch a single action description + // + // Get action including their parameters. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/actions/{actionId} (the `GetAction` operationId). + GetActionWithResponse(ctx context.Context, actionId string, reqEditors ...RequestEditorFn) (*GetActionResponse, error) + + // GetTargetAdviceSummaryWithBodyWithResponse Get all currently active advice for a given environment and query. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/advice (the `GetTargetAdviceSummary` operationId). + GetTargetAdviceSummaryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetTargetAdviceSummaryResponse, error) + + // GetTargetAdviceSummaryWithResponse Get all currently active advice for a given environment and query. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/advice (the `GetTargetAdviceSummary` operationId). + GetTargetAdviceSummaryWithResponse(ctx context.Context, body GetTargetAdviceSummaryJSONRequestBody, reqEditors ...RequestEditorFn) (*GetTargetAdviceSummaryResponse, error) + + // FindWithResponse Get all audit log entries + // + // Retrieve all audit logs in the given time-frame.
This endpoint requires an admin-token and can't be used with a team-based token. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/audit-log (the `Find` operationId). + FindWithResponse(ctx context.Context, params *FindParams, reqEditors ...RequestEditorFn) (*FindResponse, error) + + // ForwardToPlatformWithResponse Forward to Steadybit platform to either create an experiment associated to the `tag` or forward to the experiments linked already to the `tag` + // + // This endpoint can be used as a link for the badge of the `/api/badges/linked-badge.svg` API to either create a new experiment or show the linked experiments in Steadybit. This will help to link it correctly e.g. in your CMS-systems. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/badges/link (the `ForwardToPlatform` operationId). + ForwardToPlatformWithResponse(ctx context.Context, params *ForwardToPlatformParams, reqEditors ...RequestEditorFn) (*ForwardToPlatformResponse, error) + + // GetLinkedBadgeWithResponse Get badge for create experiment or run status as SVG image + // + // Creates an image badge that is either for creating a new experiment linked to an `externalReference` or - if an experiment with the given `externalReference` already exists - a badge showing the run status of the experiment. The badge is return as SVG to integrate it nicely e.g. into your CMS-systems. You can use the `/api/badges/link` endpoint to link it appropriately + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/badges/linked-badge.svg (the `GetLinkedBadge` operationId). + GetLinkedBadgeWithResponse(ctx context.Context, params *GetLinkedBadgeParams, reqEditors ...RequestEditorFn) (*GetLinkedBadgeResponse, error) + + // GetEnvironmentsWithResponse Fetch a list of all environments + // + // Get a list of all environments that exist. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/environments (the `GetEnvironments` operationId). + GetEnvironmentsWithResponse(ctx context.Context, params *GetEnvironmentsParams, reqEditors ...RequestEditorFn) (*GetEnvironmentsResponse, error) + + // UpsertEnvironmentWithBodyWithResponse Create or update an environment + // + // Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/environments (the `UpsertEnvironment` operationId). + UpsertEnvironmentWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertEnvironmentResponse, error) + + // UpsertEnvironmentWithResponse Create or update an environment + // + // Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/environments (the `UpsertEnvironment` operationId). + UpsertEnvironmentWithResponse(ctx context.Context, body UpsertEnvironmentJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertEnvironmentResponse, error) + + // DeleteEnvironmentWithResponse Delete environment + // + // Remove the given environment from the Steadybit platform. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/environments/{id} (the `DeleteEnvironment` operationId). + DeleteEnvironmentWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteEnvironmentResponse, error) + + // GetEnvironmentWithResponse Fetch a single environment + // + // Get all details of a single existing environment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/environments/{id} (the `GetEnvironment` operationId). + GetEnvironmentWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetEnvironmentResponse, error) + + // GetEnvironmentVariablesWithResponse Get environment variables + // + // Get all environment variables associated to a single environment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/environments/{id}/variables (the `GetEnvironmentVariables` operationId). + GetEnvironmentVariablesWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetEnvironmentVariablesResponse, error) + + // SetEnvironmentVariablesWithBodyWithResponse Replace all environment variables + // + // All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/environments/{id}/variables (the `SetEnvironmentVariables` operationId). + SetEnvironmentVariablesWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetEnvironmentVariablesResponse, error) + + // SetEnvironmentVariablesWithResponse Replace all environment variables + // + // All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/environments/{id}/variables (the `SetEnvironmentVariables` operationId). + SetEnvironmentVariablesWithResponse(ctx context.Context, id openapi_types.UUID, body SetEnvironmentVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*SetEnvironmentVariablesResponse, error) + + // UpdateEnvironmentVariablesWithBodyWithResponse Add / merge all environment variables + // + // All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/environments/{id}/variables (the `UpdateEnvironmentVariables` operationId). + UpdateEnvironmentVariablesWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateEnvironmentVariablesResponse, error) + + // UpdateEnvironmentVariablesWithResponse Add / merge all environment variables + // + // All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/environments/{id}/variables (the `UpdateEnvironmentVariables` operationId). + UpdateEnvironmentVariablesWithResponse(ctx context.Context, id openapi_types.UUID, body UpdateEnvironmentVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateEnvironmentVariablesResponse, error) + + // GetExperimentsWithResponse Fetch a list of all experiments + // + // Get a list of all experiments that exist. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments (the `GetExperiments` operationId). + GetExperimentsWithResponse(ctx context.Context, params *GetExperimentsParams, reqEditors ...RequestEditorFn) (*GetExperimentsResponse, error) + + // CreateOrUpdateExperimentWithBodyWithResponse Create or update an experiment + // + // Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments (the `CreateOrUpdateExperiment` operationId). + CreateOrUpdateExperimentWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateOrUpdateExperimentResponse, error) + + // CreateOrUpdateExperimentWithResponse Create or update an experiment + // + // Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments (the `CreateOrUpdateExperiment` operationId). + CreateOrUpdateExperimentWithResponse(ctx context.Context, body CreateOrUpdateExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateOrUpdateExperimentResponse, error) + + // SaveAndRunWithBodyWithResponse Save and run experiment + // + // Save the given experiment and immediately run it. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/execute (the `SaveAndRun` operationId). + SaveAndRunWithBodyWithResponse(ctx context.Context, params *SaveAndRunParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SaveAndRunResponse, error) + + // SaveAndRunWithResponse Save and run experiment + // + // Save the given experiment and immediately run it. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/execute (the `SaveAndRun` operationId). + SaveAndRunWithResponse(ctx context.Context, params *SaveAndRunParams, body SaveAndRunJSONRequestBody, reqEditors ...RequestEditorFn) (*SaveAndRunResponse, error) + + // GetExperimentExecutions1WithResponse Fetch a list of all experiment executions + // + // Get a list of all experiment executions that exist. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/executions (the `GetExperimentExecutions1` operationId). + // + // Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set + GetExperimentExecutions1WithResponse(ctx context.Context, params *GetExperimentExecutions1Params, reqEditors ...RequestEditorFn) (*GetExperimentExecutions1Response, error) + + // GetExperimentExecutions2WithBodyWithResponse Fetch a list of experiment executions + // + // Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions (the `GetExperimentExecutions2` operationId). + GetExperimentExecutions2WithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetExperimentExecutions2Response, error) + + // GetExperimentExecutions2WithResponse Fetch a list of experiment executions + // + // Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions (the `GetExperimentExecutions2` operationId). + GetExperimentExecutions2WithResponse(ctx context.Context, body GetExperimentExecutions2JSONRequestBody, reqEditors ...RequestEditorFn) (*GetExperimentExecutions2Response, error) + + // GetExperimentExecutionWithResponse Fetch a single experiment executions of a single experiment + // + // Get a single experiment execution that was performed for a specific experiment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/executions/{id} (the `GetExperimentExecution` operationId). + GetExperimentExecutionWithResponse(ctx context.Context, id int64, params *GetExperimentExecutionParams, reqEditors ...RequestEditorFn) (*GetExperimentExecutionResponse, error) + + // GetArtifactWithResponse performs a GET /api/experiments/executions/{id}/artifacts/{targetExecutionId}/{artifactId} (the `GetArtifact` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + GetArtifactWithResponse(ctx context.Context, id int64, targetExecutionId string, artifactId string, reqEditors ...RequestEditorFn) (*GetArtifactResponse, error) + + // CancelExperimentExecutionWithResponse Cancel a running experiment execution of a single experiment + // + // Cancels a currently running experiment execution to be stopped as soon as possible. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions/{id}/cancel (the `CancelExperimentExecution` operationId). + CancelExperimentExecutionWithResponse(ctx context.Context, id int64, reqEditors ...RequestEditorFn) (*CancelExperimentExecutionResponse, error) + + // UpdateExecutionPropertiesWithBodyWithResponse Update properties of an experiment execution + // + // Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions/{id}/properties (the `UpdateExecutionProperties` operationId). + UpdateExecutionPropertiesWithBodyWithResponse(ctx context.Context, id int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateExecutionPropertiesResponse, error) + + // UpdateExecutionPropertiesWithResponse Update properties of an experiment execution + // + // Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions/{id}/properties (the `UpdateExecutionProperties` operationId). + UpdateExecutionPropertiesWithResponse(ctx context.Context, id int64, body UpdateExecutionPropertiesJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateExecutionPropertiesResponse, error) + + // AddExecutionPropertyValueWithBodyWithResponse Add a single value to a list property of an experiment execution. + // + // This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions/{id}/properties/{key}/add (the `AddExecutionPropertyValue` operationId). + AddExecutionPropertyValueWithBodyWithResponse(ctx context.Context, id int64, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AddExecutionPropertyValueResponse, error) + + // AddExecutionPropertyValueWithResponse Add a single value to a list property of an experiment execution. + // + // This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions/{id}/properties/{key}/add (the `AddExecutionPropertyValue` operationId). + AddExecutionPropertyValueWithResponse(ctx context.Context, id int64, key string, body AddExecutionPropertyValueJSONRequestBody, reqEditors ...RequestEditorFn) (*AddExecutionPropertyValueResponse, error) + + // SetExecutionPropertyValueWithBodyWithResponse Set the value of a property of an experiment execution. + // + // Only properties with `editableInExecution` set to `true` can be modified. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions/{id}/properties/{key}/set (the `SetExecutionPropertyValue` operationId). + SetExecutionPropertyValueWithBodyWithResponse(ctx context.Context, id int64, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetExecutionPropertyValueResponse, error) + + // SetExecutionPropertyValueWithResponse Set the value of a property of an experiment execution. + // + // Only properties with `editableInExecution` set to `true` can be modified. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/executions/{id}/properties/{key}/set (the `SetExecutionPropertyValue` operationId). + SetExecutionPropertyValueWithResponse(ctx context.Context, id int64, key string, body SetExecutionPropertyValueJSONRequestBody, reqEditors ...RequestEditorFn) (*SetExecutionPropertyValueResponse, error) + + // UpsertScheduleWithBodyWithResponse Create or update an experiment schedule + // + // Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/schedules (the `UpsertSchedule` operationId). + UpsertScheduleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertScheduleResponse, error) + + // UpsertScheduleWithResponse Create or update an experiment schedule + // + // Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/schedules (the `UpsertSchedule` operationId). + UpsertScheduleWithResponse(ctx context.Context, body UpsertScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertScheduleResponse, error) + + // GetAllSchedulesV2WithResponse Get all current experiment schedule configurations + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/schedules/v2 (the `GetAllSchedulesV2` operationId). + GetAllSchedulesV2WithResponse(ctx context.Context, params *GetAllSchedulesV2Params, reqEditors ...RequestEditorFn) (*GetAllSchedulesV2Response, error) + + // RemoveExperimentScheduleByIdWithResponse Remove an existing experiment schedule + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/experiments/schedules/{id} (the `RemoveExperimentScheduleById` operationId). + RemoveExperimentScheduleByIdWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*RemoveExperimentScheduleByIdResponse, error) + + // GetSchedulesWithResponse Get experiment schedules for a specific experiment schedule id + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/schedules/{id} (the `GetSchedules` operationId). + GetSchedulesWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*GetSchedulesResponse, error) + + // PatchScheduleWithBodyWithResponse Partially update an experiment schedule + // + // Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PATCH /api/experiments/schedules/{id} (the `PatchSchedule` operationId). + PatchScheduleWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchScheduleResponse, error) + + // PatchScheduleWithResponse Partially update an experiment schedule + // + // Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PATCH /api/experiments/schedules/{id} (the `PatchSchedule` operationId). + PatchScheduleWithResponse(ctx context.Context, id string, body PatchScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchScheduleResponse, error) + + // GetExperimentTemplatesWithResponse Fetch a list of all templates + // + // Get a list of all templates that exist. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/templates (the `GetExperimentTemplates` operationId). + GetExperimentTemplatesWithResponse(ctx context.Context, params *GetExperimentTemplatesParams, reqEditors ...RequestEditorFn) (*GetExperimentTemplatesResponse, error) + + // UpsertExperimentTemplateWithBodyWithResponse Create or update an experiment template + // + // Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates (the `UpsertExperimentTemplate` operationId). + UpsertExperimentTemplateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertExperimentTemplateResponse, error) + + // UpsertExperimentTemplateWithResponse Create or update an experiment template + // + // Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates (the `UpsertExperimentTemplate` operationId). + UpsertExperimentTemplateWithResponse(ctx context.Context, body UpsertExperimentTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertExperimentTemplateResponse, error) + + // ImportFromHubWithBodyWithResponse Import experiment templates + // + // Import experiment templates with given IDs from linked hub. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates/imports (the `ImportFromHub` operationId). + ImportFromHubWithBodyWithResponse(ctx context.Context, params *ImportFromHubParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ImportFromHubResponse, error) + + // ImportFromHubWithResponse Import experiment templates + // + // Import experiment templates with given IDs from linked hub. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates/imports (the `ImportFromHub` operationId). + ImportFromHubWithResponse(ctx context.Context, params *ImportFromHubParams, body ImportFromHubJSONRequestBody, reqEditors ...RequestEditorFn) (*ImportFromHubResponse, error) + + // DeleteExperimentTemplateWithResponse Delete experiment template + // + // Remove the given experiment template from the Steadybit platform. If this template is used in a service profile, it will be removed from the profile and all provided service experiments will get deleted. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/experiments/templates/{id} (the `DeleteExperimentTemplate` operationId). + DeleteExperimentTemplateWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteExperimentTemplateResponse, error) + + // GetExperimentTemplateWithResponse Fetch a single experiment template + // + // Get all details of a single existing experiment template. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/templates/{id} (the `GetExperimentTemplate` operationId). + GetExperimentTemplateWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetExperimentTemplateResponse, error) + + // CreateExperimentByTemplateWithBodyWithResponse Create an experiment based on an experiment template + // + // Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-create (the `CreateExperimentByTemplate` operationId). + CreateExperimentByTemplateWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, params *CreateExperimentByTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateExperimentByTemplateResponse, error) + + // CreateExperimentByTemplateWithResponse Create an experiment based on an experiment template + // + // Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-create (the `CreateExperimentByTemplate` operationId). + CreateExperimentByTemplateWithResponse(ctx context.Context, id openapi_types.UUID, params *CreateExperimentByTemplateParams, body CreateExperimentByTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateExperimentByTemplateResponse, error) + + // SaveAndRunFromTemplateWithBodyWithResponse Create an experiment based on an experiment template and run experiment + // + // Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-execute (the `SaveAndRunFromTemplate` operationId). + SaveAndRunFromTemplateWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SaveAndRunFromTemplateResponse, error) + + // SaveAndRunFromTemplateWithResponse Create an experiment based on an experiment template and run experiment + // + // Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-execute (the `SaveAndRunFromTemplate` operationId). + SaveAndRunFromTemplateWithResponse(ctx context.Context, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, body SaveAndRunFromTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*SaveAndRunFromTemplateResponse, error) + + // UpdateExperimentByTemplateWithBodyWithResponse Update an existing experiment based on a template + // + // Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-update/{key} (the `UpdateExperimentByTemplate` operationId). + UpdateExperimentByTemplateWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateExperimentByTemplateResponse, error) + + // UpdateExperimentByTemplateWithResponse Update an existing experiment based on a template + // + // Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/templates/{id}/experiment-update/{key} (the `UpdateExperimentByTemplate` operationId). + UpdateExperimentByTemplateWithResponse(ctx context.Context, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, body UpdateExperimentByTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateExperimentByTemplateResponse, error) + + // DeleteExperimentWithResponse Delete experiment + // + // Remove the given experiment. The associated number is still reserved afterwards and will not be reused. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/experiments/{key} (the `DeleteExperiment` operationId). + DeleteExperimentWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*DeleteExperimentResponse, error) + + // GetExperimentWithResponse Fetch a single experiment + // + // Get all details of a single existing experiment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/{key} (the `GetExperiment` operationId). + GetExperimentWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetExperimentResponse, error) + + // UpdateExperimentWithBodyWithResponse Update an experiment + // + // Update the experiment identified by the experiment `key`. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/{key} (the `UpdateExperiment` operationId). + UpdateExperimentWithBodyWithResponse(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateExperimentResponse, error) + + // UpdateExperimentWithResponse Update an experiment + // + // Update the experiment identified by the experiment `key`. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/{key} (the `UpdateExperiment` operationId). + UpdateExperimentWithResponse(ctx context.Context, key string, body UpdateExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateExperimentResponse, error) + + // GetExperimentBadgeWithResponse Get experiment run status as SVG image + // + // Get the status of the latest experiment run of the associated experiment as SVG to integrate it nicely e.g. into your CMS-systems. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/{key}/badge.svg (the `GetExperimentBadge` operationId). + GetExperimentBadgeWithResponse(ctx context.Context, key string, params *GetExperimentBadgeParams, reqEditors ...RequestEditorFn) (*GetExperimentBadgeResponse, error) + + // ExecuteExperimentWithBodyWithResponse Execute an experiment + // + // Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. + // + // Examples: + // - Override environment from the experiment for a single run: + // ``` + // { + // "environment": "Shop Stage" + // } + // ``` + // - Override the variables for a single execution: + // ``` + // { + // "variables": { + // "httpEndpoint": "http://dev.shop.products.internal" + // } + // } + // ``` + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/{key}/execute (the `ExecuteExperiment` operationId). + ExecuteExperimentWithBodyWithResponse(ctx context.Context, key string, params *ExecuteExperimentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ExecuteExperimentResponse, error) + + // ExecuteExperimentWithResponse Execute an experiment + // + // Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. + // + // Examples: + // - Override environment from the experiment for a single run: + // ``` + // { + // "environment": "Shop Stage" + // } + // ``` + // - Override the variables for a single execution: + // ``` + // { + // "variables": { + // "httpEndpoint": "http://dev.shop.products.internal" + // } + // } + // ``` + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/experiments/{key}/execute (the `ExecuteExperiment` operationId). + ExecuteExperimentWithResponse(ctx context.Context, key string, params *ExecuteExperimentParams, body ExecuteExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*ExecuteExperimentResponse, error) + + // GetExperimentExecutions3WithResponse Fetch a list of all experiment executions of a single experiment + // + // Get a list of all experiment executions that were performed for a specific experiment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/experiments/{key}/executions (the `GetExperimentExecutions3` operationId). + GetExperimentExecutions3WithResponse(ctx context.Context, key string, params *GetExperimentExecutions3Params, reqEditors ...RequestEditorFn) (*GetExperimentExecutions3Response, error) + + // GetLandscapeViewsWithResponse Fetch all saved landscape views of a team + // + // Get a list of all saved explorer landscape views that belong to the given team. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/explore/landscape/views (the `GetLandscapeViews` operationId). + GetLandscapeViewsWithResponse(ctx context.Context, params *GetLandscapeViewsParams, reqEditors ...RequestEditorFn) (*GetLandscapeViewsResponse, error) + + // CreateLandscapeViewWithBodyWithResponse Create a saved landscape view + // + // Create a new saved explorer landscape view for a team. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/explore/landscape/views (the `CreateLandscapeView` operationId). + CreateLandscapeViewWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateLandscapeViewResponse, error) + + // CreateLandscapeViewWithResponse Create a saved landscape view + // + // Create a new saved explorer landscape view for a team. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/explore/landscape/views (the `CreateLandscapeView` operationId). + CreateLandscapeViewWithResponse(ctx context.Context, body CreateLandscapeViewJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateLandscapeViewResponse, error) + + // DeleteLandscapeViewWithResponse Delete a saved landscape view + // + // Remove the given saved explorer landscape view from the Steadybit platform. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/explore/landscape/views/{id} (the `DeleteLandscapeView` operationId). + DeleteLandscapeViewWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteLandscapeViewResponse, error) + + // GetLandscapeViewWithResponse Fetch a single saved landscape view + // + // Get all details of a single saved explorer landscape view. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/explore/landscape/views/{id} (the `GetLandscapeView` operationId). + GetLandscapeViewWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetLandscapeViewResponse, error) + + // UpdateLandscapeViewWithBodyWithResponse Update a saved landscape view + // + // Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/explore/landscape/views/{id} (the `UpdateLandscapeView` operationId). + UpdateLandscapeViewWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateLandscapeViewResponse, error) + + // UpdateLandscapeViewWithResponse Update a saved landscape view + // + // Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/explore/landscape/views/{id} (the `UpdateLandscapeView` operationId). + UpdateLandscapeViewWithResponse(ctx context.Context, id openapi_types.UUID, body UpdateLandscapeViewJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateLandscapeViewResponse, error) + + // HealthWithResponse performs a GET /api/health (the `Health` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + HealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HealthResponse, error) + + // LivenessWithResponse performs a GET /api/health/liveness (the `Liveness` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + LivenessWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*LivenessResponse, error) + + // ReadinessWithResponse performs a GET /api/health/readiness (the `Readiness` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + ReadinessWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ReadinessResponse, error) + + // GetHubsWithResponse Fetch a list of all hubs + // + // Get a list of all hubs that are currently connected. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/hubs (the `GetHubs` operationId). + GetHubsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHubsResponse, error) + + // UpsertHubWithBodyWithResponse Create or update a hub + // + // Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/hubs (the `UpsertHub` operationId). + UpsertHubWithBodyWithResponse(ctx context.Context, params *UpsertHubParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertHubResponse, error) + + // UpsertHubWithResponse Create or update a hub + // + // Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/hubs (the `UpsertHub` operationId). + UpsertHubWithResponse(ctx context.Context, params *UpsertHubParams, body UpsertHubJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertHubResponse, error) + + // ConnectionCheckWithBodyWithResponse Check a hub connection + // + // Check if the given hub connection details point to a valid hub. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/hubs/connection-check (the `ConnectionCheck` operationId). + ConnectionCheckWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ConnectionCheckResponse, error) + + // ConnectionCheckWithResponse Check a hub connection + // + // Check if the given hub connection details point to a valid hub. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/hubs/connection-check (the `ConnectionCheck` operationId). + ConnectionCheckWithResponse(ctx context.Context, body ConnectionCheckJSONRequestBody, reqEditors ...RequestEditorFn) (*ConnectionCheckResponse, error) + + // DeleteHubWithResponse Delete a hub + // + // Remove the given hub. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/hubs/{id} (the `DeleteHub` operationId). + DeleteHubWithResponse(ctx context.Context, id openapi_types.UUID, params *DeleteHubParams, reqEditors ...RequestEditorFn) (*DeleteHubResponse, error) + + // GetHubByIdWithResponse Fetch a single hub + // + // Get all details of a single hub. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/hubs/{id} (the `GetHubById` operationId). + GetHubByIdWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetHubByIdResponse, error) + + // ResyncHubWithResponse Re-synchronize a hub + // + // Fetch the latest hub definition based on `hubRepository`. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/hubs/{id}/resync (the `ResyncHub` operationId). + ResyncHubWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*ResyncHubResponse, error) + + // GetPreflightWebhooksWithResponse Fetch a list of preflight webhooks + // + // Get a list of all existing preflight webhooks. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/integrations/preflight (the `GetPreflightWebhooks` operationId). + GetPreflightWebhooksWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetPreflightWebhooksResponse, error) + + // UpsertPreflightWebhookWithBodyWithResponse Create or update a preflight webhook + // + // Insert or update a preflight webhook.
Experiment runs that were not executed due to engaged / active kill switch will not be automatically executed, they need to be triggered again. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/killswitch (the `DisengageKillswitch` operationId). + DisengageKillswitchWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*DisengageKillswitchResponse, error) + + // GetKillswitchWithResponse Get the current status of the kill switch + // + // Determines the current status of the kill switch without changing it. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/killswitch (the `GetKillswitch` operationId). + GetKillswitchWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetKillswitchResponse, error) + + // EngageKillswitchWithResponse Activate / engage the kill switch + // + // Activates / engages the kill switch to cancel all experiments running at the moment and prevent execution of new experiments until the kill switch is disengaged / deactivated again. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/killswitch (the `EngageKillswitch` operationId). + EngageKillswitchWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*EngageKillswitchResponse, error) + + // GetLicenseSummaryWithResponse Get license summary. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/license (the `GetLicenseSummary` operationId). + GetLicenseSummaryWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetLicenseSummaryResponse, error) + + // GetReportWithResponse Get license report. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/license/report (the `GetReport` operationId). + GetReportWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetReportResponse, error) + + // GetPreflightActionSummaryWithResponse Get all preflight actions. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/preflight/actions (the `GetPreflightActionSummary` operationId). + GetPreflightActionSummaryWithResponse(ctx context.Context, params *GetPreflightActionSummaryParams, reqEditors ...RequestEditorFn) (*GetPreflightActionSummaryResponse, error) + + // GetAssociationsWithResponse Get all current associations. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/properties/associations (the `GetAssociations` operationId). + GetAssociationsWithResponse(ctx context.Context, params *GetAssociationsParams, reqEditors ...RequestEditorFn) (*GetAssociationsResponse, error) + + // UpsertPropertyAssociationWithBodyWithResponse Create or update a property association + // + // Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. + // + // Examples: + // - Assign the property `RESULT_COLOR` to all experiment designs: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": false, + // "required": true + // } + // ``` + // - Assign the property `RESULT_COLOR` to the design ADM-15: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": false, + // "experimentKey": "ADM-15", + // "required": true + // } + // ``` + // - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": true, + // "experimentKey": "ADM-15", + // "required": false + // } + // ``` + // - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: + // ``` + // { + // "key": "RESULT_COLOR", + // "associationType": "SERVICE", + // "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", + // "required": false + // } + // ``` + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/properties/associations (the `UpsertPropertyAssociation` operationId). + UpsertPropertyAssociationWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertPropertyAssociationResponse, error) + + // UpsertPropertyAssociationWithResponse Create or update a property association + // + // Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. + // + // Examples: + // - Assign the property `RESULT_COLOR` to all experiment designs: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": false, + // "required": true + // } + // ``` + // - Assign the property `RESULT_COLOR` to the design ADM-15: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": false, + // "experimentKey": "ADM-15", + // "required": true + // } + // ``` + // - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: + // ``` + // { + // "key": "RESULT_COLOR", + // "editableInExecution": true, + // "experimentKey": "ADM-15", + // "required": false + // } + // ``` + // - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: + // ``` + // { + // "key": "RESULT_COLOR", + // "associationType": "SERVICE", + // "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", + // "required": false + // } + // ``` + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/properties/associations (the `UpsertPropertyAssociation` operationId). + UpsertPropertyAssociationWithResponse(ctx context.Context, body UpsertPropertyAssociationJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertPropertyAssociationResponse, error) + + // DeletePropertyAssociationWithResponse Remove an existing property association. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/properties/associations/{id} (the `DeletePropertyAssociation` operationId). + DeletePropertyAssociationWithResponse(ctx context.Context, id openapi_types.UUID, params *DeletePropertyAssociationParams, reqEditors ...RequestEditorFn) (*DeletePropertyAssociationResponse, error) + + // GetPropertyDefinition1WithResponse Get property association by a given id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/properties/associations/{id} (the `GetPropertyDefinition1` operationId). + GetPropertyDefinition1WithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetPropertyDefinition1Response, error) + + // GetPropertyDefinitionsWithResponse performs a GET /api/properties/definitions (the `GetPropertyDefinitions` operationId) request. + // + // Returns a wrapper object for the known response body format(s). + GetPropertyDefinitionsWithResponse(ctx context.Context, params *GetPropertyDefinitionsParams, reqEditors ...RequestEditorFn) (*GetPropertyDefinitionsResponse, error) + + // UpsertPropertyDefinitionWithBodyWithResponse Create or update property definition + // + // Insert or update the property definition specified by the given `key`. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/properties/definitions (the `UpsertPropertyDefinition` operationId). + UpsertPropertyDefinitionWithBodyWithResponse(ctx context.Context, params *UpsertPropertyDefinitionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertPropertyDefinitionResponse, error) + + // UpsertPropertyDefinitionWithResponse Create or update property definition + // + // Insert or update the property definition specified by the given `key`. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/properties/definitions (the `UpsertPropertyDefinition` operationId). + UpsertPropertyDefinitionWithResponse(ctx context.Context, params *UpsertPropertyDefinitionParams, body UpsertPropertyDefinitionJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertPropertyDefinitionResponse, error) + + // DeletePropertyDefinitionWithResponse Remove an existing property definition + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/properties/definitions/{key} (the `DeletePropertyDefinition` operationId). + DeletePropertyDefinitionWithResponse(ctx context.Context, key string, params *DeletePropertyDefinitionParams, reqEditors ...RequestEditorFn) (*DeletePropertyDefinitionResponse, error) + + // GetPropertyDefinitionWithResponse Get property definition for a specific property definition key. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/properties/definitions/{key} (the `GetPropertyDefinition` operationId). + GetPropertyDefinitionWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetPropertyDefinitionResponse, error) + + // GetEnvironmentCountsWithBodyWithResponse Get environment counts over time + // + // Returns the number of environments in the tenant aggregated into time buckets. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/environments (the `GetEnvironmentCounts` operationId). + GetEnvironmentCountsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetEnvironmentCountsResponse, error) + + // GetEnvironmentCountsWithResponse Get environment counts over time + // + // Returns the number of environments in the tenant aggregated into time buckets. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/environments (the `GetEnvironmentCounts` operationId). + GetEnvironmentCountsWithResponse(ctx context.Context, body GetEnvironmentCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetEnvironmentCountsResponse, error) + + // GetExperimentCreationsWithBodyWithResponse Get experiment creation counts over time + // + // Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/experiments/created (the `GetExperimentCreations` operationId). + GetExperimentCreationsWithBodyWithResponse(ctx context.Context, params *GetExperimentCreationsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetExperimentCreationsResponse, error) + + // GetExperimentCreationsWithResponse Get experiment creation counts over time + // + // Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/experiments/created (the `GetExperimentCreations` operationId). + GetExperimentCreationsWithResponse(ctx context.Context, params *GetExperimentCreationsParams, body GetExperimentCreationsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetExperimentCreationsResponse, error) + + // GetExperimentExecutionsWithBodyWithResponse Get experiment execution counts over time + // + // Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/experiments/executed (the `GetExperimentExecutions` operationId). + GetExperimentExecutionsWithBodyWithResponse(ctx context.Context, params *GetExperimentExecutionsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetExperimentExecutionsResponse, error) + + // GetExperimentExecutionsWithResponse Get experiment execution counts over time + // + // Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/experiments/executed (the `GetExperimentExecutions` operationId). + GetExperimentExecutionsWithResponse(ctx context.Context, params *GetExperimentExecutionsParams, body GetExperimentExecutionsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetExperimentExecutionsResponse, error) + + // GetAverageRiskWithBodyWithResponse Get average service risk over time + // + // Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/services/average (the `GetAverageRisk` operationId). + GetAverageRiskWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetAverageRiskResponse, error) + + // GetAverageRiskWithResponse Get average service risk over time + // + // Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/services/average (the `GetAverageRisk` operationId). + GetAverageRiskWithResponse(ctx context.Context, body GetAverageRiskJSONRequestBody, reqEditors ...RequestEditorFn) (*GetAverageRiskResponse, error) + + // GetRiskByCategoryWithBodyWithResponse Get average service risk grouped by category over time + // + // Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/services/by-category (the `GetRiskByCategory` operationId). + GetRiskByCategoryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetRiskByCategoryResponse, error) + + // GetRiskByCategoryWithResponse Get average service risk grouped by category over time + // + // Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/services/by-category (the `GetRiskByCategory` operationId). + GetRiskByCategoryWithResponse(ctx context.Context, body GetRiskByCategoryJSONRequestBody, reqEditors ...RequestEditorFn) (*GetRiskByCategoryResponse, error) + + // GetRiskDistributionWithBodyWithResponse Get service risk level distribution over time + // + // Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/services/distribution (the `GetRiskDistribution` operationId). + GetRiskDistributionWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetRiskDistributionResponse, error) + + // GetRiskDistributionWithResponse Get service risk level distribution over time + // + // Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/services/distribution (the `GetRiskDistribution` operationId). + GetRiskDistributionWithResponse(ctx context.Context, body GetRiskDistributionJSONRequestBody, reqEditors ...RequestEditorFn) (*GetRiskDistributionResponse, error) + + // GetTeamCountsWithBodyWithResponse Get team counts over time + // + // Returns the number of teams in the tenant aggregated into time buckets. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/teams (the `GetTeamCounts` operationId). + GetTeamCountsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetTeamCountsResponse, error) + + // GetTeamCountsWithResponse Get team counts over time + // + // Returns the number of teams in the tenant aggregated into time buckets. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/teams (the `GetTeamCounts` operationId). + GetTeamCountsWithResponse(ctx context.Context, body GetTeamCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetTeamCountsResponse, error) + + // GetUserCountsWithBodyWithResponse Get user counts over time + // + // Returns the number of users in the tenant aggregated into time buckets. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/users (the `GetUserCounts` operationId). + GetUserCountsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetUserCountsResponse, error) + + // GetUserCountsWithResponse Get user counts over time + // + // Returns the number of users in the tenant aggregated into time buckets. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/reports/users (the `GetUserCounts` operationId). + GetUserCountsWithResponse(ctx context.Context, body GetUserCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetUserCountsResponse, error) + + // GetServiceListWithResponse Fetch a list of services + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/services (the `GetServiceList` operationId). + GetServiceListWithResponse(ctx context.Context, params *GetServiceListParams, reqEditors ...RequestEditorFn) (*GetServiceListResponse, error) + + // UpsertServiceWithBodyWithResponse Create or update service + // + // Insert or update the service specified by the given `id`. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/services (the `UpsertService` operationId). + UpsertServiceWithBodyWithResponse(ctx context.Context, params *UpsertServiceParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertServiceResponse, error) + + // UpsertServiceWithResponse Create or update service + // + // Insert or update the service specified by the given `id`. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/services (the `UpsertService` operationId). + UpsertServiceWithResponse(ctx context.Context, params *UpsertServiceParams, body UpsertServiceJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertServiceResponse, error) + + // GetProfilesWithResponse Fetch a list of service profiles + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/services/profiles (the `GetProfiles` operationId). + GetProfilesWithResponse(ctx context.Context, params *GetProfilesParams, reqEditors ...RequestEditorFn) (*GetProfilesResponse, error) + + // UpsertProfileWithBodyWithResponse Create or update service profile + // + // Insert or update the service profile specified by the given `id`. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/services/profiles (the `UpsertProfile` operationId). + UpsertProfileWithBodyWithResponse(ctx context.Context, params *UpsertProfileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertProfileResponse, error) + + // UpsertProfileWithResponse Create or update service profile + // + // Insert or update the service profile specified by the given `id`. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/services/profiles (the `UpsertProfile` operationId). + UpsertProfileWithResponse(ctx context.Context, params *UpsertProfileParams, body UpsertProfileJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertProfileResponse, error) + + // DeleteProfileWithResponse Delete an existing service profile + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/services/profiles/{id} (the `DeleteProfile` operationId). + DeleteProfileWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteProfileResponse, error) + + // GetProfileWithResponse Get service profile by id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/services/profiles/{id} (the `GetProfile` operationId). + GetProfileWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetProfileResponse, error) + + // DeleteServiceWithResponse Delete an existing service + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/services/{id} (the `DeleteService` operationId). + DeleteServiceWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteServiceResponse, error) + + // GetServiceWithResponse Get service by id. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/services/{id} (the `GetService` operationId). + GetServiceWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetServiceResponse, error) + + // GetServiceExperimentsWithResponse Get experiments associated to an service. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/services/{id}/experiments (the `GetServiceExperiments` operationId). + GetServiceExperimentsWithResponse(ctx context.Context, id openapi_types.UUID, params *GetServiceExperimentsParams, reqEditors ...RequestEditorFn) (*GetServiceExperimentsResponse, error) + + // UnlinkCustomExperimentWithResponse Remove a linked custom experiment from a service. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/services/{id}/experiments/custom (the `UnlinkCustomExperiment` operationId). + UnlinkCustomExperimentWithResponse(ctx context.Context, id openapi_types.UUID, params *UnlinkCustomExperimentParams, reqEditors ...RequestEditorFn) (*UnlinkCustomExperimentResponse, error) + + // LinkCustomExperimentWithBodyWithResponse Link a custom experiment to a service. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/services/{id}/experiments/custom (the `LinkCustomExperiment` operationId). + LinkCustomExperimentWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*LinkCustomExperimentResponse, error) + + // LinkCustomExperimentWithResponse Link a custom experiment to a service. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/services/{id}/experiments/custom (the `LinkCustomExperiment` operationId). + LinkCustomExperimentWithResponse(ctx context.Context, id openapi_types.UUID, body LinkCustomExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*LinkCustomExperimentResponse, error) + + // UpsertProvidedExperimentWithBodyWithResponse Create or update a provided experiment. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/services/{id}/experiments/provided (the `UpsertProvidedExperiment` operationId). + UpsertProvidedExperimentWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, params *UpsertProvidedExperimentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertProvidedExperimentResponse, error) + + // UpsertProvidedExperimentWithResponse Create or update a provided experiment. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/services/{id}/experiments/provided (the `UpsertProvidedExperiment` operationId). + UpsertProvidedExperimentWithResponse(ctx context.Context, id openapi_types.UUID, params *UpsertProvidedExperimentParams, body UpsertProvidedExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertProvidedExperimentResponse, error) + + // GetRiskWithResponse Get the risk score for a service + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/services/{id}/risk (the `GetRisk` operationId). + GetRiskWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetRiskResponse, error) + + // GetServiceVariablesWithResponse Get service variables + // + // Get all variables owned by the service. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/services/{id}/variables (the `GetServiceVariables` operationId). + GetServiceVariablesWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetServiceVariablesResponse, error) + + // MergeServiceVariablesWithBodyWithResponse Add / merge service variables + // + // All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PATCH /api/services/{id}/variables (the `MergeServiceVariables` operationId). + MergeServiceVariablesWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MergeServiceVariablesResponse, error) + + // MergeServiceVariablesWithResponse Add / merge service variables + // + // All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PATCH /api/services/{id}/variables (the `MergeServiceVariables` operationId). + MergeServiceVariablesWithResponse(ctx context.Context, id openapi_types.UUID, body MergeServiceVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*MergeServiceVariablesResponse, error) + + // SetServiceVariablesWithBodyWithResponse Replace all service variables + // + // All provided variables will be associated with the given service and existing ones removed. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/services/{id}/variables (the `SetServiceVariables` operationId). + SetServiceVariablesWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetServiceVariablesResponse, error) + + // SetServiceVariablesWithResponse Replace all service variables + // + // All provided variables will be associated with the given service and existing ones removed. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/services/{id}/variables (the `SetServiceVariables` operationId). + SetServiceVariablesWithResponse(ctx context.Context, id openapi_types.UUID, body SetServiceVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*SetServiceVariablesResponse, error) + + // GetTargetsStatsWithResponse Gather target statistics without any filters + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/target-stats (the `GetTargetsStats` operationId). + GetTargetsStatsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetTargetsStatsResponse, error) + + // GetTargetsStats1WithBodyWithResponse Gather target statistics for a given predicate or query + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/target-stats (the `GetTargetsStats1` operationId). + GetTargetsStats1WithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetTargetsStats1Response, error) + + // GetTargetsStats1WithResponse Gather target statistics for a given predicate or query + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/target-stats (the `GetTargetsStats1` operationId). + GetTargetsStats1WithResponse(ctx context.Context, body GetTargetsStats1JSONRequestBody, reqEditors ...RequestEditorFn) (*GetTargetsStats1Response, error) + + // GetTargetsWithResponse Get targets + // + // Get targets. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/targets (the `GetTargets` operationId). + GetTargetsWithResponse(ctx context.Context, params *GetTargetsParams, reqEditors ...RequestEditorFn) (*GetTargetsResponse, error) + + // GetTargetAttributeKeysWithResponse Get attribute key + // + // Get all available attribute keys for a specific target type in a given environment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/targets/attributes/keys (the `GetTargetAttributeKeys` operationId). + GetTargetAttributeKeysWithResponse(ctx context.Context, params *GetTargetAttributeKeysParams, reqEditors ...RequestEditorFn) (*GetTargetAttributeKeysResponse, error) + + // GetTargetAttributeValuesWithResponse Get attribute values + // + // Get all available attribute values for a specific attribute and target type in a given environment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/targets/attributes/values (the `GetTargetAttributeValues` operationId). + GetTargetAttributeValuesWithResponse(ctx context.Context, params *GetTargetAttributeValuesParams, reqEditors ...RequestEditorFn) (*GetTargetAttributeValuesResponse, error) + + // GetTeamsWithResponse Fetch a list of all teams + // + // Get a list of all teams that exist.
If used with a team-associated `accessToken` and `onlyAccessible` is set to `true` you only get the team of the `accessToken`. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/teams (the `GetTeams` operationId). + GetTeamsWithResponse(ctx context.Context, params *GetTeamsParams, reqEditors ...RequestEditorFn) (*GetTeamsResponse, error) + + // UpsertTeamWithBodyWithResponse Create or update a team + // + // Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams (the `UpsertTeam` operationId). + UpsertTeamWithBodyWithResponse(ctx context.Context, params *UpsertTeamParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertTeamResponse, error) + + // UpsertTeamWithResponse Create or update a team + // + // Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams (the `UpsertTeam` operationId). + UpsertTeamWithResponse(ctx context.Context, params *UpsertTeamParams, body UpsertTeamJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertTeamResponse, error) + + // DeleteTeamWithResponse Delete team + // + // Remove the given team from the Steadybit platform. This will only work, if there are no experiments running at the moment. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with DELETE /api/teams/{key} (the `DeleteTeam` operationId). + DeleteTeamWithResponse(ctx context.Context, key string, params *DeleteTeamParams, reqEditors ...RequestEditorFn) (*DeleteTeamResponse, error) + + // GetTeamWithResponse Fetch a single team + // + // Get all details of a single existing teams. + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/teams/{key} (the `GetTeam` operationId). + GetTeamWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetTeamResponse, error) + + // GetTeamEnvironmentsWithResponse Get all environments assigned to the team + // + // Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/teams/{key}/environments (the `GetTeamEnvironments` operationId). + GetTeamEnvironmentsWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetTeamEnvironmentsResponse, error) + + // SetTeamEnvironmentsWithBodyWithResponse Update the environments of a specific team + // + // The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/teams/{key}/environments (the `SetTeamEnvironments` operationId). + SetTeamEnvironmentsWithBodyWithResponse(ctx context.Context, key string, params *SetTeamEnvironmentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetTeamEnvironmentsResponse, error) + + // SetTeamEnvironmentsWithResponse Update the environments of a specific team + // + // The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/teams/{key}/environments (the `SetTeamEnvironments` operationId). + SetTeamEnvironmentsWithResponse(ctx context.Context, key string, params *SetTeamEnvironmentsParams, body SetTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*SetTeamEnvironmentsResponse, error) + + // AddTeamEnvironmentsWithBodyWithResponse Add an allowed environment to a team + // + // The given environments will be added to the specified team. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams/{key}/environments/add (the `AddTeamEnvironments` operationId). + AddTeamEnvironmentsWithBodyWithResponse(ctx context.Context, key string, params *AddTeamEnvironmentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AddTeamEnvironmentsResponse, error) + + // AddTeamEnvironmentsWithResponse Add an allowed environment to a team + // + // The given environments will be added to the specified team. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams/{key}/environments/add (the `AddTeamEnvironments` operationId). + AddTeamEnvironmentsWithResponse(ctx context.Context, key string, params *AddTeamEnvironmentsParams, body AddTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*AddTeamEnvironmentsResponse, error) + + // RemoveTeamEnvironmentsWithBodyWithResponse Remove allowed environment from a team + // + // The given environments will be removed from the specified team. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams/{key}/environments/remove (the `RemoveTeamEnvironments` operationId). + RemoveTeamEnvironmentsWithBodyWithResponse(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RemoveTeamEnvironmentsResponse, error) + + // RemoveTeamEnvironmentsWithResponse Remove allowed environment from a team + // + // The given environments will be removed from the specified team. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams/{key}/environments/remove (the `RemoveTeamEnvironments` operationId). + RemoveTeamEnvironmentsWithResponse(ctx context.Context, key string, body RemoveTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*RemoveTeamEnvironmentsResponse, error) + + // GetTeamMembersWithResponse Get all members being part of the team + // + // Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). + // + // Returns a wrapper object for the known response body format(s). + // + // Corresponds with GET /api/teams/{key}/members (the `GetTeamMembers` operationId). + GetTeamMembersWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetTeamMembersResponse, error) + + // SetTeamMembersWithBodyWithResponse Update the members of a specific team + // + // The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/teams/{key}/members (the `SetTeamMembers` operationId). + SetTeamMembersWithBodyWithResponse(ctx context.Context, key string, params *SetTeamMembersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetTeamMembersResponse, error) + + // SetTeamMembersWithResponse Update the members of a specific team + // + // The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with PUT /api/teams/{key}/members (the `SetTeamMembers` operationId). + SetTeamMembersWithResponse(ctx context.Context, key string, params *SetTeamMembersParams, body SetTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*SetTeamMembersResponse, error) + + // AddTeamMembersWithBodyWithResponse Add team members to a team + // + // The given members will be added to the specified team. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams/{key}/members/add (the `AddTeamMembers` operationId). + AddTeamMembersWithBodyWithResponse(ctx context.Context, key string, params *AddTeamMembersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AddTeamMembersResponse, error) + + // AddTeamMembersWithResponse Add team members to a team + // + // The given members will be added to the specified team. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams/{key}/members/add (the `AddTeamMembers` operationId). + AddTeamMembersWithResponse(ctx context.Context, key string, params *AddTeamMembersParams, body AddTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*AddTeamMembersResponse, error) + + // RemoveTeamMembersWithBodyWithResponse Remove team members from a team + // + // The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams/{key}/members/remove (the `RemoveTeamMembers` operationId). + RemoveTeamMembersWithBodyWithResponse(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RemoveTeamMembersResponse, error) + + // RemoveTeamMembersWithResponse Remove team members from a team + // + // The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/teams/{key}/members/remove (the `RemoveTeamMembers` operationId). + RemoveTeamMembersWithResponse(ctx context.Context, key string, body RemoveTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*RemoveTeamMembersResponse, error) + + // InviteUserWithBodyWithResponse Invite users to a tenant + // + // Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. + // + // Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/users/invite (the `InviteUser` operationId). + InviteUserWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*InviteUserResponse, error) + + // InviteUserWithResponse Invite users to a tenant + // + // Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. + // + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Corresponds with POST /api/users/invite (the `InviteUser` operationId). + InviteUserWithResponse(ctx context.Context, body InviteUserJSONRequestBody, reqEditors ...RequestEditorFn) (*InviteUserResponse, error) +} + +type GetAccessTokensResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOAccessTokensPageItemAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOAccessTokensPageItemAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetAccessTokensResponse) GetJSON200() *PagedResponseAOAccessTokensPageItemAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetAccessTokensResponse) GetYAML200() *PagedResponseAOAccessTokensPageItemAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetAccessTokensResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetAccessTokensResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetAccessTokensResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetAccessTokensResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type CreateAccessTokenResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *CreateAccessTokenResponseAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *CreateAccessTokenResponseAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r CreateAccessTokenResponse) GetJSON200() *CreateAccessTokenResponseAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r CreateAccessTokenResponse) GetYAML200() *CreateAccessTokenResponseAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r CreateAccessTokenResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r CreateAccessTokenResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateAccessTokenResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreateAccessTokenResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetAccessTokens1Response struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOAccessTokensPageItemV2AO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOAccessTokensPageItemV2AO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetAccessTokens1Response) GetJSON200() *PagedResponseAOAccessTokensPageItemV2AO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetAccessTokens1Response) GetYAML200() *PagedResponseAOAccessTokensPageItemV2AO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetAccessTokens1Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetAccessTokens1Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetAccessTokens1Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetAccessTokens1Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type CreateAccessToken1Response struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *CreateAccessTokenResponseV2AO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *CreateAccessTokenResponseV2AO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r CreateAccessToken1Response) GetJSON200() *CreateAccessTokenResponseV2AO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r CreateAccessToken1Response) GetYAML200() *CreateAccessTokenResponseV2AO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r CreateAccessToken1Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r CreateAccessToken1Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateAccessToken1Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreateAccessToken1Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteAccessToken1Response struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteAccessToken1Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteAccessToken1Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteAccessToken1Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteAccessToken1Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type RecreateAccessTokenResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *CreateAccessTokenResponseV2AO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *CreateAccessTokenResponseV2AO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r RecreateAccessTokenResponse) GetJSON200() *CreateAccessTokenResponseV2AO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r RecreateAccessTokenResponse) GetYAML200() *CreateAccessTokenResponseV2AO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r RecreateAccessTokenResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r RecreateAccessTokenResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RecreateAccessTokenResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r RecreateAccessTokenResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteAccessTokenResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteAccessTokenResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteAccessTokenResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteAccessTokenResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteAccessTokenResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type FindAllActionsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ActionSummariesAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ActionSummariesAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r FindAllActionsResponse) GetJSON200() *ActionSummariesAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r FindAllActionsResponse) GetYAML200() *ActionSummariesAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r FindAllActionsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r FindAllActionsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r FindAllActionsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r FindAllActionsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetActionResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ActionAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ActionAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetActionResponse) GetJSON200() *ActionAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetActionResponse) GetYAML200() *ActionAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetActionResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetActionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetActionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetActionResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTargetAdviceSummaryResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *AdviceSummaryAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *AdviceSummaryAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTargetAdviceSummaryResponse) GetJSON200() *AdviceSummaryAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTargetAdviceSummaryResponse) GetYAML200() *AdviceSummaryAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTargetAdviceSummaryResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTargetAdviceSummaryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTargetAdviceSummaryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTargetAdviceSummaryResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type FindResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *[]AuditLogEntry +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r FindResponse) GetJSON200() *[]AuditLogEntry { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r FindResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r FindResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r FindResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r FindResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ForwardToPlatformResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON307 the response for an HTTP 307 `application/json` response + JSON307 *map[string]interface{} + // YAML307 the response for an HTTP 307 `application/yaml` response + YAML307 *map[string]interface{} +} + +// GetJSON307 returns the response for an HTTP 307 `application/json` response +func (r ForwardToPlatformResponse) GetJSON307() *map[string]interface{} { + return r.JSON307 +} + +// GetYAML307 returns the response for an HTTP 307 `application/yaml` response +func (r ForwardToPlatformResponse) GetYAML307() *map[string]interface{} { + return r.YAML307 +} + +// GetBody returns the raw response body bytes +func (r ForwardToPlatformResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r ForwardToPlatformResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ForwardToPlatformResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ForwardToPlatformResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetLinkedBadgeResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r GetLinkedBadgeResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetLinkedBadgeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetLinkedBadgeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetLinkedBadgeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetEnvironmentsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *EnvironmentSummariesAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *EnvironmentSummariesAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetEnvironmentsResponse) GetJSON200() *EnvironmentSummariesAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetEnvironmentsResponse) GetYAML200() *EnvironmentSummariesAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetEnvironmentsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetEnvironmentsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEnvironmentsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEnvironmentsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertEnvironmentResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *EnvironmentAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *EnvironmentAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *EnvironmentAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *EnvironmentAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertEnvironmentResponse) GetJSON200() *EnvironmentAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertEnvironmentResponse) GetYAML200() *EnvironmentAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertEnvironmentResponse) GetJSON201() *EnvironmentAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertEnvironmentResponse) GetYAML201() *EnvironmentAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertEnvironmentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertEnvironmentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertEnvironmentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertEnvironmentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteEnvironmentResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteEnvironmentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteEnvironmentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteEnvironmentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteEnvironmentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetEnvironmentResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *EnvironmentAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *EnvironmentAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetEnvironmentResponse) GetJSON200() *EnvironmentAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetEnvironmentResponse) GetYAML200() *EnvironmentAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetEnvironmentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetEnvironmentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEnvironmentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEnvironmentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetEnvironmentVariablesResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *string + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *string +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetEnvironmentVariablesResponse) GetJSON200() *string { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetEnvironmentVariablesResponse) GetYAML200() *string { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetEnvironmentVariablesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetEnvironmentVariablesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEnvironmentVariablesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEnvironmentVariablesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SetEnvironmentVariablesResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r SetEnvironmentVariablesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SetEnvironmentVariablesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SetEnvironmentVariablesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SetEnvironmentVariablesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpdateEnvironmentVariablesResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r UpdateEnvironmentVariablesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpdateEnvironmentVariablesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateEnvironmentVariablesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateEnvironmentVariablesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentSummariesAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentSummariesAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentsResponse) GetJSON200() *ExperimentSummariesAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentsResponse) GetYAML200() *ExperimentSummariesAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type CreateOrUpdateExperimentResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r CreateOrUpdateExperimentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r CreateOrUpdateExperimentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateOrUpdateExperimentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreateOrUpdateExperimentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SaveAndRunResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExecuteExperimentResponseAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExecuteExperimentResponseAO + // JSON422 the response for an HTTP 422 `application/json` response + JSON422 *ExecuteExperimentResponseAO + // YAML422 the response for an HTTP 422 `application/yaml` response + YAML422 *ExecuteExperimentResponseAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r SaveAndRunResponse) GetJSON200() *ExecuteExperimentResponseAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r SaveAndRunResponse) GetYAML200() *ExecuteExperimentResponseAO { + return r.YAML200 +} + +// GetJSON422 returns the response for an HTTP 422 `application/json` response +func (r SaveAndRunResponse) GetJSON422() *ExecuteExperimentResponseAO { + return r.JSON422 +} + +// GetYAML422 returns the response for an HTTP 422 `application/yaml` response +func (r SaveAndRunResponse) GetYAML422() *ExecuteExperimentResponseAO { + return r.YAML422 +} + +// GetBody returns the raw response body bytes +func (r SaveAndRunResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SaveAndRunResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SaveAndRunResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SaveAndRunResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentExecutions1Response struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentExecutionSummariesAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentExecutionSummariesAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentExecutions1Response) GetJSON200() *ExperimentExecutionSummariesAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentExecutions1Response) GetYAML200() *ExperimentExecutionSummariesAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentExecutions1Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentExecutions1Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentExecutions1Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentExecutions1Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentExecutions2Response struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOExperimentExecutionPageItemAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOExperimentExecutionPageItemAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentExecutions2Response) GetJSON200() *PagedResponseAOExperimentExecutionPageItemAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentExecutions2Response) GetYAML200() *PagedResponseAOExperimentExecutionPageItemAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentExecutions2Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentExecutions2Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentExecutions2Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentExecutions2Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentExecutionResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentExecutionAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentExecutionAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentExecutionResponse) GetJSON200() *ExperimentExecutionAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentExecutionResponse) GetYAML200() *ExperimentExecutionAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentExecutionResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentExecutionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentExecutionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentExecutionResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetArtifactResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r GetArtifactResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetArtifactResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetArtifactResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetArtifactResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type CancelExperimentExecutionResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r CancelExperimentExecutionResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r CancelExperimentExecutionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CancelExperimentExecutionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CancelExperimentExecutionResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpdateExecutionPropertiesResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r UpdateExecutionPropertiesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpdateExecutionPropertiesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateExecutionPropertiesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateExecutionPropertiesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type AddExecutionPropertyValueResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r AddExecutionPropertyValueResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r AddExecutionPropertyValueResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r AddExecutionPropertyValueResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r AddExecutionPropertyValueResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SetExecutionPropertyValueResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r SetExecutionPropertyValueResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SetExecutionPropertyValueResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SetExecutionPropertyValueResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SetExecutionPropertyValueResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertScheduleResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentScheduleAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentScheduleAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *ExperimentScheduleAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *ExperimentScheduleAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertScheduleResponse) GetJSON200() *ExperimentScheduleAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertScheduleResponse) GetYAML200() *ExperimentScheduleAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertScheduleResponse) GetJSON201() *ExperimentScheduleAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertScheduleResponse) GetYAML201() *ExperimentScheduleAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertScheduleResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertScheduleResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertScheduleResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertScheduleResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetAllSchedulesV2Response struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *[]ExperimentScheduleAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *[]ExperimentScheduleAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetAllSchedulesV2Response) GetJSON200() *[]ExperimentScheduleAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetAllSchedulesV2Response) GetYAML200() *[]ExperimentScheduleAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetAllSchedulesV2Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetAllSchedulesV2Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetAllSchedulesV2Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetAllSchedulesV2Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type RemoveExperimentScheduleByIdResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r RemoveExperimentScheduleByIdResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r RemoveExperimentScheduleByIdResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RemoveExperimentScheduleByIdResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r RemoveExperimentScheduleByIdResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetSchedulesResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentScheduleAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentScheduleAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetSchedulesResponse) GetJSON200() *ExperimentScheduleAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetSchedulesResponse) GetYAML200() *ExperimentScheduleAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetSchedulesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetSchedulesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSchedulesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetSchedulesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PatchScheduleResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentScheduleAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentScheduleAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r PatchScheduleResponse) GetJSON200() *ExperimentScheduleAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r PatchScheduleResponse) GetYAML200() *ExperimentScheduleAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r PatchScheduleResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r PatchScheduleResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PatchScheduleResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PatchScheduleResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentTemplatesResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentTemplateSummariesAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentTemplateSummariesAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentTemplatesResponse) GetJSON200() *ExperimentTemplateSummariesAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentTemplatesResponse) GetYAML200() *ExperimentTemplateSummariesAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentTemplatesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentTemplatesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentTemplatesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentTemplatesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertExperimentTemplateResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentTemplateAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentTemplateAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *ExperimentTemplateAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *ExperimentTemplateAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertExperimentTemplateResponse) GetJSON200() *ExperimentTemplateAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertExperimentTemplateResponse) GetYAML200() *ExperimentTemplateAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertExperimentTemplateResponse) GetJSON201() *ExperimentTemplateAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertExperimentTemplateResponse) GetYAML201() *ExperimentTemplateAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertExperimentTemplateResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertExperimentTemplateResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertExperimentTemplateResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertExperimentTemplateResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ImportFromHubResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r ImportFromHubResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r ImportFromHubResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ImportFromHubResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ImportFromHubResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteExperimentTemplateResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteExperimentTemplateResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteExperimentTemplateResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteExperimentTemplateResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteExperimentTemplateResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentTemplateResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentTemplateAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentTemplateAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentTemplateResponse) GetJSON200() *ExperimentTemplateAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentTemplateResponse) GetYAML200() *ExperimentTemplateAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentTemplateResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentTemplateResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentTemplateResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentTemplateResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type CreateExperimentByTemplateResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r CreateExperimentByTemplateResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r CreateExperimentByTemplateResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateExperimentByTemplateResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreateExperimentByTemplateResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SaveAndRunFromTemplateResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExecuteExperimentResponseAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExecuteExperimentResponseAO + // JSON422 the response for an HTTP 422 `application/json` response + JSON422 *ExecuteExperimentResponseAO + // YAML422 the response for an HTTP 422 `application/yaml` response + YAML422 *ExecuteExperimentResponseAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r SaveAndRunFromTemplateResponse) GetJSON200() *ExecuteExperimentResponseAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r SaveAndRunFromTemplateResponse) GetYAML200() *ExecuteExperimentResponseAO { + return r.YAML200 +} + +// GetJSON422 returns the response for an HTTP 422 `application/json` response +func (r SaveAndRunFromTemplateResponse) GetJSON422() *ExecuteExperimentResponseAO { + return r.JSON422 +} + +// GetYAML422 returns the response for an HTTP 422 `application/yaml` response +func (r SaveAndRunFromTemplateResponse) GetYAML422() *ExecuteExperimentResponseAO { + return r.YAML422 +} + +// GetBody returns the raw response body bytes +func (r SaveAndRunFromTemplateResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SaveAndRunFromTemplateResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SaveAndRunFromTemplateResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SaveAndRunFromTemplateResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpdateExperimentByTemplateResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r UpdateExperimentByTemplateResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpdateExperimentByTemplateResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateExperimentByTemplateResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateExperimentByTemplateResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteExperimentResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteExperimentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteExperimentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteExperimentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteExperimentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentResponse) GetJSON200() *ExperimentAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentResponse) GetYAML200() *ExperimentAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpdateExperimentResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r UpdateExperimentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpdateExperimentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateExperimentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateExperimentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentBadgeResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r GetExperimentBadgeResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentBadgeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentBadgeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentBadgeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ExecuteExperimentResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *ExecuteExperimentResponseAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *ExecuteExperimentResponseAO + // JSON404 the response for an HTTP 404 `application/json` response + JSON404 *ExecuteExperimentResponseAO + // YAML404 the response for an HTTP 404 `application/yaml` response + YAML404 *ExecuteExperimentResponseAO +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r ExecuteExperimentResponse) GetJSON201() *ExecuteExperimentResponseAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r ExecuteExperimentResponse) GetYAML201() *ExecuteExperimentResponseAO { + return r.YAML201 +} + +// GetJSON404 returns the response for an HTTP 404 `application/json` response +func (r ExecuteExperimentResponse) GetJSON404() *ExecuteExperimentResponseAO { + return r.JSON404 +} + +// GetYAML404 returns the response for an HTTP 404 `application/yaml` response +func (r ExecuteExperimentResponse) GetYAML404() *ExecuteExperimentResponseAO { + return r.YAML404 +} + +// GetBody returns the raw response body bytes +func (r ExecuteExperimentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r ExecuteExperimentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ExecuteExperimentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ExecuteExperimentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentExecutions3Response struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ExperimentExecutionSummariesAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ExperimentExecutionSummariesAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentExecutions3Response) GetJSON200() *ExperimentExecutionSummariesAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentExecutions3Response) GetYAML200() *ExperimentExecutionSummariesAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentExecutions3Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentExecutions3Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentExecutions3Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentExecutions3Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetLandscapeViewsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ListResponseLandscapeViewAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ListResponseLandscapeViewAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetLandscapeViewsResponse) GetJSON200() *ListResponseLandscapeViewAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetLandscapeViewsResponse) GetYAML200() *ListResponseLandscapeViewAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetLandscapeViewsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetLandscapeViewsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetLandscapeViewsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetLandscapeViewsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type CreateLandscapeViewResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *LandscapeViewAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *LandscapeViewAO +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r CreateLandscapeViewResponse) GetJSON201() *LandscapeViewAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r CreateLandscapeViewResponse) GetYAML201() *LandscapeViewAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r CreateLandscapeViewResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r CreateLandscapeViewResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateLandscapeViewResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r CreateLandscapeViewResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteLandscapeViewResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteLandscapeViewResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteLandscapeViewResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteLandscapeViewResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteLandscapeViewResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetLandscapeViewResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *LandscapeViewAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *LandscapeViewAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetLandscapeViewResponse) GetJSON200() *LandscapeViewAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetLandscapeViewResponse) GetYAML200() *LandscapeViewAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetLandscapeViewResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetLandscapeViewResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetLandscapeViewResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetLandscapeViewResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpdateLandscapeViewResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *LandscapeViewAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *LandscapeViewAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpdateLandscapeViewResponse) GetJSON200() *LandscapeViewAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpdateLandscapeViewResponse) GetYAML200() *LandscapeViewAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r UpdateLandscapeViewResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpdateLandscapeViewResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateLandscapeViewResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateLandscapeViewResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type HealthResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *map[string]interface{} + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *map[string]interface{} +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r HealthResponse) GetJSON200() *map[string]interface{} { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r HealthResponse) GetYAML200() *map[string]interface{} { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r HealthResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r HealthResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r HealthResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r HealthResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type LivenessResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *map[string]interface{} + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *map[string]interface{} +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r LivenessResponse) GetJSON200() *map[string]interface{} { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r LivenessResponse) GetYAML200() *map[string]interface{} { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r LivenessResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r LivenessResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r LivenessResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r LivenessResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ReadinessResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *map[string]interface{} + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *map[string]interface{} +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ReadinessResponse) GetJSON200() *map[string]interface{} { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r ReadinessResponse) GetYAML200() *map[string]interface{} { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r ReadinessResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r ReadinessResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ReadinessResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ReadinessResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetHubsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *HubSummariesAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *HubSummariesAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetHubsResponse) GetJSON200() *HubSummariesAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetHubsResponse) GetYAML200() *HubSummariesAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetHubsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetHubsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetHubsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetHubsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertHubResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *HubAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *HubAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *HubAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *HubAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertHubResponse) GetJSON200() *HubAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertHubResponse) GetYAML200() *HubAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertHubResponse) GetJSON201() *HubAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertHubResponse) GetYAML201() *HubAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertHubResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertHubResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertHubResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertHubResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ConnectionCheckResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *HubConnectionCheckResponseAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *HubConnectionCheckResponseAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ConnectionCheckResponse) GetJSON200() *HubConnectionCheckResponseAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r ConnectionCheckResponse) GetYAML200() *HubConnectionCheckResponseAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r ConnectionCheckResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r ConnectionCheckResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ConnectionCheckResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ConnectionCheckResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteHubResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteHubResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteHubResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteHubResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteHubResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetHubByIdResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *HubAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *HubAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetHubByIdResponse) GetJSON200() *HubAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetHubByIdResponse) GetYAML200() *HubAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetHubByIdResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetHubByIdResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetHubByIdResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetHubByIdResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type ResyncHubResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *HubAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *HubAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r ResyncHubResponse) GetJSON200() *HubAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r ResyncHubResponse) GetYAML200() *HubAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r ResyncHubResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r ResyncHubResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ResyncHubResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r ResyncHubResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetPreflightWebhooksResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ListResponsePreflightWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ListResponsePreflightWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPreflightWebhooksResponse) GetJSON200() *ListResponsePreflightWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetPreflightWebhooksResponse) GetYAML200() *ListResponsePreflightWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetPreflightWebhooksResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetPreflightWebhooksResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPreflightWebhooksResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPreflightWebhooksResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertPreflightWebhookResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PreflightWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PreflightWebhookAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *PreflightWebhookAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *PreflightWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertPreflightWebhookResponse) GetJSON200() *PreflightWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertPreflightWebhookResponse) GetYAML200() *PreflightWebhookAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertPreflightWebhookResponse) GetJSON201() *PreflightWebhookAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertPreflightWebhookResponse) GetYAML201() *PreflightWebhookAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertPreflightWebhookResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertPreflightWebhookResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertPreflightWebhookResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertPreflightWebhookResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetPreflightActionIntegrationsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ListResponsePreflightActionIntegrationAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ListResponsePreflightActionIntegrationAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPreflightActionIntegrationsResponse) GetJSON200() *ListResponsePreflightActionIntegrationAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetPreflightActionIntegrationsResponse) GetYAML200() *ListResponsePreflightActionIntegrationAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetPreflightActionIntegrationsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetPreflightActionIntegrationsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPreflightActionIntegrationsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPreflightActionIntegrationsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertPreflightActionIntegrationResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PreflightActionIntegrationAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PreflightActionIntegrationAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *PreflightActionIntegrationAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *PreflightActionIntegrationAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertPreflightActionIntegrationResponse) GetJSON200() *PreflightActionIntegrationAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertPreflightActionIntegrationResponse) GetYAML200() *PreflightActionIntegrationAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertPreflightActionIntegrationResponse) GetJSON201() *PreflightActionIntegrationAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertPreflightActionIntegrationResponse) GetYAML201() *PreflightActionIntegrationAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertPreflightActionIntegrationResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertPreflightActionIntegrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertPreflightActionIntegrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertPreflightActionIntegrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeletePreflightActionIntegrationResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PreflightActionIntegrationAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PreflightActionIntegrationAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r DeletePreflightActionIntegrationResponse) GetJSON200() *PreflightActionIntegrationAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r DeletePreflightActionIntegrationResponse) GetYAML200() *PreflightActionIntegrationAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r DeletePreflightActionIntegrationResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeletePreflightActionIntegrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeletePreflightActionIntegrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePreflightActionIntegrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetPreflightActionIntegrationResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PreflightActionIntegrationAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PreflightActionIntegrationAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPreflightActionIntegrationResponse) GetJSON200() *PreflightActionIntegrationAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetPreflightActionIntegrationResponse) GetYAML200() *PreflightActionIntegrationAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetPreflightActionIntegrationResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetPreflightActionIntegrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPreflightActionIntegrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPreflightActionIntegrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeletePreflightWebhookResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PreflightWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PreflightWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r DeletePreflightWebhookResponse) GetJSON200() *PreflightWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r DeletePreflightWebhookResponse) GetYAML200() *PreflightWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r DeletePreflightWebhookResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeletePreflightWebhookResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeletePreflightWebhookResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePreflightWebhookResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetPreflightWebhookResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PreflightWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PreflightWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPreflightWebhookResponse) GetJSON200() *PreflightWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetPreflightWebhookResponse) GetYAML200() *PreflightWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetPreflightWebhookResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetPreflightWebhookResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPreflightWebhookResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPreflightWebhookResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetSlackIntegrationsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ListResponseSlackWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ListResponseSlackWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetSlackIntegrationsResponse) GetJSON200() *ListResponseSlackWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetSlackIntegrationsResponse) GetYAML200() *ListResponseSlackWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetSlackIntegrationsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetSlackIntegrationsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSlackIntegrationsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetSlackIntegrationsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertSlackIntegrationResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *SlackWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *SlackWebhookAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *SlackWebhookAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *SlackWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertSlackIntegrationResponse) GetJSON200() *SlackWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertSlackIntegrationResponse) GetYAML200() *SlackWebhookAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertSlackIntegrationResponse) GetJSON201() *SlackWebhookAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertSlackIntegrationResponse) GetYAML201() *SlackWebhookAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertSlackIntegrationResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertSlackIntegrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertSlackIntegrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertSlackIntegrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteSlackIntegrationResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *SlackWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *SlackWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r DeleteSlackIntegrationResponse) GetJSON200() *SlackWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r DeleteSlackIntegrationResponse) GetYAML200() *SlackWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r DeleteSlackIntegrationResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteSlackIntegrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteSlackIntegrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteSlackIntegrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetSlackIntegrationResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *SlackWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *SlackWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetSlackIntegrationResponse) GetJSON200() *SlackWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetSlackIntegrationResponse) GetYAML200() *SlackWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetSlackIntegrationResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetSlackIntegrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSlackIntegrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetSlackIntegrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetCustomWebhooksResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ListResponseCustomWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ListResponseCustomWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetCustomWebhooksResponse) GetJSON200() *ListResponseCustomWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetCustomWebhooksResponse) GetYAML200() *ListResponseCustomWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetCustomWebhooksResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetCustomWebhooksResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetCustomWebhooksResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetCustomWebhooksResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertCustomWebhookResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *CustomWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *CustomWebhookAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *CustomWebhookAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *CustomWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertCustomWebhookResponse) GetJSON200() *CustomWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertCustomWebhookResponse) GetYAML200() *CustomWebhookAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertCustomWebhookResponse) GetJSON201() *CustomWebhookAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertCustomWebhookResponse) GetYAML201() *CustomWebhookAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertCustomWebhookResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertCustomWebhookResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertCustomWebhookResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertCustomWebhookResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteCustomWebhookResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *CustomWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *CustomWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r DeleteCustomWebhookResponse) GetJSON200() *CustomWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r DeleteCustomWebhookResponse) GetYAML200() *CustomWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r DeleteCustomWebhookResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteCustomWebhookResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteCustomWebhookResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteCustomWebhookResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetCustomWebhookResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *CustomWebhookAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *CustomWebhookAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetCustomWebhookResponse) GetJSON200() *CustomWebhookAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetCustomWebhookResponse) GetYAML200() *CustomWebhookAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetCustomWebhookResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetCustomWebhookResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetCustomWebhookResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetCustomWebhookResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DisengageKillswitchResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DisengageKillswitchResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DisengageKillswitchResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DisengageKillswitchResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DisengageKillswitchResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetKillswitchResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *KillswitchAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *KillswitchAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetKillswitchResponse) GetJSON200() *KillswitchAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetKillswitchResponse) GetYAML200() *KillswitchAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetKillswitchResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetKillswitchResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetKillswitchResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetKillswitchResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type EngageKillswitchResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r EngageKillswitchResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r EngageKillswitchResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r EngageKillswitchResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r EngageKillswitchResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetLicenseSummaryResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *GetLicenseSummaryAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *GetLicenseSummaryAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetLicenseSummaryResponse) GetJSON200() *GetLicenseSummaryAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetLicenseSummaryResponse) GetYAML200() *GetLicenseSummaryAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetLicenseSummaryResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetLicenseSummaryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetLicenseSummaryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetLicenseSummaryResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetReportResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r GetReportResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetReportResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetReportResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetReportResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetPreflightActionSummaryResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PreflightActionSummaryAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PreflightActionSummaryAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPreflightActionSummaryResponse) GetJSON200() *PreflightActionSummaryAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetPreflightActionSummaryResponse) GetYAML200() *PreflightActionSummaryAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetPreflightActionSummaryResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetPreflightActionSummaryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPreflightActionSummaryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPreflightActionSummaryResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetAssociationsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOPropertyAssociationAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOPropertyAssociationAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetAssociationsResponse) GetJSON200() *PagedResponseAOPropertyAssociationAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetAssociationsResponse) GetYAML200() *PagedResponseAOPropertyAssociationAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetAssociationsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetAssociationsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetAssociationsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetAssociationsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertPropertyAssociationResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PropertyAssociationAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PropertyAssociationAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *PropertyAssociationAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *PropertyAssociationAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertPropertyAssociationResponse) GetJSON200() *PropertyAssociationAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertPropertyAssociationResponse) GetYAML200() *PropertyAssociationAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertPropertyAssociationResponse) GetJSON201() *PropertyAssociationAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertPropertyAssociationResponse) GetYAML201() *PropertyAssociationAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertPropertyAssociationResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertPropertyAssociationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertPropertyAssociationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertPropertyAssociationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeletePropertyAssociationResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeletePropertyAssociationResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeletePropertyAssociationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeletePropertyAssociationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePropertyAssociationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetPropertyDefinition1Response struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PropertyAssociationAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PropertyAssociationAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPropertyDefinition1Response) GetJSON200() *PropertyAssociationAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetPropertyDefinition1Response) GetYAML200() *PropertyAssociationAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetPropertyDefinition1Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetPropertyDefinition1Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPropertyDefinition1Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPropertyDefinition1Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetPropertyDefinitionsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOPropertyDefinitionAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOPropertyDefinitionAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPropertyDefinitionsResponse) GetJSON200() *PagedResponseAOPropertyDefinitionAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetPropertyDefinitionsResponse) GetYAML200() *PagedResponseAOPropertyDefinitionAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetPropertyDefinitionsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetPropertyDefinitionsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPropertyDefinitionsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPropertyDefinitionsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertPropertyDefinitionResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PropertyDefinitionAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PropertyDefinitionAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *PropertyDefinitionAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *PropertyDefinitionAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertPropertyDefinitionResponse) GetJSON200() *PropertyDefinitionAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertPropertyDefinitionResponse) GetYAML200() *PropertyDefinitionAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertPropertyDefinitionResponse) GetJSON201() *PropertyDefinitionAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertPropertyDefinitionResponse) GetYAML201() *PropertyDefinitionAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertPropertyDefinitionResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertPropertyDefinitionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertPropertyDefinitionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertPropertyDefinitionResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeletePropertyDefinitionResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeletePropertyDefinitionResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeletePropertyDefinitionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeletePropertyDefinitionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeletePropertyDefinitionResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetPropertyDefinitionResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PropertyDefinitionAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PropertyDefinitionAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetPropertyDefinitionResponse) GetJSON200() *PropertyDefinitionAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetPropertyDefinitionResponse) GetYAML200() *PropertyDefinitionAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetPropertyDefinitionResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetPropertyDefinitionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPropertyDefinitionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetPropertyDefinitionResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetEnvironmentCountsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TimeSeriesReportAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TimeSeriesReportAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetEnvironmentCountsResponse) GetJSON200() *TimeSeriesReportAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetEnvironmentCountsResponse) GetYAML200() *TimeSeriesReportAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetEnvironmentCountsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetEnvironmentCountsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEnvironmentCountsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEnvironmentCountsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentCreationsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TimeSeriesReportAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TimeSeriesReportAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentCreationsResponse) GetJSON200() *TimeSeriesReportAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentCreationsResponse) GetYAML200() *TimeSeriesReportAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentCreationsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentCreationsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentCreationsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentCreationsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetExperimentExecutionsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TimeSeriesReportAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TimeSeriesReportAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetExperimentExecutionsResponse) GetJSON200() *TimeSeriesReportAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetExperimentExecutionsResponse) GetYAML200() *TimeSeriesReportAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetExperimentExecutionsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetExperimentExecutionsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetExperimentExecutionsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetExperimentExecutionsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetAverageRiskResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TimeSeriesReportAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TimeSeriesReportAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetAverageRiskResponse) GetJSON200() *TimeSeriesReportAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetAverageRiskResponse) GetYAML200() *TimeSeriesReportAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetAverageRiskResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetAverageRiskResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetAverageRiskResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetAverageRiskResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetRiskByCategoryResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TimeSeriesReportAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TimeSeriesReportAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetRiskByCategoryResponse) GetJSON200() *TimeSeriesReportAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetRiskByCategoryResponse) GetYAML200() *TimeSeriesReportAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetRiskByCategoryResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetRiskByCategoryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetRiskByCategoryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetRiskByCategoryResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetRiskDistributionResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TimeSeriesReportAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TimeSeriesReportAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetRiskDistributionResponse) GetJSON200() *TimeSeriesReportAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetRiskDistributionResponse) GetYAML200() *TimeSeriesReportAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetRiskDistributionResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetRiskDistributionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetRiskDistributionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetRiskDistributionResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTeamCountsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TimeSeriesReportAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TimeSeriesReportAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTeamCountsResponse) GetJSON200() *TimeSeriesReportAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTeamCountsResponse) GetYAML200() *TimeSeriesReportAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTeamCountsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTeamCountsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTeamCountsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTeamCountsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetUserCountsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TimeSeriesReportAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TimeSeriesReportAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetUserCountsResponse) GetJSON200() *TimeSeriesReportAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetUserCountsResponse) GetYAML200() *TimeSeriesReportAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetUserCountsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetUserCountsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetUserCountsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetUserCountsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetServiceListResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOServiceSummaryAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOServiceSummaryAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetServiceListResponse) GetJSON200() *PagedResponseAOServiceSummaryAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetServiceListResponse) GetYAML200() *PagedResponseAOServiceSummaryAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetServiceListResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetServiceListResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetServiceListResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetServiceListResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertServiceResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ServiceAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ServiceAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *ServiceAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *ServiceAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertServiceResponse) GetJSON200() *ServiceAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertServiceResponse) GetYAML200() *ServiceAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertServiceResponse) GetJSON201() *ServiceAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertServiceResponse) GetYAML201() *ServiceAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertServiceResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertServiceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertServiceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertServiceResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetProfilesResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOServiceProfileAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOServiceProfileAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetProfilesResponse) GetJSON200() *PagedResponseAOServiceProfileAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetProfilesResponse) GetYAML200() *PagedResponseAOServiceProfileAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetProfilesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetProfilesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetProfilesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetProfilesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertProfileResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ServiceProfileAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ServiceProfileAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *ServiceProfileAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *ServiceProfileAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertProfileResponse) GetJSON200() *ServiceProfileAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertProfileResponse) GetYAML200() *ServiceProfileAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertProfileResponse) GetJSON201() *ServiceProfileAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertProfileResponse) GetYAML201() *ServiceProfileAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertProfileResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertProfileResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertProfileResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertProfileResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteProfileResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteProfileResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteProfileResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteProfileResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteProfileResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetProfileResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ServiceProfileAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ServiceProfileAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetProfileResponse) GetJSON200() *ServiceProfileAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetProfileResponse) GetYAML200() *ServiceProfileAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetProfileResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetProfileResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetProfileResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetProfileResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteServiceResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r DeleteServiceResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteServiceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteServiceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteServiceResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetServiceResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ServiceAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ServiceAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetServiceResponse) GetJSON200() *ServiceAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetServiceResponse) GetYAML200() *ServiceAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetServiceResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetServiceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetServiceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetServiceResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetServiceExperimentsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOServiceExperimentAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOServiceExperimentAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetServiceExperimentsResponse) GetJSON200() *PagedResponseAOServiceExperimentAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetServiceExperimentsResponse) GetYAML200() *PagedResponseAOServiceExperimentAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetServiceExperimentsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetServiceExperimentsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetServiceExperimentsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetServiceExperimentsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UnlinkCustomExperimentResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r UnlinkCustomExperimentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UnlinkCustomExperimentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UnlinkCustomExperimentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UnlinkCustomExperimentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type LinkCustomExperimentResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r LinkCustomExperimentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r LinkCustomExperimentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r LinkCustomExperimentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r LinkCustomExperimentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertProvidedExperimentResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r UpsertProvidedExperimentResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertProvidedExperimentResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertProvidedExperimentResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertProvidedExperimentResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetRiskResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *ServiceRiskAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *ServiceRiskAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetRiskResponse) GetJSON200() *ServiceRiskAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetRiskResponse) GetYAML200() *ServiceRiskAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetRiskResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetRiskResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetRiskResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetRiskResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetServiceVariablesResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *string + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *string +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetServiceVariablesResponse) GetJSON200() *string { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetServiceVariablesResponse) GetYAML200() *string { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetServiceVariablesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetServiceVariablesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetServiceVariablesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetServiceVariablesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type MergeServiceVariablesResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r MergeServiceVariablesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r MergeServiceVariablesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r MergeServiceVariablesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r MergeServiceVariablesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SetServiceVariablesResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r SetServiceVariablesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SetServiceVariablesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SetServiceVariablesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SetServiceVariablesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTargetsStatsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *map[string]int64 + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *map[string]int64 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTargetsStatsResponse) GetJSON200() *map[string]int64 { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTargetsStatsResponse) GetYAML200() *map[string]int64 { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTargetsStatsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTargetsStatsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTargetsStatsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTargetsStatsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTargetsStats1Response struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *map[string]int64 + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *map[string]int64 +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTargetsStats1Response) GetJSON200() *map[string]int64 { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTargetsStats1Response) GetYAML200() *map[string]int64 { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTargetsStats1Response) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTargetsStats1Response) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTargetsStats1Response) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTargetsStats1Response) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTargetsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *CursorSliceResponseAOTargetAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *CursorSliceResponseAOTargetAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTargetsResponse) GetJSON200() *CursorSliceResponseAOTargetAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTargetsResponse) GetYAML200() *CursorSliceResponseAOTargetAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTargetsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTargetsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTargetsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTargetsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTargetAttributeKeysResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOString + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOString +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTargetAttributeKeysResponse) GetJSON200() *PagedResponseAOString { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTargetAttributeKeysResponse) GetYAML200() *PagedResponseAOString { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTargetAttributeKeysResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTargetAttributeKeysResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTargetAttributeKeysResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTargetAttributeKeysResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTargetAttributeValuesResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *PagedResponseAOString + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *PagedResponseAOString +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTargetAttributeValuesResponse) GetJSON200() *PagedResponseAOString { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTargetAttributeValuesResponse) GetYAML200() *PagedResponseAOString { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTargetAttributeValuesResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTargetAttributeValuesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTargetAttributeValuesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTargetAttributeValuesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTeamsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamSummariesAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamSummariesAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTeamsResponse) GetJSON200() *TeamSummariesAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTeamsResponse) GetYAML200() *TeamSummariesAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTeamsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTeamsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTeamsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTeamsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpsertTeamResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamAO + // JSON201 the response for an HTTP 201 `application/json` response + JSON201 *TeamAO + // YAML201 the response for an HTTP 201 `application/yaml` response + YAML201 *TeamAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r UpsertTeamResponse) GetJSON200() *TeamAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r UpsertTeamResponse) GetYAML200() *TeamAO { + return r.YAML200 +} + +// GetJSON201 returns the response for an HTTP 201 `application/json` response +func (r UpsertTeamResponse) GetJSON201() *TeamAO { + return r.JSON201 +} + +// GetYAML201 returns the response for an HTTP 201 `application/yaml` response +func (r UpsertTeamResponse) GetYAML201() *TeamAO { + return r.YAML201 +} + +// GetBody returns the raw response body bytes +func (r UpsertTeamResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r UpsertTeamResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpsertTeamResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpsertTeamResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteTeamResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamAO + // JSON403 the response for an HTTP 403 `application/json` response + JSON403 *TeamAO + // YAML403 the response for an HTTP 403 `application/yaml` response + YAML403 *TeamAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r DeleteTeamResponse) GetJSON200() *TeamAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r DeleteTeamResponse) GetYAML200() *TeamAO { + return r.YAML200 +} + +// GetJSON403 returns the response for an HTTP 403 `application/json` response +func (r DeleteTeamResponse) GetJSON403() *TeamAO { + return r.JSON403 +} + +// GetYAML403 returns the response for an HTTP 403 `application/yaml` response +func (r DeleteTeamResponse) GetYAML403() *TeamAO { + return r.YAML403 +} + +// GetBody returns the raw response body bytes +func (r DeleteTeamResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r DeleteTeamResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteTeamResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteTeamResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTeamResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTeamResponse) GetJSON200() *TeamAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTeamResponse) GetYAML200() *TeamAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTeamResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTeamResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTeamResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTeamResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTeamEnvironmentsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamEnvironmentsAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamEnvironmentsAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTeamEnvironmentsResponse) GetJSON200() *TeamEnvironmentsAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTeamEnvironmentsResponse) GetYAML200() *TeamEnvironmentsAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTeamEnvironmentsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTeamEnvironmentsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTeamEnvironmentsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTeamEnvironmentsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SetTeamEnvironmentsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamEnvironmentsAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamEnvironmentsAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r SetTeamEnvironmentsResponse) GetJSON200() *TeamEnvironmentsAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r SetTeamEnvironmentsResponse) GetYAML200() *TeamEnvironmentsAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r SetTeamEnvironmentsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SetTeamEnvironmentsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SetTeamEnvironmentsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SetTeamEnvironmentsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type AddTeamEnvironmentsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamEnvironmentsAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamEnvironmentsAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r AddTeamEnvironmentsResponse) GetJSON200() *TeamEnvironmentsAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r AddTeamEnvironmentsResponse) GetYAML200() *TeamEnvironmentsAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r AddTeamEnvironmentsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r AddTeamEnvironmentsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r AddTeamEnvironmentsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r AddTeamEnvironmentsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type RemoveTeamEnvironmentsResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamEnvironmentsAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamEnvironmentsAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r RemoveTeamEnvironmentsResponse) GetJSON200() *TeamEnvironmentsAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r RemoveTeamEnvironmentsResponse) GetYAML200() *TeamEnvironmentsAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r RemoveTeamEnvironmentsResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r RemoveTeamEnvironmentsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RemoveTeamEnvironmentsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r RemoveTeamEnvironmentsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTeamMembersResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamMembersAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamMembersAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r GetTeamMembersResponse) GetJSON200() *TeamMembersAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r GetTeamMembersResponse) GetYAML200() *TeamMembersAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r GetTeamMembersResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r GetTeamMembersResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTeamMembersResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTeamMembersResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type SetTeamMembersResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamMembersAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamMembersAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r SetTeamMembersResponse) GetJSON200() *TeamMembersAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r SetTeamMembersResponse) GetYAML200() *TeamMembersAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r SetTeamMembersResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SetTeamMembersResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SetTeamMembersResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SetTeamMembersResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type AddTeamMembersResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamMembersAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamMembersAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r AddTeamMembersResponse) GetJSON200() *TeamMembersAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r AddTeamMembersResponse) GetYAML200() *TeamMembersAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r AddTeamMembersResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r AddTeamMembersResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r AddTeamMembersResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r AddTeamMembersResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type RemoveTeamMembersResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *TeamMembersAO + // YAML200 the response for an HTTP 200 `application/yaml` response + YAML200 *TeamMembersAO +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r RemoveTeamMembersResponse) GetJSON200() *TeamMembersAO { + return r.JSON200 +} + +// GetYAML200 returns the response for an HTTP 200 `application/yaml` response +func (r RemoveTeamMembersResponse) GetYAML200() *TeamMembersAO { + return r.YAML200 +} + +// GetBody returns the raw response body bytes +func (r RemoveTeamMembersResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r RemoveTeamMembersResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RemoveTeamMembersResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r RemoveTeamMembersResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type InviteUserResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// GetBody returns the raw response body bytes +func (r InviteUserResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r InviteUserResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r InviteUserResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r InviteUserResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +// GetAccessTokensWithResponse Get access token list +// +// Deprecated, use v2 instead. Get a list of all access tokens. The access token itself is abbreviated for security reasons. Access tokens with v2 features are not returned, as they can not be represented cleanly in the old format. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/access-tokens (the `GetAccessTokens` operationId). +// +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *ClientWithResponses) GetAccessTokensWithResponse(ctx context.Context, params *GetAccessTokensParams, reqEditors ...RequestEditorFn) (*GetAccessTokensResponse, error) { + rsp, err := c.GetAccessTokens(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetAccessTokensResponse(rsp) +} + +// CreateAccessTokenWithBodyWithResponse Add a access token +// +// Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/access-tokens (the `CreateAccessToken` operationId). +// +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *ClientWithResponses) CreateAccessTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAccessTokenResponse, error) { + rsp, err := c.CreateAccessTokenWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateAccessTokenResponse(rsp) +} + +// CreateAccessTokenWithResponse Add a access token +// +// Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/access-tokens (the `CreateAccessToken` operationId). +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *ClientWithResponses) CreateAccessTokenWithResponse(ctx context.Context, body CreateAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateAccessTokenResponse, error) { + rsp, err := c.CreateAccessToken(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateAccessTokenResponse(rsp) +} + +// GetAccessTokens1WithResponse Get access token list +// +// Get a list of all access tokens. The access token itself is abbreviated for security reasons. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/access-tokens/v2 (the `GetAccessTokens1` operationId). +func (c *ClientWithResponses) GetAccessTokens1WithResponse(ctx context.Context, params *GetAccessTokens1Params, reqEditors ...RequestEditorFn) (*GetAccessTokens1Response, error) { + rsp, err := c.GetAccessTokens1(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetAccessTokens1Response(rsp) +} + +// CreateAccessToken1WithBodyWithResponse Create an access token +// +// Generate a new access token. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/access-tokens/v2 (the `CreateAccessToken1` operationId). +func (c *ClientWithResponses) CreateAccessToken1WithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateAccessToken1Response, error) { + rsp, err := c.CreateAccessToken1WithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateAccessToken1Response(rsp) +} + +// CreateAccessToken1WithResponse Create an access token +// +// Generate a new access token. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/access-tokens/v2 (the `CreateAccessToken1` operationId). +func (c *ClientWithResponses) CreateAccessToken1WithResponse(ctx context.Context, body CreateAccessToken1JSONRequestBody, reqEditors ...RequestEditorFn) (*CreateAccessToken1Response, error) { + rsp, err := c.CreateAccessToken1(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateAccessToken1Response(rsp) +} + +// DeleteAccessToken1WithResponse Delete access token +// +// Remove the access token. After that, the access token can't be used anymore. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/access-tokens/v2/{id} (the `DeleteAccessToken1` operationId). +func (c *ClientWithResponses) DeleteAccessToken1WithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*DeleteAccessToken1Response, error) { + rsp, err := c.DeleteAccessToken1(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteAccessToken1Response(rsp) +} + +// RecreateAccessTokenWithBodyWithResponse Recreate an access token +// +// Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/access-tokens/v2/{id}/recreate (the `RecreateAccessToken` operationId). +func (c *ClientWithResponses) RecreateAccessTokenWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RecreateAccessTokenResponse, error) { + rsp, err := c.RecreateAccessTokenWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRecreateAccessTokenResponse(rsp) +} + +// RecreateAccessTokenWithResponse Recreate an access token +// +// Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/access-tokens/v2/{id}/recreate (the `RecreateAccessToken` operationId). +func (c *ClientWithResponses) RecreateAccessTokenWithResponse(ctx context.Context, id string, body RecreateAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*RecreateAccessTokenResponse, error) { + rsp, err := c.RecreateAccessToken(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRecreateAccessTokenResponse(rsp) +} + +// DeleteAccessTokenWithResponse Delete access token +// +// Remove the access token associated. After that, the access token can't be used anymore for e.g. creating a new experiment or running an experiment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/access-tokens/{id} (the `DeleteAccessToken` operationId). +// +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *ClientWithResponses) DeleteAccessTokenWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*DeleteAccessTokenResponse, error) { + rsp, err := c.DeleteAccessToken(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteAccessTokenResponse(rsp) +} + +// FindAllActionsWithResponse Get all actions. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/actions (the `FindAllActions` operationId). +func (c *ClientWithResponses) FindAllActionsWithResponse(ctx context.Context, params *FindAllActionsParams, reqEditors ...RequestEditorFn) (*FindAllActionsResponse, error) { + rsp, err := c.FindAllActions(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseFindAllActionsResponse(rsp) +} + +// GetActionWithResponse Fetch a single action description +// +// Get action including their parameters. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/actions/{actionId} (the `GetAction` operationId). +func (c *ClientWithResponses) GetActionWithResponse(ctx context.Context, actionId string, reqEditors ...RequestEditorFn) (*GetActionResponse, error) { + rsp, err := c.GetAction(ctx, actionId, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetActionResponse(rsp) +} + +// GetTargetAdviceSummaryWithBodyWithResponse Get all currently active advice for a given environment and query. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/advice (the `GetTargetAdviceSummary` operationId). +func (c *ClientWithResponses) GetTargetAdviceSummaryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetTargetAdviceSummaryResponse, error) { + rsp, err := c.GetTargetAdviceSummaryWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTargetAdviceSummaryResponse(rsp) +} + +// GetTargetAdviceSummaryWithResponse Get all currently active advice for a given environment and query. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/advice (the `GetTargetAdviceSummary` operationId). +func (c *ClientWithResponses) GetTargetAdviceSummaryWithResponse(ctx context.Context, body GetTargetAdviceSummaryJSONRequestBody, reqEditors ...RequestEditorFn) (*GetTargetAdviceSummaryResponse, error) { + rsp, err := c.GetTargetAdviceSummary(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTargetAdviceSummaryResponse(rsp) +} + +// FindWithResponse Get all audit log entries +// +// Retrieve all audit logs in the given time-frame.
This endpoint requires an admin-token and can't be used with a team-based token. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/audit-log (the `Find` operationId). +func (c *ClientWithResponses) FindWithResponse(ctx context.Context, params *FindParams, reqEditors ...RequestEditorFn) (*FindResponse, error) { + rsp, err := c.Find(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseFindResponse(rsp) +} + +// ForwardToPlatformWithResponse Forward to Steadybit platform to either create an experiment associated to the `tag` or forward to the experiments linked already to the `tag` +// +// This endpoint can be used as a link for the badge of the `/api/badges/linked-badge.svg` API to either create a new experiment or show the linked experiments in Steadybit. This will help to link it correctly e.g. in your CMS-systems. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/badges/link (the `ForwardToPlatform` operationId). +func (c *ClientWithResponses) ForwardToPlatformWithResponse(ctx context.Context, params *ForwardToPlatformParams, reqEditors ...RequestEditorFn) (*ForwardToPlatformResponse, error) { + rsp, err := c.ForwardToPlatform(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseForwardToPlatformResponse(rsp) +} + +// GetLinkedBadgeWithResponse Get badge for create experiment or run status as SVG image +// +// Creates an image badge that is either for creating a new experiment linked to an `externalReference` or - if an experiment with the given `externalReference` already exists - a badge showing the run status of the experiment. The badge is return as SVG to integrate it nicely e.g. into your CMS-systems. You can use the `/api/badges/link` endpoint to link it appropriately +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/badges/linked-badge.svg (the `GetLinkedBadge` operationId). +func (c *ClientWithResponses) GetLinkedBadgeWithResponse(ctx context.Context, params *GetLinkedBadgeParams, reqEditors ...RequestEditorFn) (*GetLinkedBadgeResponse, error) { + rsp, err := c.GetLinkedBadge(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetLinkedBadgeResponse(rsp) +} + +// GetEnvironmentsWithResponse Fetch a list of all environments +// +// Get a list of all environments that exist. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/environments (the `GetEnvironments` operationId). +func (c *ClientWithResponses) GetEnvironmentsWithResponse(ctx context.Context, params *GetEnvironmentsParams, reqEditors ...RequestEditorFn) (*GetEnvironmentsResponse, error) { + rsp, err := c.GetEnvironments(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEnvironmentsResponse(rsp) +} + +// UpsertEnvironmentWithBodyWithResponse Create or update an environment +// +// Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/environments (the `UpsertEnvironment` operationId). +func (c *ClientWithResponses) UpsertEnvironmentWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertEnvironmentResponse, error) { + rsp, err := c.UpsertEnvironmentWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertEnvironmentResponse(rsp) +} + +// UpsertEnvironmentWithResponse Create or update an environment +// +// Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/environments (the `UpsertEnvironment` operationId). +func (c *ClientWithResponses) UpsertEnvironmentWithResponse(ctx context.Context, body UpsertEnvironmentJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertEnvironmentResponse, error) { + rsp, err := c.UpsertEnvironment(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertEnvironmentResponse(rsp) +} + +// DeleteEnvironmentWithResponse Delete environment +// +// Remove the given environment from the Steadybit platform. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/environments/{id} (the `DeleteEnvironment` operationId). +func (c *ClientWithResponses) DeleteEnvironmentWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteEnvironmentResponse, error) { + rsp, err := c.DeleteEnvironment(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteEnvironmentResponse(rsp) +} + +// GetEnvironmentWithResponse Fetch a single environment +// +// Get all details of a single existing environment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/environments/{id} (the `GetEnvironment` operationId). +func (c *ClientWithResponses) GetEnvironmentWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetEnvironmentResponse, error) { + rsp, err := c.GetEnvironment(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEnvironmentResponse(rsp) +} + +// GetEnvironmentVariablesWithResponse Get environment variables +// +// Get all environment variables associated to a single environment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/environments/{id}/variables (the `GetEnvironmentVariables` operationId). +func (c *ClientWithResponses) GetEnvironmentVariablesWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetEnvironmentVariablesResponse, error) { + rsp, err := c.GetEnvironmentVariables(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEnvironmentVariablesResponse(rsp) +} + +// SetEnvironmentVariablesWithBodyWithResponse Replace all environment variables +// +// All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/environments/{id}/variables (the `SetEnvironmentVariables` operationId). +func (c *ClientWithResponses) SetEnvironmentVariablesWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetEnvironmentVariablesResponse, error) { + rsp, err := c.SetEnvironmentVariablesWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetEnvironmentVariablesResponse(rsp) +} + +// SetEnvironmentVariablesWithResponse Replace all environment variables +// +// All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/environments/{id}/variables (the `SetEnvironmentVariables` operationId). +func (c *ClientWithResponses) SetEnvironmentVariablesWithResponse(ctx context.Context, id openapi_types.UUID, body SetEnvironmentVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*SetEnvironmentVariablesResponse, error) { + rsp, err := c.SetEnvironmentVariables(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetEnvironmentVariablesResponse(rsp) +} + +// UpdateEnvironmentVariablesWithBodyWithResponse Add / merge all environment variables +// +// All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/environments/{id}/variables (the `UpdateEnvironmentVariables` operationId). +func (c *ClientWithResponses) UpdateEnvironmentVariablesWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateEnvironmentVariablesResponse, error) { + rsp, err := c.UpdateEnvironmentVariablesWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateEnvironmentVariablesResponse(rsp) +} + +// UpdateEnvironmentVariablesWithResponse Add / merge all environment variables +// +// All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/environments/{id}/variables (the `UpdateEnvironmentVariables` operationId). +func (c *ClientWithResponses) UpdateEnvironmentVariablesWithResponse(ctx context.Context, id openapi_types.UUID, body UpdateEnvironmentVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateEnvironmentVariablesResponse, error) { + rsp, err := c.UpdateEnvironmentVariables(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateEnvironmentVariablesResponse(rsp) +} + +// GetExperimentsWithResponse Fetch a list of all experiments +// +// Get a list of all experiments that exist. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments (the `GetExperiments` operationId). +func (c *ClientWithResponses) GetExperimentsWithResponse(ctx context.Context, params *GetExperimentsParams, reqEditors ...RequestEditorFn) (*GetExperimentsResponse, error) { + rsp, err := c.GetExperiments(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentsResponse(rsp) +} + +// CreateOrUpdateExperimentWithBodyWithResponse Create or update an experiment +// +// Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments (the `CreateOrUpdateExperiment` operationId). +func (c *ClientWithResponses) CreateOrUpdateExperimentWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateOrUpdateExperimentResponse, error) { + rsp, err := c.CreateOrUpdateExperimentWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateOrUpdateExperimentResponse(rsp) +} + +// CreateOrUpdateExperimentWithResponse Create or update an experiment +// +// Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments (the `CreateOrUpdateExperiment` operationId). +func (c *ClientWithResponses) CreateOrUpdateExperimentWithResponse(ctx context.Context, body CreateOrUpdateExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateOrUpdateExperimentResponse, error) { + rsp, err := c.CreateOrUpdateExperiment(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateOrUpdateExperimentResponse(rsp) +} + +// SaveAndRunWithBodyWithResponse Save and run experiment +// +// Save the given experiment and immediately run it. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/execute (the `SaveAndRun` operationId). +func (c *ClientWithResponses) SaveAndRunWithBodyWithResponse(ctx context.Context, params *SaveAndRunParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SaveAndRunResponse, error) { + rsp, err := c.SaveAndRunWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSaveAndRunResponse(rsp) +} + +// SaveAndRunWithResponse Save and run experiment +// +// Save the given experiment and immediately run it. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/execute (the `SaveAndRun` operationId). +func (c *ClientWithResponses) SaveAndRunWithResponse(ctx context.Context, params *SaveAndRunParams, body SaveAndRunJSONRequestBody, reqEditors ...RequestEditorFn) (*SaveAndRunResponse, error) { + rsp, err := c.SaveAndRun(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSaveAndRunResponse(rsp) +} + +// GetExperimentExecutions1WithResponse Fetch a list of all experiment executions +// +// Get a list of all experiment executions that exist. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/executions (the `GetExperimentExecutions1` operationId). +// +// Deprecated: this operation has been marked as deprecated upstream, but no `x-deprecated-reason` was set +func (c *ClientWithResponses) GetExperimentExecutions1WithResponse(ctx context.Context, params *GetExperimentExecutions1Params, reqEditors ...RequestEditorFn) (*GetExperimentExecutions1Response, error) { + rsp, err := c.GetExperimentExecutions1(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentExecutions1Response(rsp) +} + +// GetExperimentExecutions2WithBodyWithResponse Fetch a list of experiment executions +// +// Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions (the `GetExperimentExecutions2` operationId). +func (c *ClientWithResponses) GetExperimentExecutions2WithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetExperimentExecutions2Response, error) { + rsp, err := c.GetExperimentExecutions2WithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentExecutions2Response(rsp) +} + +// GetExperimentExecutions2WithResponse Fetch a list of experiment executions +// +// Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions (the `GetExperimentExecutions2` operationId). +func (c *ClientWithResponses) GetExperimentExecutions2WithResponse(ctx context.Context, body GetExperimentExecutions2JSONRequestBody, reqEditors ...RequestEditorFn) (*GetExperimentExecutions2Response, error) { + rsp, err := c.GetExperimentExecutions2(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentExecutions2Response(rsp) +} + +// GetExperimentExecutionWithResponse Fetch a single experiment executions of a single experiment +// +// Get a single experiment execution that was performed for a specific experiment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/executions/{id} (the `GetExperimentExecution` operationId). +func (c *ClientWithResponses) GetExperimentExecutionWithResponse(ctx context.Context, id int64, params *GetExperimentExecutionParams, reqEditors ...RequestEditorFn) (*GetExperimentExecutionResponse, error) { + rsp, err := c.GetExperimentExecution(ctx, id, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentExecutionResponse(rsp) +} + +// GetArtifactWithResponse performs a GET /api/experiments/executions/{id}/artifacts/{targetExecutionId}/{artifactId} (the `GetArtifact` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) GetArtifactWithResponse(ctx context.Context, id int64, targetExecutionId string, artifactId string, reqEditors ...RequestEditorFn) (*GetArtifactResponse, error) { + rsp, err := c.GetArtifact(ctx, id, targetExecutionId, artifactId, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetArtifactResponse(rsp) +} + +// CancelExperimentExecutionWithResponse Cancel a running experiment execution of a single experiment +// +// Cancels a currently running experiment execution to be stopped as soon as possible. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions/{id}/cancel (the `CancelExperimentExecution` operationId). +func (c *ClientWithResponses) CancelExperimentExecutionWithResponse(ctx context.Context, id int64, reqEditors ...RequestEditorFn) (*CancelExperimentExecutionResponse, error) { + rsp, err := c.CancelExperimentExecution(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseCancelExperimentExecutionResponse(rsp) +} + +// UpdateExecutionPropertiesWithBodyWithResponse Update properties of an experiment execution +// +// Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions/{id}/properties (the `UpdateExecutionProperties` operationId). +func (c *ClientWithResponses) UpdateExecutionPropertiesWithBodyWithResponse(ctx context.Context, id int64, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateExecutionPropertiesResponse, error) { + rsp, err := c.UpdateExecutionPropertiesWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateExecutionPropertiesResponse(rsp) +} + +// UpdateExecutionPropertiesWithResponse Update properties of an experiment execution +// +// Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions/{id}/properties (the `UpdateExecutionProperties` operationId). +func (c *ClientWithResponses) UpdateExecutionPropertiesWithResponse(ctx context.Context, id int64, body UpdateExecutionPropertiesJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateExecutionPropertiesResponse, error) { + rsp, err := c.UpdateExecutionProperties(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateExecutionPropertiesResponse(rsp) +} + +// AddExecutionPropertyValueWithBodyWithResponse Add a single value to a list property of an experiment execution. +// +// This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions/{id}/properties/{key}/add (the `AddExecutionPropertyValue` operationId). +func (c *ClientWithResponses) AddExecutionPropertyValueWithBodyWithResponse(ctx context.Context, id int64, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AddExecutionPropertyValueResponse, error) { + rsp, err := c.AddExecutionPropertyValueWithBody(ctx, id, key, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseAddExecutionPropertyValueResponse(rsp) +} + +// AddExecutionPropertyValueWithResponse Add a single value to a list property of an experiment execution. +// +// This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions/{id}/properties/{key}/add (the `AddExecutionPropertyValue` operationId). +func (c *ClientWithResponses) AddExecutionPropertyValueWithResponse(ctx context.Context, id int64, key string, body AddExecutionPropertyValueJSONRequestBody, reqEditors ...RequestEditorFn) (*AddExecutionPropertyValueResponse, error) { + rsp, err := c.AddExecutionPropertyValue(ctx, id, key, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseAddExecutionPropertyValueResponse(rsp) +} + +// SetExecutionPropertyValueWithBodyWithResponse Set the value of a property of an experiment execution. +// +// Only properties with `editableInExecution` set to `true` can be modified. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions/{id}/properties/{key}/set (the `SetExecutionPropertyValue` operationId). +func (c *ClientWithResponses) SetExecutionPropertyValueWithBodyWithResponse(ctx context.Context, id int64, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetExecutionPropertyValueResponse, error) { + rsp, err := c.SetExecutionPropertyValueWithBody(ctx, id, key, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetExecutionPropertyValueResponse(rsp) +} + +// SetExecutionPropertyValueWithResponse Set the value of a property of an experiment execution. +// +// Only properties with `editableInExecution` set to `true` can be modified. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/executions/{id}/properties/{key}/set (the `SetExecutionPropertyValue` operationId). +func (c *ClientWithResponses) SetExecutionPropertyValueWithResponse(ctx context.Context, id int64, key string, body SetExecutionPropertyValueJSONRequestBody, reqEditors ...RequestEditorFn) (*SetExecutionPropertyValueResponse, error) { + rsp, err := c.SetExecutionPropertyValue(ctx, id, key, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetExecutionPropertyValueResponse(rsp) +} + +// UpsertScheduleWithBodyWithResponse Create or update an experiment schedule +// +// Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/schedules (the `UpsertSchedule` operationId). +func (c *ClientWithResponses) UpsertScheduleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertScheduleResponse, error) { + rsp, err := c.UpsertScheduleWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertScheduleResponse(rsp) +} + +// UpsertScheduleWithResponse Create or update an experiment schedule +// +// Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/schedules (the `UpsertSchedule` operationId). +func (c *ClientWithResponses) UpsertScheduleWithResponse(ctx context.Context, body UpsertScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertScheduleResponse, error) { + rsp, err := c.UpsertSchedule(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertScheduleResponse(rsp) +} + +// GetAllSchedulesV2WithResponse Get all current experiment schedule configurations +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/schedules/v2 (the `GetAllSchedulesV2` operationId). +func (c *ClientWithResponses) GetAllSchedulesV2WithResponse(ctx context.Context, params *GetAllSchedulesV2Params, reqEditors ...RequestEditorFn) (*GetAllSchedulesV2Response, error) { + rsp, err := c.GetAllSchedulesV2(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetAllSchedulesV2Response(rsp) +} + +// RemoveExperimentScheduleByIdWithResponse Remove an existing experiment schedule +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/experiments/schedules/{id} (the `RemoveExperimentScheduleById` operationId). +func (c *ClientWithResponses) RemoveExperimentScheduleByIdWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*RemoveExperimentScheduleByIdResponse, error) { + rsp, err := c.RemoveExperimentScheduleById(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseRemoveExperimentScheduleByIdResponse(rsp) +} + +// GetSchedulesWithResponse Get experiment schedules for a specific experiment schedule id +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/schedules/{id} (the `GetSchedules` operationId). +func (c *ClientWithResponses) GetSchedulesWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*GetSchedulesResponse, error) { + rsp, err := c.GetSchedules(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetSchedulesResponse(rsp) +} + +// PatchScheduleWithBodyWithResponse Partially update an experiment schedule +// +// Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PATCH /api/experiments/schedules/{id} (the `PatchSchedule` operationId). +func (c *ClientWithResponses) PatchScheduleWithBodyWithResponse(ctx context.Context, id string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchScheduleResponse, error) { + rsp, err := c.PatchScheduleWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchScheduleResponse(rsp) +} + +// PatchScheduleWithResponse Partially update an experiment schedule +// +// Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PATCH /api/experiments/schedules/{id} (the `PatchSchedule` operationId). +func (c *ClientWithResponses) PatchScheduleWithResponse(ctx context.Context, id string, body PatchScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchScheduleResponse, error) { + rsp, err := c.PatchSchedule(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchScheduleResponse(rsp) +} + +// GetExperimentTemplatesWithResponse Fetch a list of all templates +// +// Get a list of all templates that exist. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/templates (the `GetExperimentTemplates` operationId). +func (c *ClientWithResponses) GetExperimentTemplatesWithResponse(ctx context.Context, params *GetExperimentTemplatesParams, reqEditors ...RequestEditorFn) (*GetExperimentTemplatesResponse, error) { + rsp, err := c.GetExperimentTemplates(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentTemplatesResponse(rsp) +} + +// UpsertExperimentTemplateWithBodyWithResponse Create or update an experiment template +// +// Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates (the `UpsertExperimentTemplate` operationId). +func (c *ClientWithResponses) UpsertExperimentTemplateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertExperimentTemplateResponse, error) { + rsp, err := c.UpsertExperimentTemplateWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertExperimentTemplateResponse(rsp) +} + +// UpsertExperimentTemplateWithResponse Create or update an experiment template +// +// Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates (the `UpsertExperimentTemplate` operationId). +func (c *ClientWithResponses) UpsertExperimentTemplateWithResponse(ctx context.Context, body UpsertExperimentTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertExperimentTemplateResponse, error) { + rsp, err := c.UpsertExperimentTemplate(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertExperimentTemplateResponse(rsp) +} + +// ImportFromHubWithBodyWithResponse Import experiment templates +// +// Import experiment templates with given IDs from linked hub. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates/imports (the `ImportFromHub` operationId). +func (c *ClientWithResponses) ImportFromHubWithBodyWithResponse(ctx context.Context, params *ImportFromHubParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ImportFromHubResponse, error) { + rsp, err := c.ImportFromHubWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseImportFromHubResponse(rsp) +} + +// ImportFromHubWithResponse Import experiment templates +// +// Import experiment templates with given IDs from linked hub. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates/imports (the `ImportFromHub` operationId). +func (c *ClientWithResponses) ImportFromHubWithResponse(ctx context.Context, params *ImportFromHubParams, body ImportFromHubJSONRequestBody, reqEditors ...RequestEditorFn) (*ImportFromHubResponse, error) { + rsp, err := c.ImportFromHub(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseImportFromHubResponse(rsp) +} + +// DeleteExperimentTemplateWithResponse Delete experiment template +// +// Remove the given experiment template from the Steadybit platform. If this template is used in a service profile, it will be removed from the profile and all provided service experiments will get deleted. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/experiments/templates/{id} (the `DeleteExperimentTemplate` operationId). +func (c *ClientWithResponses) DeleteExperimentTemplateWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteExperimentTemplateResponse, error) { + rsp, err := c.DeleteExperimentTemplate(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteExperimentTemplateResponse(rsp) +} + +// GetExperimentTemplateWithResponse Fetch a single experiment template +// +// Get all details of a single existing experiment template. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/templates/{id} (the `GetExperimentTemplate` operationId). +func (c *ClientWithResponses) GetExperimentTemplateWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetExperimentTemplateResponse, error) { + rsp, err := c.GetExperimentTemplate(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentTemplateResponse(rsp) +} + +// CreateExperimentByTemplateWithBodyWithResponse Create an experiment based on an experiment template +// +// Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-create (the `CreateExperimentByTemplate` operationId). +func (c *ClientWithResponses) CreateExperimentByTemplateWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, params *CreateExperimentByTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateExperimentByTemplateResponse, error) { + rsp, err := c.CreateExperimentByTemplateWithBody(ctx, id, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateExperimentByTemplateResponse(rsp) +} + +// CreateExperimentByTemplateWithResponse Create an experiment based on an experiment template +// +// Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-create (the `CreateExperimentByTemplate` operationId). +func (c *ClientWithResponses) CreateExperimentByTemplateWithResponse(ctx context.Context, id openapi_types.UUID, params *CreateExperimentByTemplateParams, body CreateExperimentByTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateExperimentByTemplateResponse, error) { + rsp, err := c.CreateExperimentByTemplate(ctx, id, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateExperimentByTemplateResponse(rsp) +} + +// SaveAndRunFromTemplateWithBodyWithResponse Create an experiment based on an experiment template and run experiment +// +// Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-execute (the `SaveAndRunFromTemplate` operationId). +func (c *ClientWithResponses) SaveAndRunFromTemplateWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SaveAndRunFromTemplateResponse, error) { + rsp, err := c.SaveAndRunFromTemplateWithBody(ctx, id, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSaveAndRunFromTemplateResponse(rsp) +} + +// SaveAndRunFromTemplateWithResponse Create an experiment based on an experiment template and run experiment +// +// Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-execute (the `SaveAndRunFromTemplate` operationId). +func (c *ClientWithResponses) SaveAndRunFromTemplateWithResponse(ctx context.Context, id openapi_types.UUID, params *SaveAndRunFromTemplateParams, body SaveAndRunFromTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*SaveAndRunFromTemplateResponse, error) { + rsp, err := c.SaveAndRunFromTemplate(ctx, id, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSaveAndRunFromTemplateResponse(rsp) +} + +// UpdateExperimentByTemplateWithBodyWithResponse Update an existing experiment based on a template +// +// Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-update/{key} (the `UpdateExperimentByTemplate` operationId). +func (c *ClientWithResponses) UpdateExperimentByTemplateWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateExperimentByTemplateResponse, error) { + rsp, err := c.UpdateExperimentByTemplateWithBody(ctx, id, key, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateExperimentByTemplateResponse(rsp) +} + +// UpdateExperimentByTemplateWithResponse Update an existing experiment based on a template +// +// Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/templates/{id}/experiment-update/{key} (the `UpdateExperimentByTemplate` operationId). +func (c *ClientWithResponses) UpdateExperimentByTemplateWithResponse(ctx context.Context, id openapi_types.UUID, key string, params *UpdateExperimentByTemplateParams, body UpdateExperimentByTemplateJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateExperimentByTemplateResponse, error) { + rsp, err := c.UpdateExperimentByTemplate(ctx, id, key, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateExperimentByTemplateResponse(rsp) +} + +// DeleteExperimentWithResponse Delete experiment +// +// Remove the given experiment. The associated number is still reserved afterwards and will not be reused. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/experiments/{key} (the `DeleteExperiment` operationId). +func (c *ClientWithResponses) DeleteExperimentWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*DeleteExperimentResponse, error) { + rsp, err := c.DeleteExperiment(ctx, key, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteExperimentResponse(rsp) +} + +// GetExperimentWithResponse Fetch a single experiment +// +// Get all details of a single existing experiment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/{key} (the `GetExperiment` operationId). +func (c *ClientWithResponses) GetExperimentWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetExperimentResponse, error) { + rsp, err := c.GetExperiment(ctx, key, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentResponse(rsp) +} + +// UpdateExperimentWithBodyWithResponse Update an experiment +// +// Update the experiment identified by the experiment `key`. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/{key} (the `UpdateExperiment` operationId). +func (c *ClientWithResponses) UpdateExperimentWithBodyWithResponse(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateExperimentResponse, error) { + rsp, err := c.UpdateExperimentWithBody(ctx, key, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateExperimentResponse(rsp) +} + +// UpdateExperimentWithResponse Update an experiment +// +// Update the experiment identified by the experiment `key`. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/{key} (the `UpdateExperiment` operationId). +func (c *ClientWithResponses) UpdateExperimentWithResponse(ctx context.Context, key string, body UpdateExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateExperimentResponse, error) { + rsp, err := c.UpdateExperiment(ctx, key, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateExperimentResponse(rsp) +} + +// GetExperimentBadgeWithResponse Get experiment run status as SVG image +// +// Get the status of the latest experiment run of the associated experiment as SVG to integrate it nicely e.g. into your CMS-systems. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/{key}/badge.svg (the `GetExperimentBadge` operationId). +func (c *ClientWithResponses) GetExperimentBadgeWithResponse(ctx context.Context, key string, params *GetExperimentBadgeParams, reqEditors ...RequestEditorFn) (*GetExperimentBadgeResponse, error) { + rsp, err := c.GetExperimentBadge(ctx, key, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentBadgeResponse(rsp) +} + +// ExecuteExperimentWithBodyWithResponse Execute an experiment +// +// Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. +// +// Examples: +// - Override environment from the experiment for a single run: +// ``` +// { +// "environment": "Shop Stage" +// } +// ``` +// - Override the variables for a single execution: +// ``` +// { +// "variables": { +// "httpEndpoint": "http://dev.shop.products.internal" +// } +// } +// ``` +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/{key}/execute (the `ExecuteExperiment` operationId). +func (c *ClientWithResponses) ExecuteExperimentWithBodyWithResponse(ctx context.Context, key string, params *ExecuteExperimentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ExecuteExperimentResponse, error) { + rsp, err := c.ExecuteExperimentWithBody(ctx, key, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseExecuteExperimentResponse(rsp) +} + +// ExecuteExperimentWithResponse Execute an experiment +// +// Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. +// +// Examples: +// - Override environment from the experiment for a single run: +// ``` +// { +// "environment": "Shop Stage" +// } +// ``` +// - Override the variables for a single execution: +// ``` +// { +// "variables": { +// "httpEndpoint": "http://dev.shop.products.internal" +// } +// } +// ``` +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/experiments/{key}/execute (the `ExecuteExperiment` operationId). +func (c *ClientWithResponses) ExecuteExperimentWithResponse(ctx context.Context, key string, params *ExecuteExperimentParams, body ExecuteExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*ExecuteExperimentResponse, error) { + rsp, err := c.ExecuteExperiment(ctx, key, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseExecuteExperimentResponse(rsp) +} + +// GetExperimentExecutions3WithResponse Fetch a list of all experiment executions of a single experiment +// +// Get a list of all experiment executions that were performed for a specific experiment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/experiments/{key}/executions (the `GetExperimentExecutions3` operationId). +func (c *ClientWithResponses) GetExperimentExecutions3WithResponse(ctx context.Context, key string, params *GetExperimentExecutions3Params, reqEditors ...RequestEditorFn) (*GetExperimentExecutions3Response, error) { + rsp, err := c.GetExperimentExecutions3(ctx, key, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentExecutions3Response(rsp) +} + +// GetLandscapeViewsWithResponse Fetch all saved landscape views of a team +// +// Get a list of all saved explorer landscape views that belong to the given team. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/explore/landscape/views (the `GetLandscapeViews` operationId). +func (c *ClientWithResponses) GetLandscapeViewsWithResponse(ctx context.Context, params *GetLandscapeViewsParams, reqEditors ...RequestEditorFn) (*GetLandscapeViewsResponse, error) { + rsp, err := c.GetLandscapeViews(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetLandscapeViewsResponse(rsp) +} + +// CreateLandscapeViewWithBodyWithResponse Create a saved landscape view +// +// Create a new saved explorer landscape view for a team. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/explore/landscape/views (the `CreateLandscapeView` operationId). +func (c *ClientWithResponses) CreateLandscapeViewWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateLandscapeViewResponse, error) { + rsp, err := c.CreateLandscapeViewWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateLandscapeViewResponse(rsp) +} + +// CreateLandscapeViewWithResponse Create a saved landscape view +// +// Create a new saved explorer landscape view for a team. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/explore/landscape/views (the `CreateLandscapeView` operationId). +func (c *ClientWithResponses) CreateLandscapeViewWithResponse(ctx context.Context, body CreateLandscapeViewJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateLandscapeViewResponse, error) { + rsp, err := c.CreateLandscapeView(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateLandscapeViewResponse(rsp) +} + +// DeleteLandscapeViewWithResponse Delete a saved landscape view +// +// Remove the given saved explorer landscape view from the Steadybit platform. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/explore/landscape/views/{id} (the `DeleteLandscapeView` operationId). +func (c *ClientWithResponses) DeleteLandscapeViewWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteLandscapeViewResponse, error) { + rsp, err := c.DeleteLandscapeView(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteLandscapeViewResponse(rsp) +} + +// GetLandscapeViewWithResponse Fetch a single saved landscape view +// +// Get all details of a single saved explorer landscape view. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/explore/landscape/views/{id} (the `GetLandscapeView` operationId). +func (c *ClientWithResponses) GetLandscapeViewWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetLandscapeViewResponse, error) { + rsp, err := c.GetLandscapeView(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetLandscapeViewResponse(rsp) +} + +// UpdateLandscapeViewWithBodyWithResponse Update a saved landscape view +// +// Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/explore/landscape/views/{id} (the `UpdateLandscapeView` operationId). +func (c *ClientWithResponses) UpdateLandscapeViewWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateLandscapeViewResponse, error) { + rsp, err := c.UpdateLandscapeViewWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateLandscapeViewResponse(rsp) +} + +// UpdateLandscapeViewWithResponse Update a saved landscape view +// +// Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/explore/landscape/views/{id} (the `UpdateLandscapeView` operationId). +func (c *ClientWithResponses) UpdateLandscapeViewWithResponse(ctx context.Context, id openapi_types.UUID, body UpdateLandscapeViewJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateLandscapeViewResponse, error) { + rsp, err := c.UpdateLandscapeView(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateLandscapeViewResponse(rsp) +} + +// HealthWithResponse performs a GET /api/health (the `Health` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) HealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HealthResponse, error) { + rsp, err := c.Health(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseHealthResponse(rsp) +} + +// LivenessWithResponse performs a GET /api/health/liveness (the `Liveness` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) LivenessWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*LivenessResponse, error) { + rsp, err := c.Liveness(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseLivenessResponse(rsp) +} + +// ReadinessWithResponse performs a GET /api/health/readiness (the `Readiness` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) ReadinessWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ReadinessResponse, error) { + rsp, err := c.Readiness(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseReadinessResponse(rsp) +} + +// GetHubsWithResponse Fetch a list of all hubs +// +// Get a list of all hubs that are currently connected. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/hubs (the `GetHubs` operationId). +func (c *ClientWithResponses) GetHubsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHubsResponse, error) { + rsp, err := c.GetHubs(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetHubsResponse(rsp) +} + +// UpsertHubWithBodyWithResponse Create or update a hub +// +// Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/hubs (the `UpsertHub` operationId). +func (c *ClientWithResponses) UpsertHubWithBodyWithResponse(ctx context.Context, params *UpsertHubParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertHubResponse, error) { + rsp, err := c.UpsertHubWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertHubResponse(rsp) +} + +// UpsertHubWithResponse Create or update a hub +// +// Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/hubs (the `UpsertHub` operationId). +func (c *ClientWithResponses) UpsertHubWithResponse(ctx context.Context, params *UpsertHubParams, body UpsertHubJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertHubResponse, error) { + rsp, err := c.UpsertHub(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertHubResponse(rsp) +} + +// ConnectionCheckWithBodyWithResponse Check a hub connection +// +// Check if the given hub connection details point to a valid hub. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/hubs/connection-check (the `ConnectionCheck` operationId). +func (c *ClientWithResponses) ConnectionCheckWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ConnectionCheckResponse, error) { + rsp, err := c.ConnectionCheckWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseConnectionCheckResponse(rsp) +} + +// ConnectionCheckWithResponse Check a hub connection +// +// Check if the given hub connection details point to a valid hub. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/hubs/connection-check (the `ConnectionCheck` operationId). +func (c *ClientWithResponses) ConnectionCheckWithResponse(ctx context.Context, body ConnectionCheckJSONRequestBody, reqEditors ...RequestEditorFn) (*ConnectionCheckResponse, error) { + rsp, err := c.ConnectionCheck(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseConnectionCheckResponse(rsp) +} + +// DeleteHubWithResponse Delete a hub +// +// Remove the given hub. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/hubs/{id} (the `DeleteHub` operationId). +func (c *ClientWithResponses) DeleteHubWithResponse(ctx context.Context, id openapi_types.UUID, params *DeleteHubParams, reqEditors ...RequestEditorFn) (*DeleteHubResponse, error) { + rsp, err := c.DeleteHub(ctx, id, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteHubResponse(rsp) +} + +// GetHubByIdWithResponse Fetch a single hub +// +// Get all details of a single hub. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/hubs/{id} (the `GetHubById` operationId). +func (c *ClientWithResponses) GetHubByIdWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetHubByIdResponse, error) { + rsp, err := c.GetHubById(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetHubByIdResponse(rsp) +} + +// ResyncHubWithResponse Re-synchronize a hub +// +// Fetch the latest hub definition based on `hubRepository`. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/hubs/{id}/resync (the `ResyncHub` operationId). +func (c *ClientWithResponses) ResyncHubWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*ResyncHubResponse, error) { + rsp, err := c.ResyncHub(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseResyncHubResponse(rsp) +} + +// GetPreflightWebhooksWithResponse Fetch a list of preflight webhooks +// +// Get a list of all existing preflight webhooks. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/integrations/preflight (the `GetPreflightWebhooks` operationId). +func (c *ClientWithResponses) GetPreflightWebhooksWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetPreflightWebhooksResponse, error) { + rsp, err := c.GetPreflightWebhooks(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetPreflightWebhooksResponse(rsp) +} + +// UpsertPreflightWebhookWithBodyWithResponse Create or update a preflight webhook +// +// Insert or update a preflight webhook.
Experiment runs that were not executed due to engaged / active kill switch will not be automatically executed, they need to be triggered again. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/killswitch (the `DisengageKillswitch` operationId). +func (c *ClientWithResponses) DisengageKillswitchWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*DisengageKillswitchResponse, error) { + rsp, err := c.DisengageKillswitch(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseDisengageKillswitchResponse(rsp) +} + +// GetKillswitchWithResponse Get the current status of the kill switch +// +// Determines the current status of the kill switch without changing it. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/killswitch (the `GetKillswitch` operationId). +func (c *ClientWithResponses) GetKillswitchWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetKillswitchResponse, error) { + rsp, err := c.GetKillswitch(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetKillswitchResponse(rsp) +} + +// EngageKillswitchWithResponse Activate / engage the kill switch +// +// Activates / engages the kill switch to cancel all experiments running at the moment and prevent execution of new experiments until the kill switch is disengaged / deactivated again. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/killswitch (the `EngageKillswitch` operationId). +func (c *ClientWithResponses) EngageKillswitchWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*EngageKillswitchResponse, error) { + rsp, err := c.EngageKillswitch(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseEngageKillswitchResponse(rsp) +} + +// GetLicenseSummaryWithResponse Get license summary. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/license (the `GetLicenseSummary` operationId). +func (c *ClientWithResponses) GetLicenseSummaryWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetLicenseSummaryResponse, error) { + rsp, err := c.GetLicenseSummary(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetLicenseSummaryResponse(rsp) +} + +// GetReportWithResponse Get license report. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/license/report (the `GetReport` operationId). +func (c *ClientWithResponses) GetReportWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetReportResponse, error) { + rsp, err := c.GetReport(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetReportResponse(rsp) +} + +// GetPreflightActionSummaryWithResponse Get all preflight actions. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/preflight/actions (the `GetPreflightActionSummary` operationId). +func (c *ClientWithResponses) GetPreflightActionSummaryWithResponse(ctx context.Context, params *GetPreflightActionSummaryParams, reqEditors ...RequestEditorFn) (*GetPreflightActionSummaryResponse, error) { + rsp, err := c.GetPreflightActionSummary(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetPreflightActionSummaryResponse(rsp) +} + +// GetAssociationsWithResponse Get all current associations. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/properties/associations (the `GetAssociations` operationId). +func (c *ClientWithResponses) GetAssociationsWithResponse(ctx context.Context, params *GetAssociationsParams, reqEditors ...RequestEditorFn) (*GetAssociationsResponse, error) { + rsp, err := c.GetAssociations(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetAssociationsResponse(rsp) +} + +// UpsertPropertyAssociationWithBodyWithResponse Create or update a property association +// +// Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. +// +// Examples: +// - Assign the property `RESULT_COLOR` to all experiment designs: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": false, +// "required": true +// } +// ``` +// - Assign the property `RESULT_COLOR` to the design ADM-15: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": false, +// "experimentKey": "ADM-15", +// "required": true +// } +// ``` +// - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": true, +// "experimentKey": "ADM-15", +// "required": false +// } +// ``` +// - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: +// ``` +// { +// "key": "RESULT_COLOR", +// "associationType": "SERVICE", +// "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", +// "required": false +// } +// ``` +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/properties/associations (the `UpsertPropertyAssociation` operationId). +func (c *ClientWithResponses) UpsertPropertyAssociationWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertPropertyAssociationResponse, error) { + rsp, err := c.UpsertPropertyAssociationWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertPropertyAssociationResponse(rsp) +} + +// UpsertPropertyAssociationWithResponse Create or update a property association +// +// Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. +// +// Examples: +// - Assign the property `RESULT_COLOR` to all experiment designs: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": false, +// "required": true +// } +// ``` +// - Assign the property `RESULT_COLOR` to the design ADM-15: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": false, +// "experimentKey": "ADM-15", +// "required": true +// } +// ``` +// - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: +// ``` +// { +// "key": "RESULT_COLOR", +// "editableInExecution": true, +// "experimentKey": "ADM-15", +// "required": false +// } +// ``` +// - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: +// ``` +// { +// "key": "RESULT_COLOR", +// "associationType": "SERVICE", +// "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", +// "required": false +// } +// ``` +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/properties/associations (the `UpsertPropertyAssociation` operationId). +func (c *ClientWithResponses) UpsertPropertyAssociationWithResponse(ctx context.Context, body UpsertPropertyAssociationJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertPropertyAssociationResponse, error) { + rsp, err := c.UpsertPropertyAssociation(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertPropertyAssociationResponse(rsp) +} + +// DeletePropertyAssociationWithResponse Remove an existing property association. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/properties/associations/{id} (the `DeletePropertyAssociation` operationId). +func (c *ClientWithResponses) DeletePropertyAssociationWithResponse(ctx context.Context, id openapi_types.UUID, params *DeletePropertyAssociationParams, reqEditors ...RequestEditorFn) (*DeletePropertyAssociationResponse, error) { + rsp, err := c.DeletePropertyAssociation(ctx, id, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeletePropertyAssociationResponse(rsp) +} + +// GetPropertyDefinition1WithResponse Get property association by a given id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/properties/associations/{id} (the `GetPropertyDefinition1` operationId). +func (c *ClientWithResponses) GetPropertyDefinition1WithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetPropertyDefinition1Response, error) { + rsp, err := c.GetPropertyDefinition1(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetPropertyDefinition1Response(rsp) +} + +// GetPropertyDefinitionsWithResponse performs a GET /api/properties/definitions (the `GetPropertyDefinitions` operationId) request. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) GetPropertyDefinitionsWithResponse(ctx context.Context, params *GetPropertyDefinitionsParams, reqEditors ...RequestEditorFn) (*GetPropertyDefinitionsResponse, error) { + rsp, err := c.GetPropertyDefinitions(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetPropertyDefinitionsResponse(rsp) +} + +// UpsertPropertyDefinitionWithBodyWithResponse Create or update property definition +// +// Insert or update the property definition specified by the given `key`. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/properties/definitions (the `UpsertPropertyDefinition` operationId). +func (c *ClientWithResponses) UpsertPropertyDefinitionWithBodyWithResponse(ctx context.Context, params *UpsertPropertyDefinitionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertPropertyDefinitionResponse, error) { + rsp, err := c.UpsertPropertyDefinitionWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertPropertyDefinitionResponse(rsp) +} + +// UpsertPropertyDefinitionWithResponse Create or update property definition +// +// Insert or update the property definition specified by the given `key`. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/properties/definitions (the `UpsertPropertyDefinition` operationId). +func (c *ClientWithResponses) UpsertPropertyDefinitionWithResponse(ctx context.Context, params *UpsertPropertyDefinitionParams, body UpsertPropertyDefinitionJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertPropertyDefinitionResponse, error) { + rsp, err := c.UpsertPropertyDefinition(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertPropertyDefinitionResponse(rsp) +} + +// DeletePropertyDefinitionWithResponse Remove an existing property definition +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/properties/definitions/{key} (the `DeletePropertyDefinition` operationId). +func (c *ClientWithResponses) DeletePropertyDefinitionWithResponse(ctx context.Context, key string, params *DeletePropertyDefinitionParams, reqEditors ...RequestEditorFn) (*DeletePropertyDefinitionResponse, error) { + rsp, err := c.DeletePropertyDefinition(ctx, key, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeletePropertyDefinitionResponse(rsp) +} + +// GetPropertyDefinitionWithResponse Get property definition for a specific property definition key. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/properties/definitions/{key} (the `GetPropertyDefinition` operationId). +func (c *ClientWithResponses) GetPropertyDefinitionWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetPropertyDefinitionResponse, error) { + rsp, err := c.GetPropertyDefinition(ctx, key, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetPropertyDefinitionResponse(rsp) +} + +// GetEnvironmentCountsWithBodyWithResponse Get environment counts over time +// +// Returns the number of environments in the tenant aggregated into time buckets. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/environments (the `GetEnvironmentCounts` operationId). +func (c *ClientWithResponses) GetEnvironmentCountsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetEnvironmentCountsResponse, error) { + rsp, err := c.GetEnvironmentCountsWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEnvironmentCountsResponse(rsp) +} + +// GetEnvironmentCountsWithResponse Get environment counts over time +// +// Returns the number of environments in the tenant aggregated into time buckets. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/environments (the `GetEnvironmentCounts` operationId). +func (c *ClientWithResponses) GetEnvironmentCountsWithResponse(ctx context.Context, body GetEnvironmentCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetEnvironmentCountsResponse, error) { + rsp, err := c.GetEnvironmentCounts(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEnvironmentCountsResponse(rsp) +} + +// GetExperimentCreationsWithBodyWithResponse Get experiment creation counts over time +// +// Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/experiments/created (the `GetExperimentCreations` operationId). +func (c *ClientWithResponses) GetExperimentCreationsWithBodyWithResponse(ctx context.Context, params *GetExperimentCreationsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetExperimentCreationsResponse, error) { + rsp, err := c.GetExperimentCreationsWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentCreationsResponse(rsp) +} + +// GetExperimentCreationsWithResponse Get experiment creation counts over time +// +// Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/experiments/created (the `GetExperimentCreations` operationId). +func (c *ClientWithResponses) GetExperimentCreationsWithResponse(ctx context.Context, params *GetExperimentCreationsParams, body GetExperimentCreationsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetExperimentCreationsResponse, error) { + rsp, err := c.GetExperimentCreations(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentCreationsResponse(rsp) +} + +// GetExperimentExecutionsWithBodyWithResponse Get experiment execution counts over time +// +// Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/experiments/executed (the `GetExperimentExecutions` operationId). +func (c *ClientWithResponses) GetExperimentExecutionsWithBodyWithResponse(ctx context.Context, params *GetExperimentExecutionsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetExperimentExecutionsResponse, error) { + rsp, err := c.GetExperimentExecutionsWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentExecutionsResponse(rsp) +} + +// GetExperimentExecutionsWithResponse Get experiment execution counts over time +// +// Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/experiments/executed (the `GetExperimentExecutions` operationId). +func (c *ClientWithResponses) GetExperimentExecutionsWithResponse(ctx context.Context, params *GetExperimentExecutionsParams, body GetExperimentExecutionsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetExperimentExecutionsResponse, error) { + rsp, err := c.GetExperimentExecutions(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetExperimentExecutionsResponse(rsp) +} + +// GetAverageRiskWithBodyWithResponse Get average service risk over time +// +// Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/services/average (the `GetAverageRisk` operationId). +func (c *ClientWithResponses) GetAverageRiskWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetAverageRiskResponse, error) { + rsp, err := c.GetAverageRiskWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetAverageRiskResponse(rsp) +} + +// GetAverageRiskWithResponse Get average service risk over time +// +// Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/services/average (the `GetAverageRisk` operationId). +func (c *ClientWithResponses) GetAverageRiskWithResponse(ctx context.Context, body GetAverageRiskJSONRequestBody, reqEditors ...RequestEditorFn) (*GetAverageRiskResponse, error) { + rsp, err := c.GetAverageRisk(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetAverageRiskResponse(rsp) +} + +// GetRiskByCategoryWithBodyWithResponse Get average service risk grouped by category over time +// +// Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/services/by-category (the `GetRiskByCategory` operationId). +func (c *ClientWithResponses) GetRiskByCategoryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetRiskByCategoryResponse, error) { + rsp, err := c.GetRiskByCategoryWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetRiskByCategoryResponse(rsp) +} + +// GetRiskByCategoryWithResponse Get average service risk grouped by category over time +// +// Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/services/by-category (the `GetRiskByCategory` operationId). +func (c *ClientWithResponses) GetRiskByCategoryWithResponse(ctx context.Context, body GetRiskByCategoryJSONRequestBody, reqEditors ...RequestEditorFn) (*GetRiskByCategoryResponse, error) { + rsp, err := c.GetRiskByCategory(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetRiskByCategoryResponse(rsp) +} + +// GetRiskDistributionWithBodyWithResponse Get service risk level distribution over time +// +// Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/services/distribution (the `GetRiskDistribution` operationId). +func (c *ClientWithResponses) GetRiskDistributionWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetRiskDistributionResponse, error) { + rsp, err := c.GetRiskDistributionWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetRiskDistributionResponse(rsp) +} + +// GetRiskDistributionWithResponse Get service risk level distribution over time +// +// Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/services/distribution (the `GetRiskDistribution` operationId). +func (c *ClientWithResponses) GetRiskDistributionWithResponse(ctx context.Context, body GetRiskDistributionJSONRequestBody, reqEditors ...RequestEditorFn) (*GetRiskDistributionResponse, error) { + rsp, err := c.GetRiskDistribution(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetRiskDistributionResponse(rsp) +} + +// GetTeamCountsWithBodyWithResponse Get team counts over time +// +// Returns the number of teams in the tenant aggregated into time buckets. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/teams (the `GetTeamCounts` operationId). +func (c *ClientWithResponses) GetTeamCountsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetTeamCountsResponse, error) { + rsp, err := c.GetTeamCountsWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTeamCountsResponse(rsp) +} + +// GetTeamCountsWithResponse Get team counts over time +// +// Returns the number of teams in the tenant aggregated into time buckets. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/teams (the `GetTeamCounts` operationId). +func (c *ClientWithResponses) GetTeamCountsWithResponse(ctx context.Context, body GetTeamCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetTeamCountsResponse, error) { + rsp, err := c.GetTeamCounts(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTeamCountsResponse(rsp) +} + +// GetUserCountsWithBodyWithResponse Get user counts over time +// +// Returns the number of users in the tenant aggregated into time buckets. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/users (the `GetUserCounts` operationId). +func (c *ClientWithResponses) GetUserCountsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetUserCountsResponse, error) { + rsp, err := c.GetUserCountsWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetUserCountsResponse(rsp) +} + +// GetUserCountsWithResponse Get user counts over time +// +// Returns the number of users in the tenant aggregated into time buckets. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/reports/users (the `GetUserCounts` operationId). +func (c *ClientWithResponses) GetUserCountsWithResponse(ctx context.Context, body GetUserCountsJSONRequestBody, reqEditors ...RequestEditorFn) (*GetUserCountsResponse, error) { + rsp, err := c.GetUserCounts(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetUserCountsResponse(rsp) +} + +// GetServiceListWithResponse Fetch a list of services +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/services (the `GetServiceList` operationId). +func (c *ClientWithResponses) GetServiceListWithResponse(ctx context.Context, params *GetServiceListParams, reqEditors ...RequestEditorFn) (*GetServiceListResponse, error) { + rsp, err := c.GetServiceList(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetServiceListResponse(rsp) +} + +// UpsertServiceWithBodyWithResponse Create or update service +// +// Insert or update the service specified by the given `id`. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/services (the `UpsertService` operationId). +func (c *ClientWithResponses) UpsertServiceWithBodyWithResponse(ctx context.Context, params *UpsertServiceParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertServiceResponse, error) { + rsp, err := c.UpsertServiceWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertServiceResponse(rsp) +} + +// UpsertServiceWithResponse Create or update service +// +// Insert or update the service specified by the given `id`. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/services (the `UpsertService` operationId). +func (c *ClientWithResponses) UpsertServiceWithResponse(ctx context.Context, params *UpsertServiceParams, body UpsertServiceJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertServiceResponse, error) { + rsp, err := c.UpsertService(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertServiceResponse(rsp) +} + +// GetProfilesWithResponse Fetch a list of service profiles +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/services/profiles (the `GetProfiles` operationId). +func (c *ClientWithResponses) GetProfilesWithResponse(ctx context.Context, params *GetProfilesParams, reqEditors ...RequestEditorFn) (*GetProfilesResponse, error) { + rsp, err := c.GetProfiles(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetProfilesResponse(rsp) +} + +// UpsertProfileWithBodyWithResponse Create or update service profile +// +// Insert or update the service profile specified by the given `id`. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/services/profiles (the `UpsertProfile` operationId). +func (c *ClientWithResponses) UpsertProfileWithBodyWithResponse(ctx context.Context, params *UpsertProfileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertProfileResponse, error) { + rsp, err := c.UpsertProfileWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertProfileResponse(rsp) +} + +// UpsertProfileWithResponse Create or update service profile +// +// Insert or update the service profile specified by the given `id`. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/services/profiles (the `UpsertProfile` operationId). +func (c *ClientWithResponses) UpsertProfileWithResponse(ctx context.Context, params *UpsertProfileParams, body UpsertProfileJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertProfileResponse, error) { + rsp, err := c.UpsertProfile(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertProfileResponse(rsp) +} + +// DeleteProfileWithResponse Delete an existing service profile +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/services/profiles/{id} (the `DeleteProfile` operationId). +func (c *ClientWithResponses) DeleteProfileWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteProfileResponse, error) { + rsp, err := c.DeleteProfile(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteProfileResponse(rsp) +} + +// GetProfileWithResponse Get service profile by id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/services/profiles/{id} (the `GetProfile` operationId). +func (c *ClientWithResponses) GetProfileWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetProfileResponse, error) { + rsp, err := c.GetProfile(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetProfileResponse(rsp) +} + +// DeleteServiceWithResponse Delete an existing service +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/services/{id} (the `DeleteService` operationId). +func (c *ClientWithResponses) DeleteServiceWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*DeleteServiceResponse, error) { + rsp, err := c.DeleteService(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteServiceResponse(rsp) +} + +// GetServiceWithResponse Get service by id. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/services/{id} (the `GetService` operationId). +func (c *ClientWithResponses) GetServiceWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetServiceResponse, error) { + rsp, err := c.GetService(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetServiceResponse(rsp) +} + +// GetServiceExperimentsWithResponse Get experiments associated to an service. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/services/{id}/experiments (the `GetServiceExperiments` operationId). +func (c *ClientWithResponses) GetServiceExperimentsWithResponse(ctx context.Context, id openapi_types.UUID, params *GetServiceExperimentsParams, reqEditors ...RequestEditorFn) (*GetServiceExperimentsResponse, error) { + rsp, err := c.GetServiceExperiments(ctx, id, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetServiceExperimentsResponse(rsp) +} + +// UnlinkCustomExperimentWithResponse Remove a linked custom experiment from a service. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/services/{id}/experiments/custom (the `UnlinkCustomExperiment` operationId). +func (c *ClientWithResponses) UnlinkCustomExperimentWithResponse(ctx context.Context, id openapi_types.UUID, params *UnlinkCustomExperimentParams, reqEditors ...RequestEditorFn) (*UnlinkCustomExperimentResponse, error) { + rsp, err := c.UnlinkCustomExperiment(ctx, id, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseUnlinkCustomExperimentResponse(rsp) +} + +// LinkCustomExperimentWithBodyWithResponse Link a custom experiment to a service. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/services/{id}/experiments/custom (the `LinkCustomExperiment` operationId). +func (c *ClientWithResponses) LinkCustomExperimentWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*LinkCustomExperimentResponse, error) { + rsp, err := c.LinkCustomExperimentWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseLinkCustomExperimentResponse(rsp) +} + +// LinkCustomExperimentWithResponse Link a custom experiment to a service. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/services/{id}/experiments/custom (the `LinkCustomExperiment` operationId). +func (c *ClientWithResponses) LinkCustomExperimentWithResponse(ctx context.Context, id openapi_types.UUID, body LinkCustomExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*LinkCustomExperimentResponse, error) { + rsp, err := c.LinkCustomExperiment(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseLinkCustomExperimentResponse(rsp) +} + +// UpsertProvidedExperimentWithBodyWithResponse Create or update a provided experiment. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/services/{id}/experiments/provided (the `UpsertProvidedExperiment` operationId). +func (c *ClientWithResponses) UpsertProvidedExperimentWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, params *UpsertProvidedExperimentParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertProvidedExperimentResponse, error) { + rsp, err := c.UpsertProvidedExperimentWithBody(ctx, id, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertProvidedExperimentResponse(rsp) +} + +// UpsertProvidedExperimentWithResponse Create or update a provided experiment. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/services/{id}/experiments/provided (the `UpsertProvidedExperiment` operationId). +func (c *ClientWithResponses) UpsertProvidedExperimentWithResponse(ctx context.Context, id openapi_types.UUID, params *UpsertProvidedExperimentParams, body UpsertProvidedExperimentJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertProvidedExperimentResponse, error) { + rsp, err := c.UpsertProvidedExperiment(ctx, id, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertProvidedExperimentResponse(rsp) +} + +// GetRiskWithResponse Get the risk score for a service +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/services/{id}/risk (the `GetRisk` operationId). +func (c *ClientWithResponses) GetRiskWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetRiskResponse, error) { + rsp, err := c.GetRisk(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetRiskResponse(rsp) +} + +// GetServiceVariablesWithResponse Get service variables +// +// Get all variables owned by the service. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/services/{id}/variables (the `GetServiceVariables` operationId). +func (c *ClientWithResponses) GetServiceVariablesWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*GetServiceVariablesResponse, error) { + rsp, err := c.GetServiceVariables(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetServiceVariablesResponse(rsp) +} + +// MergeServiceVariablesWithBodyWithResponse Add / merge service variables +// +// All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PATCH /api/services/{id}/variables (the `MergeServiceVariables` operationId). +func (c *ClientWithResponses) MergeServiceVariablesWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MergeServiceVariablesResponse, error) { + rsp, err := c.MergeServiceVariablesWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMergeServiceVariablesResponse(rsp) +} + +// MergeServiceVariablesWithResponse Add / merge service variables +// +// All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PATCH /api/services/{id}/variables (the `MergeServiceVariables` operationId). +func (c *ClientWithResponses) MergeServiceVariablesWithResponse(ctx context.Context, id openapi_types.UUID, body MergeServiceVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*MergeServiceVariablesResponse, error) { + rsp, err := c.MergeServiceVariables(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMergeServiceVariablesResponse(rsp) +} + +// SetServiceVariablesWithBodyWithResponse Replace all service variables +// +// All provided variables will be associated with the given service and existing ones removed. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/services/{id}/variables (the `SetServiceVariables` operationId). +func (c *ClientWithResponses) SetServiceVariablesWithBodyWithResponse(ctx context.Context, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetServiceVariablesResponse, error) { + rsp, err := c.SetServiceVariablesWithBody(ctx, id, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetServiceVariablesResponse(rsp) +} + +// SetServiceVariablesWithResponse Replace all service variables +// +// All provided variables will be associated with the given service and existing ones removed. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/services/{id}/variables (the `SetServiceVariables` operationId). +func (c *ClientWithResponses) SetServiceVariablesWithResponse(ctx context.Context, id openapi_types.UUID, body SetServiceVariablesJSONRequestBody, reqEditors ...RequestEditorFn) (*SetServiceVariablesResponse, error) { + rsp, err := c.SetServiceVariables(ctx, id, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetServiceVariablesResponse(rsp) +} + +// GetTargetsStatsWithResponse Gather target statistics without any filters +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/target-stats (the `GetTargetsStats` operationId). +func (c *ClientWithResponses) GetTargetsStatsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetTargetsStatsResponse, error) { + rsp, err := c.GetTargetsStats(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTargetsStatsResponse(rsp) +} + +// GetTargetsStats1WithBodyWithResponse Gather target statistics for a given predicate or query +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/target-stats (the `GetTargetsStats1` operationId). +func (c *ClientWithResponses) GetTargetsStats1WithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*GetTargetsStats1Response, error) { + rsp, err := c.GetTargetsStats1WithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTargetsStats1Response(rsp) +} + +// GetTargetsStats1WithResponse Gather target statistics for a given predicate or query +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/target-stats (the `GetTargetsStats1` operationId). +func (c *ClientWithResponses) GetTargetsStats1WithResponse(ctx context.Context, body GetTargetsStats1JSONRequestBody, reqEditors ...RequestEditorFn) (*GetTargetsStats1Response, error) { + rsp, err := c.GetTargetsStats1(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTargetsStats1Response(rsp) +} + +// GetTargetsWithResponse Get targets +// +// Get targets. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/targets (the `GetTargets` operationId). +func (c *ClientWithResponses) GetTargetsWithResponse(ctx context.Context, params *GetTargetsParams, reqEditors ...RequestEditorFn) (*GetTargetsResponse, error) { + rsp, err := c.GetTargets(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTargetsResponse(rsp) +} + +// GetTargetAttributeKeysWithResponse Get attribute key +// +// Get all available attribute keys for a specific target type in a given environment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/targets/attributes/keys (the `GetTargetAttributeKeys` operationId). +func (c *ClientWithResponses) GetTargetAttributeKeysWithResponse(ctx context.Context, params *GetTargetAttributeKeysParams, reqEditors ...RequestEditorFn) (*GetTargetAttributeKeysResponse, error) { + rsp, err := c.GetTargetAttributeKeys(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTargetAttributeKeysResponse(rsp) +} + +// GetTargetAttributeValuesWithResponse Get attribute values +// +// Get all available attribute values for a specific attribute and target type in a given environment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/targets/attributes/values (the `GetTargetAttributeValues` operationId). +func (c *ClientWithResponses) GetTargetAttributeValuesWithResponse(ctx context.Context, params *GetTargetAttributeValuesParams, reqEditors ...RequestEditorFn) (*GetTargetAttributeValuesResponse, error) { + rsp, err := c.GetTargetAttributeValues(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTargetAttributeValuesResponse(rsp) +} + +// GetTeamsWithResponse Fetch a list of all teams +// +// Get a list of all teams that exist.
If used with a team-associated `accessToken` and `onlyAccessible` is set to `true` you only get the team of the `accessToken`. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/teams (the `GetTeams` operationId). +func (c *ClientWithResponses) GetTeamsWithResponse(ctx context.Context, params *GetTeamsParams, reqEditors ...RequestEditorFn) (*GetTeamsResponse, error) { + rsp, err := c.GetTeams(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTeamsResponse(rsp) +} + +// UpsertTeamWithBodyWithResponse Create or update a team +// +// Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams (the `UpsertTeam` operationId). +func (c *ClientWithResponses) UpsertTeamWithBodyWithResponse(ctx context.Context, params *UpsertTeamParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpsertTeamResponse, error) { + rsp, err := c.UpsertTeamWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertTeamResponse(rsp) +} + +// UpsertTeamWithResponse Create or update a team +// +// Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams (the `UpsertTeam` operationId). +func (c *ClientWithResponses) UpsertTeamWithResponse(ctx context.Context, params *UpsertTeamParams, body UpsertTeamJSONRequestBody, reqEditors ...RequestEditorFn) (*UpsertTeamResponse, error) { + rsp, err := c.UpsertTeam(ctx, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpsertTeamResponse(rsp) +} + +// DeleteTeamWithResponse Delete team +// +// Remove the given team from the Steadybit platform. This will only work, if there are no experiments running at the moment. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with DELETE /api/teams/{key} (the `DeleteTeam` operationId). +func (c *ClientWithResponses) DeleteTeamWithResponse(ctx context.Context, key string, params *DeleteTeamParams, reqEditors ...RequestEditorFn) (*DeleteTeamResponse, error) { + rsp, err := c.DeleteTeam(ctx, key, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteTeamResponse(rsp) +} + +// GetTeamWithResponse Fetch a single team +// +// Get all details of a single existing teams. +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/teams/{key} (the `GetTeam` operationId). +func (c *ClientWithResponses) GetTeamWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetTeamResponse, error) { + rsp, err := c.GetTeam(ctx, key, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTeamResponse(rsp) +} + +// GetTeamEnvironmentsWithResponse Get all environments assigned to the team +// +// Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/teams/{key}/environments (the `GetTeamEnvironments` operationId). +func (c *ClientWithResponses) GetTeamEnvironmentsWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetTeamEnvironmentsResponse, error) { + rsp, err := c.GetTeamEnvironments(ctx, key, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTeamEnvironmentsResponse(rsp) +} + +// SetTeamEnvironmentsWithBodyWithResponse Update the environments of a specific team +// +// The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/teams/{key}/environments (the `SetTeamEnvironments` operationId). +func (c *ClientWithResponses) SetTeamEnvironmentsWithBodyWithResponse(ctx context.Context, key string, params *SetTeamEnvironmentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetTeamEnvironmentsResponse, error) { + rsp, err := c.SetTeamEnvironmentsWithBody(ctx, key, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetTeamEnvironmentsResponse(rsp) +} + +// SetTeamEnvironmentsWithResponse Update the environments of a specific team +// +// The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/teams/{key}/environments (the `SetTeamEnvironments` operationId). +func (c *ClientWithResponses) SetTeamEnvironmentsWithResponse(ctx context.Context, key string, params *SetTeamEnvironmentsParams, body SetTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*SetTeamEnvironmentsResponse, error) { + rsp, err := c.SetTeamEnvironments(ctx, key, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetTeamEnvironmentsResponse(rsp) +} + +// AddTeamEnvironmentsWithBodyWithResponse Add an allowed environment to a team +// +// The given environments will be added to the specified team. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams/{key}/environments/add (the `AddTeamEnvironments` operationId). +func (c *ClientWithResponses) AddTeamEnvironmentsWithBodyWithResponse(ctx context.Context, key string, params *AddTeamEnvironmentsParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AddTeamEnvironmentsResponse, error) { + rsp, err := c.AddTeamEnvironmentsWithBody(ctx, key, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseAddTeamEnvironmentsResponse(rsp) +} + +// AddTeamEnvironmentsWithResponse Add an allowed environment to a team +// +// The given environments will be added to the specified team. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams/{key}/environments/add (the `AddTeamEnvironments` operationId). +func (c *ClientWithResponses) AddTeamEnvironmentsWithResponse(ctx context.Context, key string, params *AddTeamEnvironmentsParams, body AddTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*AddTeamEnvironmentsResponse, error) { + rsp, err := c.AddTeamEnvironments(ctx, key, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseAddTeamEnvironmentsResponse(rsp) +} + +// RemoveTeamEnvironmentsWithBodyWithResponse Remove allowed environment from a team +// +// The given environments will be removed from the specified team. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams/{key}/environments/remove (the `RemoveTeamEnvironments` operationId). +func (c *ClientWithResponses) RemoveTeamEnvironmentsWithBodyWithResponse(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RemoveTeamEnvironmentsResponse, error) { + rsp, err := c.RemoveTeamEnvironmentsWithBody(ctx, key, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRemoveTeamEnvironmentsResponse(rsp) +} + +// RemoveTeamEnvironmentsWithResponse Remove allowed environment from a team +// +// The given environments will be removed from the specified team. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams/{key}/environments/remove (the `RemoveTeamEnvironments` operationId). +func (c *ClientWithResponses) RemoveTeamEnvironmentsWithResponse(ctx context.Context, key string, body RemoveTeamEnvironmentsJSONRequestBody, reqEditors ...RequestEditorFn) (*RemoveTeamEnvironmentsResponse, error) { + rsp, err := c.RemoveTeamEnvironments(ctx, key, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRemoveTeamEnvironmentsResponse(rsp) +} + +// GetTeamMembersWithResponse Get all members being part of the team +// +// Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). +// +// Returns a wrapper object for the known response body format(s). +// +// Corresponds with GET /api/teams/{key}/members (the `GetTeamMembers` operationId). +func (c *ClientWithResponses) GetTeamMembersWithResponse(ctx context.Context, key string, reqEditors ...RequestEditorFn) (*GetTeamMembersResponse, error) { + rsp, err := c.GetTeamMembers(ctx, key, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTeamMembersResponse(rsp) +} + +// SetTeamMembersWithBodyWithResponse Update the members of a specific team +// +// The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/teams/{key}/members (the `SetTeamMembers` operationId). +func (c *ClientWithResponses) SetTeamMembersWithBodyWithResponse(ctx context.Context, key string, params *SetTeamMembersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetTeamMembersResponse, error) { + rsp, err := c.SetTeamMembersWithBody(ctx, key, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetTeamMembersResponse(rsp) +} + +// SetTeamMembersWithResponse Update the members of a specific team +// +// The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with PUT /api/teams/{key}/members (the `SetTeamMembers` operationId). +func (c *ClientWithResponses) SetTeamMembersWithResponse(ctx context.Context, key string, params *SetTeamMembersParams, body SetTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*SetTeamMembersResponse, error) { + rsp, err := c.SetTeamMembers(ctx, key, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetTeamMembersResponse(rsp) +} + +// AddTeamMembersWithBodyWithResponse Add team members to a team +// +// The given members will be added to the specified team. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams/{key}/members/add (the `AddTeamMembers` operationId). +func (c *ClientWithResponses) AddTeamMembersWithBodyWithResponse(ctx context.Context, key string, params *AddTeamMembersParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*AddTeamMembersResponse, error) { + rsp, err := c.AddTeamMembersWithBody(ctx, key, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseAddTeamMembersResponse(rsp) +} + +// AddTeamMembersWithResponse Add team members to a team +// +// The given members will be added to the specified team. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams/{key}/members/add (the `AddTeamMembers` operationId). +func (c *ClientWithResponses) AddTeamMembersWithResponse(ctx context.Context, key string, params *AddTeamMembersParams, body AddTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*AddTeamMembersResponse, error) { + rsp, err := c.AddTeamMembers(ctx, key, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseAddTeamMembersResponse(rsp) +} + +// RemoveTeamMembersWithBodyWithResponse Remove team members from a team +// +// The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams/{key}/members/remove (the `RemoveTeamMembers` operationId). +func (c *ClientWithResponses) RemoveTeamMembersWithBodyWithResponse(ctx context.Context, key string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RemoveTeamMembersResponse, error) { + rsp, err := c.RemoveTeamMembersWithBody(ctx, key, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRemoveTeamMembersResponse(rsp) +} + +// RemoveTeamMembersWithResponse Remove team members from a team +// +// The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/teams/{key}/members/remove (the `RemoveTeamMembers` operationId). +func (c *ClientWithResponses) RemoveTeamMembersWithResponse(ctx context.Context, key string, body RemoveTeamMembersJSONRequestBody, reqEditors ...RequestEditorFn) (*RemoveTeamMembersResponse, error) { + rsp, err := c.RemoveTeamMembers(ctx, key, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRemoveTeamMembersResponse(rsp) +} + +// InviteUserWithBodyWithResponse Invite users to a tenant +// +// Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. +// +// Takes any type of body and a specified content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/users/invite (the `InviteUser` operationId). +func (c *ClientWithResponses) InviteUserWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*InviteUserResponse, error) { + rsp, err := c.InviteUserWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseInviteUserResponse(rsp) +} + +// InviteUserWithResponse Invite users to a tenant +// +// Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. +// +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Corresponds with POST /api/users/invite (the `InviteUser` operationId). +func (c *ClientWithResponses) InviteUserWithResponse(ctx context.Context, body InviteUserJSONRequestBody, reqEditors ...RequestEditorFn) (*InviteUserResponse, error) { + rsp, err := c.InviteUser(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseInviteUserResponse(rsp) +} + +// ParseGetAccessTokensResponse parses an HTTP response from a GetAccessTokensWithResponse call +func ParseGetAccessTokensResponse(rsp *http.Response) (*GetAccessTokensResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetAccessTokensResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOAccessTokensPageItemAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOAccessTokensPageItemAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseCreateAccessTokenResponse parses an HTTP response from a CreateAccessTokenWithResponse call +func ParseCreateAccessTokenResponse(rsp *http.Response) (*CreateAccessTokenResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateAccessTokenResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest CreateAccessTokenResponseAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest CreateAccessTokenResponseAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseGetAccessTokens1Response parses an HTTP response from a GetAccessTokens1WithResponse call +func ParseGetAccessTokens1Response(rsp *http.Response) (*GetAccessTokens1Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetAccessTokens1Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOAccessTokensPageItemV2AO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOAccessTokensPageItemV2AO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseCreateAccessToken1Response parses an HTTP response from a CreateAccessToken1WithResponse call +func ParseCreateAccessToken1Response(rsp *http.Response) (*CreateAccessToken1Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateAccessToken1Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest CreateAccessTokenResponseV2AO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest CreateAccessTokenResponseV2AO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteAccessToken1Response parses an HTTP response from a DeleteAccessToken1WithResponse call +func ParseDeleteAccessToken1Response(rsp *http.Response) (*DeleteAccessToken1Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteAccessToken1Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseRecreateAccessTokenResponse parses an HTTP response from a RecreateAccessTokenWithResponse call +func ParseRecreateAccessTokenResponse(rsp *http.Response) (*RecreateAccessTokenResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RecreateAccessTokenResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest CreateAccessTokenResponseV2AO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest CreateAccessTokenResponseV2AO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteAccessTokenResponse parses an HTTP response from a DeleteAccessTokenWithResponse call +func ParseDeleteAccessTokenResponse(rsp *http.Response) (*DeleteAccessTokenResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteAccessTokenResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseFindAllActionsResponse parses an HTTP response from a FindAllActionsWithResponse call +func ParseFindAllActionsResponse(rsp *http.Response) (*FindAllActionsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &FindAllActionsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ActionSummariesAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ActionSummariesAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetActionResponse parses an HTTP response from a GetActionWithResponse call +func ParseGetActionResponse(rsp *http.Response) (*GetActionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetActionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ActionAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ActionAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetTargetAdviceSummaryResponse parses an HTTP response from a GetTargetAdviceSummaryWithResponse call +func ParseGetTargetAdviceSummaryResponse(rsp *http.Response) (*GetTargetAdviceSummaryResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTargetAdviceSummaryResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest AdviceSummaryAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest AdviceSummaryAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseFindResponse parses an HTTP response from a FindWithResponse call +func ParseFindResponse(rsp *http.Response) (*FindResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &FindResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []AuditLogEntry + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseForwardToPlatformResponse parses an HTTP response from a ForwardToPlatformWithResponse call +func ParseForwardToPlatformResponse(rsp *http.Response) (*ForwardToPlatformResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ForwardToPlatformResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 307: + var dest map[string]interface{} + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON307 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 307: + var dest map[string]interface{} + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML307 = &dest + + } + + return response, nil +} + +// ParseGetLinkedBadgeResponse parses an HTTP response from a GetLinkedBadgeWithResponse call +func ParseGetLinkedBadgeResponse(rsp *http.Response) (*GetLinkedBadgeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetLinkedBadgeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetEnvironmentsResponse parses an HTTP response from a GetEnvironmentsWithResponse call +func ParseGetEnvironmentsResponse(rsp *http.Response) (*GetEnvironmentsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetEnvironmentsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest EnvironmentSummariesAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest EnvironmentSummariesAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertEnvironmentResponse parses an HTTP response from a UpsertEnvironmentWithResponse call +func ParseUpsertEnvironmentResponse(rsp *http.Response) (*UpsertEnvironmentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertEnvironmentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest EnvironmentAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest EnvironmentAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest EnvironmentAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest EnvironmentAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteEnvironmentResponse parses an HTTP response from a DeleteEnvironmentWithResponse call +func ParseDeleteEnvironmentResponse(rsp *http.Response) (*DeleteEnvironmentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteEnvironmentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetEnvironmentResponse parses an HTTP response from a GetEnvironmentWithResponse call +func ParseGetEnvironmentResponse(rsp *http.Response) (*GetEnvironmentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetEnvironmentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest EnvironmentAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest EnvironmentAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 400: + break // No content-type + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetEnvironmentVariablesResponse parses an HTTP response from a GetEnvironmentVariablesWithResponse call +func ParseGetEnvironmentVariablesResponse(rsp *http.Response) (*GetEnvironmentVariablesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetEnvironmentVariablesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest string + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest string + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseSetEnvironmentVariablesResponse parses an HTTP response from a SetEnvironmentVariablesWithResponse call +func ParseSetEnvironmentVariablesResponse(rsp *http.Response) (*SetEnvironmentVariablesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SetEnvironmentVariablesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseUpdateEnvironmentVariablesResponse parses an HTTP response from a UpdateEnvironmentVariablesWithResponse call +func ParseUpdateEnvironmentVariablesResponse(rsp *http.Response) (*UpdateEnvironmentVariablesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateEnvironmentVariablesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetExperimentsResponse parses an HTTP response from a GetExperimentsWithResponse call +func ParseGetExperimentsResponse(rsp *http.Response) (*GetExperimentsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentSummariesAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentSummariesAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseCreateOrUpdateExperimentResponse parses an HTTP response from a CreateOrUpdateExperimentWithResponse call +func ParseCreateOrUpdateExperimentResponse(rsp *http.Response) (*CreateOrUpdateExperimentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateOrUpdateExperimentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseSaveAndRunResponse parses an HTTP response from a SaveAndRunWithResponse call +func ParseSaveAndRunResponse(rsp *http.Response) (*SaveAndRunResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SaveAndRunResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExecuteExperimentResponseAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExecuteExperimentResponseAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ExecuteExperimentResponseAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 422: + var dest ExecuteExperimentResponseAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML422 = &dest + + } + + return response, nil +} + +// ParseGetExperimentExecutions1Response parses an HTTP response from a GetExperimentExecutions1WithResponse call +func ParseGetExperimentExecutions1Response(rsp *http.Response) (*GetExperimentExecutions1Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentExecutions1Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentExecutionSummariesAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentExecutionSummariesAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetExperimentExecutions2Response parses an HTTP response from a GetExperimentExecutions2WithResponse call +func ParseGetExperimentExecutions2Response(rsp *http.Response) (*GetExperimentExecutions2Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentExecutions2Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOExperimentExecutionPageItemAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOExperimentExecutionPageItemAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetExperimentExecutionResponse parses an HTTP response from a GetExperimentExecutionWithResponse call +func ParseGetExperimentExecutionResponse(rsp *http.Response) (*GetExperimentExecutionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentExecutionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentExecutionAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentExecutionAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetArtifactResponse parses an HTTP response from a GetArtifactWithResponse call +func ParseGetArtifactResponse(rsp *http.Response) (*GetArtifactResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetArtifactResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseCancelExperimentExecutionResponse parses an HTTP response from a CancelExperimentExecutionWithResponse call +func ParseCancelExperimentExecutionResponse(rsp *http.Response) (*CancelExperimentExecutionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CancelExperimentExecutionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseUpdateExecutionPropertiesResponse parses an HTTP response from a UpdateExecutionPropertiesWithResponse call +func ParseUpdateExecutionPropertiesResponse(rsp *http.Response) (*UpdateExecutionPropertiesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateExecutionPropertiesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseAddExecutionPropertyValueResponse parses an HTTP response from a AddExecutionPropertyValueWithResponse call +func ParseAddExecutionPropertyValueResponse(rsp *http.Response) (*AddExecutionPropertyValueResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &AddExecutionPropertyValueResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseSetExecutionPropertyValueResponse parses an HTTP response from a SetExecutionPropertyValueWithResponse call +func ParseSetExecutionPropertyValueResponse(rsp *http.Response) (*SetExecutionPropertyValueResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SetExecutionPropertyValueResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseUpsertScheduleResponse parses an HTTP response from a UpsertScheduleWithResponse call +func ParseUpsertScheduleResponse(rsp *http.Response) (*UpsertScheduleResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertScheduleResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentScheduleAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentScheduleAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest ExperimentScheduleAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest ExperimentScheduleAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseGetAllSchedulesV2Response parses an HTTP response from a GetAllSchedulesV2WithResponse call +func ParseGetAllSchedulesV2Response(rsp *http.Response) (*GetAllSchedulesV2Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetAllSchedulesV2Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []ExperimentScheduleAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest []ExperimentScheduleAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseRemoveExperimentScheduleByIdResponse parses an HTTP response from a RemoveExperimentScheduleByIdWithResponse call +func ParseRemoveExperimentScheduleByIdResponse(rsp *http.Response) (*RemoveExperimentScheduleByIdResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RemoveExperimentScheduleByIdResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetSchedulesResponse parses an HTTP response from a GetSchedulesWithResponse call +func ParseGetSchedulesResponse(rsp *http.Response) (*GetSchedulesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSchedulesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentScheduleAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentScheduleAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParsePatchScheduleResponse parses an HTTP response from a PatchScheduleWithResponse call +func ParsePatchScheduleResponse(rsp *http.Response) (*PatchScheduleResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PatchScheduleResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentScheduleAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentScheduleAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseGetExperimentTemplatesResponse parses an HTTP response from a GetExperimentTemplatesWithResponse call +func ParseGetExperimentTemplatesResponse(rsp *http.Response) (*GetExperimentTemplatesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentTemplatesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentTemplateSummariesAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentTemplateSummariesAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertExperimentTemplateResponse parses an HTTP response from a UpsertExperimentTemplateWithResponse call +func ParseUpsertExperimentTemplateResponse(rsp *http.Response) (*UpsertExperimentTemplateResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertExperimentTemplateResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentTemplateAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentTemplateAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest ExperimentTemplateAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest ExperimentTemplateAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseImportFromHubResponse parses an HTTP response from a ImportFromHubWithResponse call +func ParseImportFromHubResponse(rsp *http.Response) (*ImportFromHubResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ImportFromHubResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseDeleteExperimentTemplateResponse parses an HTTP response from a DeleteExperimentTemplateWithResponse call +func ParseDeleteExperimentTemplateResponse(rsp *http.Response) (*DeleteExperimentTemplateResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteExperimentTemplateResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetExperimentTemplateResponse parses an HTTP response from a GetExperimentTemplateWithResponse call +func ParseGetExperimentTemplateResponse(rsp *http.Response) (*GetExperimentTemplateResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentTemplateResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentTemplateAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentTemplateAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseCreateExperimentByTemplateResponse parses an HTTP response from a CreateExperimentByTemplateWithResponse call +func ParseCreateExperimentByTemplateResponse(rsp *http.Response) (*CreateExperimentByTemplateResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateExperimentByTemplateResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseSaveAndRunFromTemplateResponse parses an HTTP response from a SaveAndRunFromTemplateWithResponse call +func ParseSaveAndRunFromTemplateResponse(rsp *http.Response) (*SaveAndRunFromTemplateResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SaveAndRunFromTemplateResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExecuteExperimentResponseAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExecuteExperimentResponseAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 422: + var dest ExecuteExperimentResponseAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON422 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 422: + var dest ExecuteExperimentResponseAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML422 = &dest + + } + + return response, nil +} + +// ParseUpdateExperimentByTemplateResponse parses an HTTP response from a UpdateExperimentByTemplateWithResponse call +func ParseUpdateExperimentByTemplateResponse(rsp *http.Response) (*UpdateExperimentByTemplateResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateExperimentByTemplateResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseDeleteExperimentResponse parses an HTTP response from a DeleteExperimentWithResponse call +func ParseDeleteExperimentResponse(rsp *http.Response) (*DeleteExperimentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteExperimentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetExperimentResponse parses an HTTP response from a GetExperimentWithResponse call +func ParseGetExperimentResponse(rsp *http.Response) (*GetExperimentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseUpdateExperimentResponse parses an HTTP response from a UpdateExperimentWithResponse call +func ParseUpdateExperimentResponse(rsp *http.Response) (*UpdateExperimentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateExperimentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetExperimentBadgeResponse parses an HTTP response from a GetExperimentBadgeWithResponse call +func ParseGetExperimentBadgeResponse(rsp *http.Response) (*GetExperimentBadgeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentBadgeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseExecuteExperimentResponse parses an HTTP response from a ExecuteExperimentWithResponse call +func ParseExecuteExperimentResponse(rsp *http.Response) (*ExecuteExperimentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ExecuteExperimentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest ExecuteExperimentResponseAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest ExecuteExperimentResponseAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest ExecuteExperimentResponseAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 404: + var dest ExecuteExperimentResponseAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML404 = &dest + + } + + return response, nil +} + +// ParseGetExperimentExecutions3Response parses an HTTP response from a GetExperimentExecutions3WithResponse call +func ParseGetExperimentExecutions3Response(rsp *http.Response) (*GetExperimentExecutions3Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentExecutions3Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ExperimentExecutionSummariesAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ExperimentExecutionSummariesAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetLandscapeViewsResponse parses an HTTP response from a GetLandscapeViewsWithResponse call +func ParseGetLandscapeViewsResponse(rsp *http.Response) (*GetLandscapeViewsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetLandscapeViewsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListResponseLandscapeViewAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ListResponseLandscapeViewAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseCreateLandscapeViewResponse parses an HTTP response from a CreateLandscapeViewWithResponse call +func ParseCreateLandscapeViewResponse(rsp *http.Response) (*CreateLandscapeViewResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateLandscapeViewResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest LandscapeViewAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest LandscapeViewAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteLandscapeViewResponse parses an HTTP response from a DeleteLandscapeViewWithResponse call +func ParseDeleteLandscapeViewResponse(rsp *http.Response) (*DeleteLandscapeViewResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteLandscapeViewResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetLandscapeViewResponse parses an HTTP response from a GetLandscapeViewWithResponse call +func ParseGetLandscapeViewResponse(rsp *http.Response) (*GetLandscapeViewResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetLandscapeViewResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest LandscapeViewAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest LandscapeViewAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 400: + break // No content-type + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseUpdateLandscapeViewResponse parses an HTTP response from a UpdateLandscapeViewWithResponse call +func ParseUpdateLandscapeViewResponse(rsp *http.Response) (*UpdateLandscapeViewResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateLandscapeViewResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest LandscapeViewAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest LandscapeViewAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseHealthResponse parses an HTTP response from a HealthWithResponse call +func ParseHealthResponse(rsp *http.Response) (*HealthResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &HealthResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest map[string]interface{} + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest map[string]interface{} + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseLivenessResponse parses an HTTP response from a LivenessWithResponse call +func ParseLivenessResponse(rsp *http.Response) (*LivenessResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &LivenessResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest map[string]interface{} + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest map[string]interface{} + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseReadinessResponse parses an HTTP response from a ReadinessWithResponse call +func ParseReadinessResponse(rsp *http.Response) (*ReadinessResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ReadinessResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest map[string]interface{} + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest map[string]interface{} + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetHubsResponse parses an HTTP response from a GetHubsWithResponse call +func ParseGetHubsResponse(rsp *http.Response) (*GetHubsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetHubsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HubSummariesAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest HubSummariesAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertHubResponse parses an HTTP response from a UpsertHubWithResponse call +func ParseUpsertHubResponse(rsp *http.Response) (*UpsertHubResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertHubResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HubAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest HubAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest HubAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest HubAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseConnectionCheckResponse parses an HTTP response from a ConnectionCheckWithResponse call +func ParseConnectionCheckResponse(rsp *http.Response) (*ConnectionCheckResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ConnectionCheckResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HubConnectionCheckResponseAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest HubConnectionCheckResponseAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 400: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteHubResponse parses an HTTP response from a DeleteHubWithResponse call +func ParseDeleteHubResponse(rsp *http.Response) (*DeleteHubResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteHubResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetHubByIdResponse parses an HTTP response from a GetHubByIdWithResponse call +func ParseGetHubByIdResponse(rsp *http.Response) (*GetHubByIdResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetHubByIdResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HubAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest HubAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseResyncHubResponse parses an HTTP response from a ResyncHubWithResponse call +func ParseResyncHubResponse(rsp *http.Response) (*ResyncHubResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ResyncHubResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HubAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest HubAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + case rsp.StatusCode == 429: + break // No content-type + + } + + return response, nil +} + +// ParseGetPreflightWebhooksResponse parses an HTTP response from a GetPreflightWebhooksWithResponse call +func ParseGetPreflightWebhooksResponse(rsp *http.Response) (*GetPreflightWebhooksResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPreflightWebhooksResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListResponsePreflightWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ListResponsePreflightWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertPreflightWebhookResponse parses an HTTP response from a UpsertPreflightWebhookWithResponse call +func ParseUpsertPreflightWebhookResponse(rsp *http.Response) (*UpsertPreflightWebhookResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertPreflightWebhookResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PreflightWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PreflightWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest PreflightWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest PreflightWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseGetPreflightActionIntegrationsResponse parses an HTTP response from a GetPreflightActionIntegrationsWithResponse call +func ParseGetPreflightActionIntegrationsResponse(rsp *http.Response) (*GetPreflightActionIntegrationsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPreflightActionIntegrationsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListResponsePreflightActionIntegrationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ListResponsePreflightActionIntegrationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertPreflightActionIntegrationResponse parses an HTTP response from a UpsertPreflightActionIntegrationWithResponse call +func ParseUpsertPreflightActionIntegrationResponse(rsp *http.Response) (*UpsertPreflightActionIntegrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertPreflightActionIntegrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PreflightActionIntegrationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PreflightActionIntegrationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest PreflightActionIntegrationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest PreflightActionIntegrationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeletePreflightActionIntegrationResponse parses an HTTP response from a DeletePreflightActionIntegrationWithResponse call +func ParseDeletePreflightActionIntegrationResponse(rsp *http.Response) (*DeletePreflightActionIntegrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeletePreflightActionIntegrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PreflightActionIntegrationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PreflightActionIntegrationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetPreflightActionIntegrationResponse parses an HTTP response from a GetPreflightActionIntegrationWithResponse call +func ParseGetPreflightActionIntegrationResponse(rsp *http.Response) (*GetPreflightActionIntegrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPreflightActionIntegrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PreflightActionIntegrationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PreflightActionIntegrationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseDeletePreflightWebhookResponse parses an HTTP response from a DeletePreflightWebhookWithResponse call +func ParseDeletePreflightWebhookResponse(rsp *http.Response) (*DeletePreflightWebhookResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeletePreflightWebhookResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PreflightWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PreflightWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetPreflightWebhookResponse parses an HTTP response from a GetPreflightWebhookWithResponse call +func ParseGetPreflightWebhookResponse(rsp *http.Response) (*GetPreflightWebhookResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPreflightWebhookResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PreflightWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PreflightWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetSlackIntegrationsResponse parses an HTTP response from a GetSlackIntegrationsWithResponse call +func ParseGetSlackIntegrationsResponse(rsp *http.Response) (*GetSlackIntegrationsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSlackIntegrationsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListResponseSlackWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ListResponseSlackWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertSlackIntegrationResponse parses an HTTP response from a UpsertSlackIntegrationWithResponse call +func ParseUpsertSlackIntegrationResponse(rsp *http.Response) (*UpsertSlackIntegrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertSlackIntegrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SlackWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest SlackWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest SlackWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest SlackWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteSlackIntegrationResponse parses an HTTP response from a DeleteSlackIntegrationWithResponse call +func ParseDeleteSlackIntegrationResponse(rsp *http.Response) (*DeleteSlackIntegrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteSlackIntegrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SlackWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest SlackWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetSlackIntegrationResponse parses an HTTP response from a GetSlackIntegrationWithResponse call +func ParseGetSlackIntegrationResponse(rsp *http.Response) (*GetSlackIntegrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSlackIntegrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SlackWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest SlackWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetCustomWebhooksResponse parses an HTTP response from a GetCustomWebhooksWithResponse call +func ParseGetCustomWebhooksResponse(rsp *http.Response) (*GetCustomWebhooksResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetCustomWebhooksResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListResponseCustomWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ListResponseCustomWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertCustomWebhookResponse parses an HTTP response from a UpsertCustomWebhookWithResponse call +func ParseUpsertCustomWebhookResponse(rsp *http.Response) (*UpsertCustomWebhookResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertCustomWebhookResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest CustomWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest CustomWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest CustomWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest CustomWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteCustomWebhookResponse parses an HTTP response from a DeleteCustomWebhookWithResponse call +func ParseDeleteCustomWebhookResponse(rsp *http.Response) (*DeleteCustomWebhookResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteCustomWebhookResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest CustomWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest CustomWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetCustomWebhookResponse parses an HTTP response from a GetCustomWebhookWithResponse call +func ParseGetCustomWebhookResponse(rsp *http.Response) (*GetCustomWebhookResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetCustomWebhookResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest CustomWebhookAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest CustomWebhookAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseDisengageKillswitchResponse parses an HTTP response from a DisengageKillswitchWithResponse call +func ParseDisengageKillswitchResponse(rsp *http.Response) (*DisengageKillswitchResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DisengageKillswitchResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetKillswitchResponse parses an HTTP response from a GetKillswitchWithResponse call +func ParseGetKillswitchResponse(rsp *http.Response) (*GetKillswitchResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetKillswitchResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest KillswitchAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest KillswitchAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseEngageKillswitchResponse parses an HTTP response from a EngageKillswitchWithResponse call +func ParseEngageKillswitchResponse(rsp *http.Response) (*EngageKillswitchResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &EngageKillswitchResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetLicenseSummaryResponse parses an HTTP response from a GetLicenseSummaryWithResponse call +func ParseGetLicenseSummaryResponse(rsp *http.Response) (*GetLicenseSummaryResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetLicenseSummaryResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetLicenseSummaryAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest GetLicenseSummaryAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetReportResponse parses an HTTP response from a GetReportWithResponse call +func ParseGetReportResponse(rsp *http.Response) (*GetReportResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetReportResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetPreflightActionSummaryResponse parses an HTTP response from a GetPreflightActionSummaryWithResponse call +func ParseGetPreflightActionSummaryResponse(rsp *http.Response) (*GetPreflightActionSummaryResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPreflightActionSummaryResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PreflightActionSummaryAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PreflightActionSummaryAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetAssociationsResponse parses an HTTP response from a GetAssociationsWithResponse call +func ParseGetAssociationsResponse(rsp *http.Response) (*GetAssociationsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetAssociationsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOPropertyAssociationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOPropertyAssociationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertPropertyAssociationResponse parses an HTTP response from a UpsertPropertyAssociationWithResponse call +func ParseUpsertPropertyAssociationResponse(rsp *http.Response) (*UpsertPropertyAssociationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertPropertyAssociationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PropertyAssociationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PropertyAssociationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest PropertyAssociationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest PropertyAssociationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 409: + break // No content-type + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeletePropertyAssociationResponse parses an HTTP response from a DeletePropertyAssociationWithResponse call +func ParseDeletePropertyAssociationResponse(rsp *http.Response) (*DeletePropertyAssociationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeletePropertyAssociationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetPropertyDefinition1Response parses an HTTP response from a GetPropertyDefinition1WithResponse call +func ParseGetPropertyDefinition1Response(rsp *http.Response) (*GetPropertyDefinition1Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPropertyDefinition1Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PropertyAssociationAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PropertyAssociationAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetPropertyDefinitionsResponse parses an HTTP response from a GetPropertyDefinitionsWithResponse call +func ParseGetPropertyDefinitionsResponse(rsp *http.Response) (*GetPropertyDefinitionsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPropertyDefinitionsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOPropertyDefinitionAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOPropertyDefinitionAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertPropertyDefinitionResponse parses an HTTP response from a UpsertPropertyDefinitionWithResponse call +func ParseUpsertPropertyDefinitionResponse(rsp *http.Response) (*UpsertPropertyDefinitionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertPropertyDefinitionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PropertyDefinitionAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PropertyDefinitionAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest PropertyDefinitionAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest PropertyDefinitionAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 409: + break // No content-type + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeletePropertyDefinitionResponse parses an HTTP response from a DeletePropertyDefinitionWithResponse call +func ParseDeletePropertyDefinitionResponse(rsp *http.Response) (*DeletePropertyDefinitionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeletePropertyDefinitionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetPropertyDefinitionResponse parses an HTTP response from a GetPropertyDefinitionWithResponse call +func ParseGetPropertyDefinitionResponse(rsp *http.Response) (*GetPropertyDefinitionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPropertyDefinitionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PropertyDefinitionAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PropertyDefinitionAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetEnvironmentCountsResponse parses an HTTP response from a GetEnvironmentCountsWithResponse call +func ParseGetEnvironmentCountsResponse(rsp *http.Response) (*GetEnvironmentCountsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetEnvironmentCountsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetExperimentCreationsResponse parses an HTTP response from a GetExperimentCreationsWithResponse call +func ParseGetExperimentCreationsResponse(rsp *http.Response) (*GetExperimentCreationsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentCreationsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetExperimentExecutionsResponse parses an HTTP response from a GetExperimentExecutionsWithResponse call +func ParseGetExperimentExecutionsResponse(rsp *http.Response) (*GetExperimentExecutionsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetExperimentExecutionsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetAverageRiskResponse parses an HTTP response from a GetAverageRiskWithResponse call +func ParseGetAverageRiskResponse(rsp *http.Response) (*GetAverageRiskResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetAverageRiskResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetRiskByCategoryResponse parses an HTTP response from a GetRiskByCategoryWithResponse call +func ParseGetRiskByCategoryResponse(rsp *http.Response) (*GetRiskByCategoryResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetRiskByCategoryResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetRiskDistributionResponse parses an HTTP response from a GetRiskDistributionWithResponse call +func ParseGetRiskDistributionResponse(rsp *http.Response) (*GetRiskDistributionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetRiskDistributionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetTeamCountsResponse parses an HTTP response from a GetTeamCountsWithResponse call +func ParseGetTeamCountsResponse(rsp *http.Response) (*GetTeamCountsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTeamCountsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetUserCountsResponse parses an HTTP response from a GetUserCountsWithResponse call +func ParseGetUserCountsResponse(rsp *http.Response) (*GetUserCountsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetUserCountsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TimeSeriesReportAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetServiceListResponse parses an HTTP response from a GetServiceListWithResponse call +func ParseGetServiceListResponse(rsp *http.Response) (*GetServiceListResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetServiceListResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOServiceSummaryAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOServiceSummaryAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertServiceResponse parses an HTTP response from a UpsertServiceWithResponse call +func ParseUpsertServiceResponse(rsp *http.Response) (*UpsertServiceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertServiceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ServiceAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ServiceAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest ServiceAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest ServiceAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 409: + break // No content-type + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseGetProfilesResponse parses an HTTP response from a GetProfilesWithResponse call +func ParseGetProfilesResponse(rsp *http.Response) (*GetProfilesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetProfilesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOServiceProfileAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOServiceProfileAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertProfileResponse parses an HTTP response from a UpsertProfileWithResponse call +func ParseUpsertProfileResponse(rsp *http.Response) (*UpsertProfileResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertProfileResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ServiceProfileAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ServiceProfileAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest ServiceProfileAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest ServiceProfileAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 409: + break // No content-type + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteProfileResponse parses an HTTP response from a DeleteProfileWithResponse call +func ParseDeleteProfileResponse(rsp *http.Response) (*DeleteProfileResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteProfileResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetProfileResponse parses an HTTP response from a GetProfileWithResponse call +func ParseGetProfileResponse(rsp *http.Response) (*GetProfileResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetProfileResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ServiceProfileAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ServiceProfileAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteServiceResponse parses an HTTP response from a DeleteServiceWithResponse call +func ParseDeleteServiceResponse(rsp *http.Response) (*DeleteServiceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteServiceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetServiceResponse parses an HTTP response from a GetServiceWithResponse call +func ParseGetServiceResponse(rsp *http.Response) (*GetServiceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetServiceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ServiceAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ServiceAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetServiceExperimentsResponse parses an HTTP response from a GetServiceExperimentsWithResponse call +func ParseGetServiceExperimentsResponse(rsp *http.Response) (*GetServiceExperimentsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetServiceExperimentsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOServiceExperimentAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOServiceExperimentAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUnlinkCustomExperimentResponse parses an HTTP response from a UnlinkCustomExperimentWithResponse call +func ParseUnlinkCustomExperimentResponse(rsp *http.Response) (*UnlinkCustomExperimentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UnlinkCustomExperimentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseLinkCustomExperimentResponse parses an HTTP response from a LinkCustomExperimentWithResponse call +func ParseLinkCustomExperimentResponse(rsp *http.Response) (*LinkCustomExperimentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &LinkCustomExperimentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseUpsertProvidedExperimentResponse parses an HTTP response from a UpsertProvidedExperimentWithResponse call +func ParseUpsertProvidedExperimentResponse(rsp *http.Response) (*UpsertProvidedExperimentResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertProvidedExperimentResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetRiskResponse parses an HTTP response from a GetRiskWithResponse call +func ParseGetRiskResponse(rsp *http.Response) (*GetRiskResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetRiskResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ServiceRiskAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest ServiceRiskAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetServiceVariablesResponse parses an HTTP response from a GetServiceVariablesWithResponse call +func ParseGetServiceVariablesResponse(rsp *http.Response) (*GetServiceVariablesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetServiceVariablesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest string + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest string + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseMergeServiceVariablesResponse parses an HTTP response from a MergeServiceVariablesWithResponse call +func ParseMergeServiceVariablesResponse(rsp *http.Response) (*MergeServiceVariablesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &MergeServiceVariablesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseSetServiceVariablesResponse parses an HTTP response from a SetServiceVariablesWithResponse call +func ParseSetServiceVariablesResponse(rsp *http.Response) (*SetServiceVariablesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SetServiceVariablesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetTargetsStatsResponse parses an HTTP response from a GetTargetsStatsWithResponse call +func ParseGetTargetsStatsResponse(rsp *http.Response) (*GetTargetsStatsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTargetsStatsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest map[string]int64 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest map[string]int64 + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetTargetsStats1Response parses an HTTP response from a GetTargetsStats1WithResponse call +func ParseGetTargetsStats1Response(rsp *http.Response) (*GetTargetsStats1Response, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTargetsStats1Response{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest map[string]int64 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest map[string]int64 + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseGetTargetsResponse parses an HTTP response from a GetTargetsWithResponse call +func ParseGetTargetsResponse(rsp *http.Response) (*GetTargetsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTargetsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest CursorSliceResponseAOTargetAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest CursorSliceResponseAOTargetAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetTargetAttributeKeysResponse parses an HTTP response from a GetTargetAttributeKeysWithResponse call +func ParseGetTargetAttributeKeysResponse(rsp *http.Response) (*GetTargetAttributeKeysResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTargetAttributeKeysResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOString + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOString + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetTargetAttributeValuesResponse parses an HTTP response from a GetTargetAttributeValuesWithResponse call +func ParseGetTargetAttributeValuesResponse(rsp *http.Response) (*GetTargetAttributeValuesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTargetAttributeValuesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PagedResponseAOString + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest PagedResponseAOString + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetTeamsResponse parses an HTTP response from a GetTeamsWithResponse call +func ParseGetTeamsResponse(rsp *http.Response) (*GetTeamsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTeamsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamSummariesAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamSummariesAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + } + + return response, nil +} + +// ParseUpsertTeamResponse parses an HTTP response from a UpsertTeamWithResponse call +func ParseUpsertTeamResponse(rsp *http.Response) (*UpsertTeamResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpsertTeamResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest TeamAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 201: + var dest TeamAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML201 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseDeleteTeamResponse parses an HTTP response from a DeleteTeamWithResponse call +func ParseDeleteTeamResponse(rsp *http.Response) (*DeleteTeamResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteTeamResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest TeamAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 403: + var dest TeamAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML403 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetTeamResponse parses an HTTP response from a GetTeamWithResponse call +func ParseGetTeamResponse(rsp *http.Response) (*GetTeamResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTeamResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseGetTeamEnvironmentsResponse parses an HTTP response from a GetTeamEnvironmentsWithResponse call +func ParseGetTeamEnvironmentsResponse(rsp *http.Response) (*GetTeamEnvironmentsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTeamEnvironmentsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamEnvironmentsAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamEnvironmentsAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseSetTeamEnvironmentsResponse parses an HTTP response from a SetTeamEnvironmentsWithResponse call +func ParseSetTeamEnvironmentsResponse(rsp *http.Response) (*SetTeamEnvironmentsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SetTeamEnvironmentsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamEnvironmentsAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamEnvironmentsAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseAddTeamEnvironmentsResponse parses an HTTP response from a AddTeamEnvironmentsWithResponse call +func ParseAddTeamEnvironmentsResponse(rsp *http.Response) (*AddTeamEnvironmentsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &AddTeamEnvironmentsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamEnvironmentsAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamEnvironmentsAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseRemoveTeamEnvironmentsResponse parses an HTTP response from a RemoveTeamEnvironmentsWithResponse call +func ParseRemoveTeamEnvironmentsResponse(rsp *http.Response) (*RemoveTeamEnvironmentsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RemoveTeamEnvironmentsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamEnvironmentsAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamEnvironmentsAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseGetTeamMembersResponse parses an HTTP response from a GetTeamMembersWithResponse call +func ParseGetTeamMembersResponse(rsp *http.Response) (*GetTeamMembersResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTeamMembersResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamMembersAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamMembersAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 404: + break // No content-type + + } + + return response, nil +} + +// ParseSetTeamMembersResponse parses an HTTP response from a SetTeamMembersWithResponse call +func ParseSetTeamMembersResponse(rsp *http.Response) (*SetTeamMembersResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SetTeamMembersResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamMembersAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamMembersAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseAddTeamMembersResponse parses an HTTP response from a AddTeamMembersWithResponse call +func ParseAddTeamMembersResponse(rsp *http.Response) (*AddTeamMembersResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &AddTeamMembersResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamMembersAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamMembersAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseRemoveTeamMembersResponse parses an HTTP response from a RemoveTeamMembersWithResponse call +func ParseRemoveTeamMembersResponse(rsp *http.Response) (*RemoveTeamMembersResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RemoveTeamMembersResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TeamMembersAO + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "yaml") && rsp.StatusCode == 200: + var dest TeamMembersAO + if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.YAML200 = &dest + + case rsp.StatusCode == 422: + break // No content-type + + } + + return response, nil +} + +// ParseInviteUserResponse parses an HTTP response from a InviteUserWithResponse call +func ParseInviteUserResponse(rsp *http.Response) (*InviteUserResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &InviteUserResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} diff --git a/cmd/steadybit/main.go b/cmd/steadybit/main.go new file mode 100644 index 0000000..89189d8 --- /dev/null +++ b/cmd/steadybit/main.go @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package main + +import ( + "os" + + "github.com/steadybit/cli/internal/cli" +) + +func main() { + os.Exit(cli.Execute()) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..429f53d --- /dev/null +++ b/go.mod @@ -0,0 +1,37 @@ +module github.com/steadybit/cli + +go 1.26.2 + +tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen + +require ( + github.com/oapi-codegen/runtime v1.7.0 + github.com/spf13/cobra v1.10.2 + github.com/stretchr/testify v1.12.1 + go.yaml.in/yaml/v3 v3.0.5 + golang.org/x/term v0.46.0 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect + github.com/getkin/kin-openapi v0.142.0 // indirect + github.com/go-openapi/jsonpointer v0.23.1 // indirect + github.com/go-openapi/swag/jsonname v0.26.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/oapi-codegen/oapi-codegen/v2 v2.8.0 // indirect + github.com/oasdiff/yaml v0.1.1 // indirect + github.com/oasdiff/yaml3 v0.0.14 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect + github.com/speakeasy-api/jsonpath v0.6.3 // indirect + github.com/speakeasy-api/openapi v1.24.0 // indirect + github.com/spf13/pflag v1.0.9 // indirect + github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.48.0 // indirect + golang.org/x/text v0.40.0 // indirect + golang.org/x/tools v0.48.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f167d36 --- /dev/null +++ b/go.sum @@ -0,0 +1,191 @@ +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= +github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= +github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w= +github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.142.0 h1:izj0vBdFprMhitfzaX8sTqztsEQyvwhssBoB6n8NO7w= +github.com/getkin/kin-openapi v0.142.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= +github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= +github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= +github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w= +github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M= +github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= +github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/nullable v1.1.0 h1:eAh8JVc5430VtYVnq00Hrbpag9PFRGWLjxR1/3KntMs= +github.com/oapi-codegen/nullable v1.1.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= +github.com/oapi-codegen/oapi-codegen/v2 v2.8.0 h1:s4hxMxuqtR8jPzXkBTtFwY/SBuj3gEAYikmbBSdtLMM= +github.com/oapi-codegen/oapi-codegen/v2 v2.8.0/go.mod h1:yae2TI9IYB5vxQ35gFrpXh9L5H1eJv4MAUK1jumGMTo= +github.com/oapi-codegen/runtime v1.7.0 h1:t7358VYPvNbWJ9gdAkIK/smVeHpBf6yp8VTsaZsb/7k= +github.com/oapi-codegen/runtime v1.7.0/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= +github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY= +github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU= +github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw= +github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/speakeasy-api/jsonpath v0.6.3 h1:c+QPwzAOdrWvzycuc9HFsIZcxKIaWcNpC+xhOW9rJxU= +github.com/speakeasy-api/jsonpath v0.6.3/go.mod h1:2cXloNuQ+RSXi5HTRaeBh7JEmjRXTiaKpFTdZiL7URI= +github.com/speakeasy-api/openapi v1.24.0 h1:opoD27rupX7zBVPq1HkIGLeMOzNNA7JalhYP8q34i04= +github.com/speakeasy-api/openapi v1.24.0/go.mod h1:g3+dIMe0AYgbbGvnlQZqesmjAVWSm9BmsjLevnefQrg= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= +github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE= +golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cli/config.go b/internal/cli/config.go new file mode 100644 index 0000000..280864e --- /dev/null +++ b/internal/cli/config.go @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/config" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/prompt" +) + +func newConfig() *cobra.Command { + cmd := &cobra.Command{Use: "config", Short: "Show/modify the CLI configuration and authentication profiles."} + profile := &cobra.Command{Use: "profile", Short: "Configure authentication profiles."} + profile.AddCommand(newProfileAdd(), newProfileList("list", "List all configured profiles."), + newProfileList("ls", "Alias for list."), newProfileSelect(), newProfileRemove()) + cmd.AddCommand(profile, &cobra.Command{ + Use: "show", + Short: "Show the active CLI configuration. Warning: Prints secrets!", + Example: examples("steadybit config show"), + RunE: func(*cobra.Command, []string) error { + cfg, err := config.Load() + if err != nil { + return err + } + fmt.Printf("{\n \"apiAccessToken\": %q,\n \"baseUrl\": %q\n}\n", cfg.APIAccessToken, cfg.BaseURL) + return nil + }, + }) + return cmd +} + +const startHelp = `Configuration profiles enable you to use the CLI without repeatedly providing +passwords or having to remember environment variables. Configuration profiles +are stored in ~/.steadybit` + +func newProfileAdd() *cobra.Command { + var p config.Profile + cmd := &cobra.Command{ + Use: "add", + Short: "Configure a new profile (interactively or via options).", + Args: cobra.NoArgs, + Example: examples("steadybit config profile add", `steadybit config profile add -n prod -t "$STEADYBIT_TOKEN"`), + RunE: func(cmd *cobra.Command, _ []string) error { + if p.Name == "" || p.APIAccessToken == "" { + var err error + fmt.Println(startHelp) + fmt.Println() + if p.Name, err = prompt.Input("Profile name:", "", prompt.NotBlank); err != nil { + return err + } + if p.BaseURL, err = prompt.Input("Base URL of the Steadybit server:", config.DefaultBaseURL, prompt.HTTPURL); err != nil { + return err + } + fmt.Printf("\nThe CLI will need an API access token of %s to communicate with\nthe Steadybit servers. You can generate one through the following URL:\n\n %s/settings/api-tokens\n\n", + output.Bold("type team"), strings.TrimSuffix(p.BaseURL, "/")) + if p.APIAccessToken, err = prompt.Password("API access token:", prompt.NotBlank); err != nil { + return err + } + } + if err := config.AddProfile(p); err != nil { + return err + } + fmt.Printf("\n%s You can now start using the CLI. For example, you could start\nto run your first experiment via:\n\n %s\n", + output.Green("Done!"), output.Bold("steadybit experiment run -k ")) + return nil + }, + } + cmd.Flags().StringVarP(&p.Name, "name", "n", "", "Name of the profile") + cmd.Flags().StringVarP(&p.BaseURL, "baseUrl", "b", config.DefaultBaseURL, "Base URL to be used") + cmd.Flags().StringVarP(&p.APIAccessToken, "token", "t", "", "Team API token") + return cmd +} + +func newProfileList(use, short string) *cobra.Command { + return &cobra.Command{ + Use: use, + Short: short, + Args: cobra.NoArgs, + Example: examples("steadybit config profile " + use), + RunE: func(*cobra.Command, []string) error { + profiles, err := config.Profiles() + if err != nil { + return err + } + active, err := config.ActiveProfile() + if err != nil { + return err + } + for _, p := range profiles { + if active != nil && p.Name == active.Name { + fmt.Printf("* %s\n", output.Green(p.Name)) + } else { + fmt.Printf(" %s\n", p.Name) + } + } + return nil + }, + } +} + +func chooseProfile(message string, args []string) (string, error) { + if len(args) == 1 { + return args[0], nil + } + profiles, err := config.Profiles() + if err != nil { + return "", err + } + if len(profiles) == 0 { + return "", fmt.Errorf("no profiles configured") + } + for i, p := range profiles { + fmt.Printf(" %d) %s\n", i+1, p.Name) + } + answer, err := prompt.Input(message, "", func(v string) error { + for i, p := range profiles { + if v == p.Name || v == fmt.Sprint(i+1) { + return nil + } + } + return fmt.Errorf("choose one of the profiles above") + }) + if err != nil { + return "", err + } + for i, p := range profiles { + if answer == fmt.Sprint(i+1) { + return p.Name, nil + } + } + return answer, nil +} + +func newProfileSelect() *cobra.Command { + return &cobra.Command{ + Use: "select [name]", + Short: "Interactively change the currently active profile.", + Args: cobra.MaximumNArgs(1), + Example: examples("steadybit config profile select", "steadybit config profile select prod"), + RunE: func(_ *cobra.Command, args []string) error { + name, err := chooseProfile("Profile to activate:", args) + if err != nil { + return err + } + return config.SetActiveProfile(name) + }, + } +} + +func newProfileRemove() *cobra.Command { + return &cobra.Command{ + Use: "remove [name]", + Short: "Interactively remove an existing profile.", + Args: cobra.MaximumNArgs(1), + Example: examples("steadybit config profile remove", "steadybit config profile remove old"), + RunE: func(_ *cobra.Command, args []string) error { + name, err := chooseProfile("Profile to remove:", args) + if err != nil { + return err + } + return config.RemoveProfile(name) + }, + } +} diff --git a/internal/cli/experiment.go b/internal/cli/experiment.go new file mode 100644 index 0000000..ee2aba5 --- /dev/null +++ b/internal/cli/experiment.go @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/experiment" + "github.com/steadybit/cli/internal/platform" +) + +func newExperiment() *cobra.Command { + cmd := &cobra.Command{Use: "experiment", Short: "Check and run experiments."} + cmd.AddCommand(newExperimentRun(), newExperimentGet(), newExperimentApply()) + return cmd +} + +// keyValues is a repeatable KEY=VALUE flag. Only the first `=` separates. +type keyValues map[string]string + +func (k *keyValues) String() string { return "" } +func (k *keyValues) Type() string { return "KEY=VALUE" } +func (k *keyValues) Set(value string) error { + i := strings.Index(value, "=") + if i <= 0 { + return fmt.Errorf("'%s' is not in the form KEY=VALUE", value) + } + if *k == nil { + *k = keyValues{} + } + (*k)[value[:i]] = value[i+1:] + return nil +} + +func newExperimentRun() *cobra.Command { + var o experiment.RunOptions + var noWait bool + placeholders := keyValues{} + cmd := &cobra.Command{ + Use: "run", + Aliases: []string{"exec"}, + Short: "Executes an experiment run. If a file is specified the experiment is saved before execution.", + Args: cobra.NoArgs, + Example: examples( + "steadybit experiment run -k ADM-1", + "steadybit experiment run -f experiment.yml --no-wait", + "steadybit experiment run --template d7e65100-1d20-4980-be87-c351704910b8 --team ADM -p CLUSTER=prod", + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + o.Wait = !noWait + o.Placeholder = placeholders + return experiment.Run(ctx, c, o) + }), + } + f := cmd.Flags() + f.StringVarP(&o.Key, "key", "k", "", "The experiment key.") + f.StringSliceVarP(&o.Files, "file", "f", nil, "The path to the experiment file or a directory containing multiple files.") + f.BoolVarP(&o.Recursive, "recursive", "R", false, "Process the directory used in -f, --file recursively.") + f.BoolVar(&noWait, "no-wait", false, "Do not wait for experiment run to finish.") + f.BoolVar(&o.Yes, "yes", false, "Skip the prompt asking for experiment run confirmation. Not necessary when no TTY is attached.") + f.BoolVar(&o.AllowParallel, "allowParallel", false, "Skip the prompt warning about another experiment running and allow always parallel execution.") + f.IntVar(&o.Retries, "retries", 0, "Number of retries when the experiment fails validation (e.g., missing targets). 0 means no retry.") + f.IntVar(&o.RetryInterval, "retryInterval", 10, "Interval in seconds between retries.") + f.StringVar(&o.Template, "template", "", "Create the experiment from the experiment template with this id.") + f.StringVar(&o.Team, "team", "", "With --template: the key of the team owning the experiment.") + f.StringVar(&o.Environment, "environment", "", "With --template: the environment the experiment runs in.") + f.StringVar(&o.ExternalID, "external-id", "", "With --template: an identifier of your own; reusing it updates the experiment.") + f.VarP(&placeholders, "placeholder", "p", "With --template: a placeholder value. Repeat for more.") + cmd.MarkFlagsMutuallyExclusive("key", "file") + cmd.MarkFlagsMutuallyExclusive("template", "file") + return cmd +} + +func newExperimentGet() *cobra.Command { + var o experiment.GetOptions + cmd := &cobra.Command{ + Use: "get", + Short: "Get an experiment from Steadybit. Output is written to file or stdout.", + Args: cobra.NoArgs, + Example: examples("steadybit experiment get -k ADM-1", "steadybit experiment get -k ADM-1 -f experiment.json"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return experiment.Get(ctx, c, o) + }), + } + cmd.Flags().StringVarP(&o.Key, "key", "k", "", "The experiment key.") + cmd.Flags().StringVarP(&o.File, "file", "f", "", "The path to the experiment file.") + cmd.Flags().StringVarP(&o.Type, "type", "t", "", `The output format of the experiment ("json" or "yaml"). (default: "json" if the file ends in ".json", "yaml" otherwise.)`) + _ = cmd.MarkFlagRequired("key") + return cmd +} + +func newExperimentApply() *cobra.Command { + var o experiment.ApplyOptions + cmd := &cobra.Command{ + Use: "apply", + Short: "Upload an experiment to Steadybit. If a key is provided, an update is performed. Otherwise, the externalId from the file is used to create or update the experiment.", + Args: cobra.NoArgs, + Example: examples("steadybit experiment apply -f experiment.yml", "steadybit experiment apply -f ./experiments -R"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return experiment.Apply(ctx, c, o) + }), + } + cmd.Flags().StringVarP(&o.Key, "key", "k", "", "The experiment key.") + cmd.Flags().StringSliceVarP(&o.Files, "file", "f", nil, "The path to the experiment file or a directory containing multiple files.") + cmd.Flags().BoolVarP(&o.Recursive, "recursive", "R", false, "Process the directory used in -f, --file recursively.") + _ = cmd.MarkFlagRequired("file") + return cmd +} diff --git a/internal/cli/root.go b/internal/cli/root.go new file mode 100644 index 0000000..db9572e --- /dev/null +++ b/internal/cli/root.go @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package cli wires the commands. Names, flags, messages and exit codes follow the +// TypeScript CLI, which pipelines depend on. +package cli + +import ( + "context" + "errors" + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" +) + +// Laid out like the TypeScript CLI's help, which pipelines and the e2e suite read. +const usageTemplate = `Usage: {{if .Runnable}}{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}{{.CommandPath}} [command]{{end}} +{{if .HasAvailableLocalFlags}} +Options: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableSubCommands}} + +Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasExample}} + +Examples: +{{.Example}}{{end}} +` + +func examples(lines ...string) string { + for i, line := range lines { + lines[i] = " $ " + line + } + return strings.Join(lines, "\n") +} + +// withClient runs a command that talks to the platform. A missing access token is +// reported with the setup help before anything is sent. +func withClient(run func(ctx context.Context, c *platform.Client, args []string) error) func(*cobra.Command, []string) error { + return func(cmd *cobra.Command, args []string) error { + client, err := platform.New() + if err != nil { + return err + } + return run(cmd.Context(), client, args) + } +} + +func newRoot() *cobra.Command { + root := &cobra.Command{ + Use: "steadybit", + Short: "Command-line interface to interact with the Steadybit API", + Version: platform.Version, + SilenceUsage: true, + SilenceErrors: true, + Example: examples( + "steadybit experiment run -f experiment.yml", + "steadybit experiment --help", + ), + PersistentPreRun: func(cmd *cobra.Command, _ []string) { + platform.Verbose, _ = cmd.Flags().GetBool("verbose") + }, + } + root.PersistentFlags().BoolP("verbose", "v", false, "Enable verbose logging") + root.Flags().BoolP("version", "V", false, "output the version number") + root.SetVersionTemplate("{{.Version}}\n") + root.AddCommand(newConfig(), newExperiment()) + for _, cmd := range append(root.Commands(), root) { + setUsage(cmd) + } + return root +} + +func setUsage(cmd *cobra.Command) { + cmd.SetUsageTemplate(usageTemplate) + for _, sub := range cmd.Commands() { + setUsage(sub) + } +} + +func Execute() int { + err := newRoot().ExecuteContext(context.Background()) + if err == nil { + return 0 + } + if errors.Is(err, platform.ErrNoAccessToken) { + fmt.Fprintln(os.Stderr, platform.MissingTokenHelp()) + } else { + fmt.Fprintln(os.Stderr, output.Red(err.Error())) + } + return 1 +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..d9fd267 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package config reads and writes the CLI configuration exactly as the TypeScript CLI +// did, so that an upgrade keeps every profile a user has already set up. +package config + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "strings" +) + +const DefaultBaseURL = "https://platform.steadybit.com" + +type Profile struct { + Name string `json:"name"` + APIAccessToken string `json:"apiAccessToken"` + BaseURL string `json:"baseUrl,omitempty"` +} + +type Configuration struct { + APIAccessToken string + BaseURL string +} + +func dir() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, ".steadybit"), nil +} + +func file(name string) (string, error) { + d, err := dir() + if err != nil { + return "", err + } + if err := os.MkdirAll(d, 0o755); err != nil { + return "", err + } + return filepath.Join(d, name), nil +} + +func Profiles() ([]Profile, error) { + path, err := file("profiles.json") + if err != nil { + return nil, err + } + content, err := os.ReadFile(path) + if errors.Is(err, os.ErrNotExist) { + return []Profile{}, nil + } + if err != nil { + return nil, fmt.Errorf("failed to read file '%s': %w", path, err) + } + var profiles []Profile + if err := json.Unmarshal(content, &profiles); err != nil { + return nil, fmt.Errorf("failed to parse file '%s' as JSON: %w", path, err) + } + return profiles, nil +} + +func writeProfiles(profiles []Profile) error { + path, err := file("profiles.json") + if err != nil { + return err + } + content, err := json.MarshalIndent(profiles, "", " ") + if err != nil { + return err + } + // The file holds access tokens. + return os.WriteFile(path, content, 0o600) +} + +func AddProfile(profile Profile) error { + profiles, err := Profiles() + if err != nil { + return err + } + kept := profiles[:0] + for _, p := range profiles { + if p.Name != profile.Name { + kept = append(kept, p) + } + } + return writeProfiles(append(kept, profile)) +} + +func RemoveProfile(name string) error { + profiles, err := Profiles() + if err != nil { + return err + } + kept := profiles[:0] + for _, p := range profiles { + if p.Name != name { + kept = append(kept, p) + } + } + return writeProfiles(kept) +} + +func activeProfileName() (string, error) { + path, err := file("activeProfile") + if err != nil { + return "", err + } + content, err := os.ReadFile(path) + if errors.Is(err, os.ErrNotExist) { + return "", nil + } + if err != nil { + return "", fmt.Errorf("failed to read file '%s': %w", path, err) + } + return strings.TrimSpace(string(content)), nil +} + +// ActiveProfile is the selected profile, or the first one when none is selected. +func ActiveProfile() (*Profile, error) { + profiles, err := Profiles() + if err != nil || len(profiles) == 0 { + return nil, err + } + name, err := activeProfileName() + if err != nil { + return nil, err + } + for i := range profiles { + if profiles[i].Name == name { + return &profiles[i], nil + } + } + return &profiles[0], nil +} + +func SetActiveProfile(name string) error { + path, err := file("activeProfile") + if err != nil { + return err + } + return os.WriteFile(path, []byte(name), 0o644) +} + +// Load resolves the configuration: environment variables win over the active profile. +// An empty STEADYBIT_TOKEN counts as set, as it did in the TypeScript CLI, so that a +// pipeline can deliberately blank it out. +func Load() (Configuration, error) { + cfg := Configuration{BaseURL: DefaultBaseURL} + profile, err := ActiveProfile() + if err != nil { + return cfg, err + } + if profile != nil { + cfg.APIAccessToken = profile.APIAccessToken + if profile.BaseURL != "" { + cfg.BaseURL = profile.BaseURL + } + } + if token, ok := os.LookupEnv("STEADYBIT_TOKEN"); ok { + cfg.APIAccessToken = token + } + if url, ok := os.LookupEnv("STEADYBIT_URL"); ok { + cfg.BaseURL = url + } + cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/") + return cfg, nil +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 0000000..349ae1c --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package config + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Profiles written by the TypeScript CLI must keep working after the upgrade. +func TestReadsProfilesWrittenByTheTypeScriptCLI(t *testing.T) { + home := t.TempDir() + t.Setenv("HOME", home) + require.NoError(t, os.MkdirAll(filepath.Join(home, ".steadybit"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(home, ".steadybit", "profiles.json"), []byte(`[ + {"name": "prod", "apiAccessToken": "p", "baseUrl": "https://platform.steadybit.com"}, + {"name": "dev", "apiAccessToken": "d", "baseUrl": "https://platform.dev.steadybit.com/"} +]`), 0o600)) + require.NoError(t, os.WriteFile(filepath.Join(home, ".steadybit", "activeProfile"), []byte("dev\n"), 0o644)) + os.Unsetenv("STEADYBIT_TOKEN") + os.Unsetenv("STEADYBIT_URL") + + cfg, err := Load() + + require.NoError(t, err) + assert.Equal(t, Configuration{APIAccessToken: "d", BaseURL: "https://platform.dev.steadybit.com"}, cfg) +} + +func TestEnvironmentWinsAndAnEmptyTokenCounts(t *testing.T) { + t.Setenv("HOME", t.TempDir()) + require.NoError(t, AddProfile(Profile{Name: "p", APIAccessToken: "from-profile"})) + t.Setenv("STEADYBIT_TOKEN", "") + t.Setenv("STEADYBIT_URL", "http://localhost:8080") + + cfg, err := Load() + + require.NoError(t, err) + assert.Equal(t, "", cfg.APIAccessToken) + assert.Equal(t, "http://localhost:8080", cfg.BaseURL) +} + +func TestFallsBackToTheFirstProfile(t *testing.T) { + t.Setenv("HOME", t.TempDir()) + require.NoError(t, AddProfile(Profile{Name: "a", APIAccessToken: "1"})) + require.NoError(t, AddProfile(Profile{Name: "b", APIAccessToken: "2"})) + + active, err := ActiveProfile() + + require.NoError(t, err) + assert.Equal(t, "a", active.Name) +} diff --git a/internal/experiment/experiment.go b/internal/experiment/experiment.go new file mode 100644 index 0000000..10740a1 --- /dev/null +++ b/internal/experiment/experiment.go @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package experiment implements `experiment get`, `apply` and `run`. +// +// Experiment designs pass through as documents rather than generated structs: decoding +// a file into typed Go values and encoding it again would drop fields the spec does not +// know yet and rewrite zero values, silently changing files kept in Git. The generated +// client still types every path, parameter and the smaller request bodies. +package experiment + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/fs" + "net/http" + "os" + "path/filepath" + "strings" + "time" + + openapi_types "github.com/oapi-codegen/runtime/types" + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/prompt" +) + +type Document = *output.Document + +const anotherExperimentRunning = "https://steadybit.com/problems/another-experiment-running-exception" + +func read(resp *http.Response, err error) ([]byte, *http.Response, error) { + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, resp, err + } + return body, resp, platform.Check(resp, body) +} + +func jsonBody(document any) (io.Reader, error) { + b, err := json.Marshal(document) + return bytes.NewReader(b), err +} + +func isStatus(err error, status int) bool { + var apiErr *platform.APIError + return errors.As(err, &apiErr) && apiErr.Status == status +} + +func Fetch(ctx context.Context, c *platform.Client, key string) (Document, error) { + body, _, err := read(c.GetExperiment(ctx, key)) + if isStatus(err, http.StatusNotFound) { + return nil, fmt.Errorf("Experiment %s not found.", key) + } + if err != nil { + return nil, fmt.Errorf("Failed to get the experiment. HTTP request failed: %w", err) + } + document, err := output.ParseDocument(body) + if err != nil { + return nil, err + } + // Removed because it makes files awkward to reapply; the API will drop it too. + document.Delete("version") + return document, nil +} + +type GetOptions struct { + Key, File, Type string +} + +func Get(ctx context.Context, c *platform.Client, o GetOptions) error { + document, err := Fetch(ctx, c, o.Key) + if err != nil { + return err + } + datatype, err := output.ResolveDatatype(o.Type, o.File) + if err != nil { + return err + } + rendered, err := document.Render(datatype) + if err != nil { + return err + } + if o.File == "" { + // As console.log printed it: YAML already ends in a newline and gets another. + fmt.Print(string(rendered)) + if datatype == output.YAML { + fmt.Println() + } + return nil + } + if datatype == output.JSON { + // Files were written as JSON.stringify left them, on a single line. + var compact bytes.Buffer + if err := json.Compact(&compact, rendered); err != nil { + return err + } + rendered = compact.Bytes() + } + if err := os.WriteFile(o.File, rendered, 0o644); err != nil { + return err + } + fmt.Printf("Experiment %s written to %s.\n", o.Key, o.File) + return nil +} + +// ResolveFiles expands directories into their YAML files, recursively on request. +func ResolveFiles(paths []string, recursive bool) ([]string, error) { + var files []string + for _, path := range paths { + info, err := os.Stat(path) + if errors.Is(err, fs.ErrNotExist) { + return nil, fmt.Errorf("File or directory '%s' not found.", path) + } + if err != nil { + return nil, err + } + if !info.IsDir() { + files = append(files, path) + continue + } + entries, err := os.ReadDir(path) + if err != nil { + return nil, err + } + var dirs []string + for _, entry := range entries { + name := strings.ToLower(entry.Name()) + switch { + case entry.IsDir(): + dirs = append(dirs, filepath.Join(path, entry.Name())) + case strings.HasSuffix(name, ".yaml") || strings.HasSuffix(name, ".yml"): + files = append(files, filepath.Join(path, entry.Name())) + } + } + if recursive && len(dirs) > 0 { + nested, err := ResolveFiles(dirs, recursive) + if err != nil { + return nil, err + } + files = append(files, nested...) + } + } + return files, nil +} + +func load(file string) (Document, output.Datatype, error) { + content, err := os.ReadFile(file) + if err != nil { + return nil, "", fmt.Errorf("Failed to read experiment file at path '%s': %w", file, err) + } + document, err := output.ParseDocument(content) + if err != nil { + return nil, "", fmt.Errorf("Failed to parse experiment file at path '%s' as YAML/JSON: %w", file, err) + } + datatype := output.YAML + if json.Valid(content) { + datatype = output.JSON + } + return document, datatype, nil +} + +// writeBack puts the key the platform assigned at the top of the file, so that the next +// apply updates this experiment instead of creating another one. +func writeBack(file string, document Document, datatype output.Datatype, key string) error { + content, err := os.ReadFile(file) + if err != nil { + return err + } + var rendered []byte + if datatype == output.YAML { + // Prepending keeps the rest of the file byte for byte, comments and anchors included. + rendered = append([]byte("key: "+key+"\n"), content...) + } else { + document.SetFirst("key", key) + if rendered, err = document.Render(datatype); err != nil { + return err + } + } + return os.WriteFile(file, rendered, 0o644) +} + +func keyOf(document Document) string { + key, _ := document.Get("key") + return key +} + +func keyFromLocation(resp *http.Response) string { + location := resp.Header.Get("Location") + return location[strings.LastIndex(location, "/")+1:] +} + +func update(ctx context.Context, c *platform.Client, key string, document Document) error { + body, err := jsonBody(document) + if err != nil { + return err + } + _, _, err = read(c.UpdateExperimentWithBody(ctx, key, "application/json", body)) + if isStatus(err, http.StatusNotFound) { + return fmt.Errorf("Experiment %s not found.", key) + } + if err != nil { + return fmt.Errorf("Failed to save the experiment. HTTP request failed: %w", err) + } + return nil +} + +type ApplyOptions struct { + Key string + Files []string + Recursive bool +} + +func Apply(ctx context.Context, c *platform.Client, o ApplyOptions) error { + files, err := ResolveFiles(o.Files, o.Recursive) + if err != nil { + return err + } + if o.Key != "" && len(files) > 1 { + return errors.New("If --key is specified, at most one --file can be specified.") + } + for _, file := range files { + document, datatype, err := load(file) + if err != nil { + return err + } + key := o.Key + if key == "" { + key = keyOf(document) + } + if key != "" { + if err := update(ctx, c, key, document); err != nil { + return err + } + fmt.Printf("Experiment %s updated.\n", key) + continue + } + body, err := jsonBody(document) + if err != nil { + return err + } + _, resp, err := read(c.CreateOrUpdateExperimentWithBody(ctx, "application/json", body)) + if err != nil { + return fmt.Errorf("Failed to save the experiment. HTTP request failed: %w", err) + } + key = keyFromLocation(resp) + if resp.StatusCode == http.StatusCreated { + if err := writeBack(file, document, datatype, key); err != nil { + return err + } + fmt.Printf("Experiment %s created.\n", key) + } else { + fmt.Printf("Experiment %s updated.\n", key) + } + } + return nil +} + +type RunOptions struct { + Key string + Files []string + Recursive bool + Yes, Wait bool + AllowParallel bool + Retries int + RetryInterval int + + Template string + Team string + Environment string + ExternalID string + Placeholder map[string]string +} + +type started struct { + Key, APILocation, UILocation string +} + +func Run(ctx context.Context, c *platform.Client, o RunOptions) error { + if !o.Yes { + ok, err := prompt.Confirm("Are you sure you want to run the experiment?", false, true) + if err != nil { + return err + } + if !ok { + os.Exit(0) + } + } + + persist := o.Retries == 0 + runs := []func(parallel bool) (started, error){} + switch { + case o.Template != "" && o.Key != "": + return errors.New("--key cannot be combined with --template. Use `experiment apply --template -k` to update it.") + case o.Template != "": + runs = append(runs, func(parallel bool) (started, error) { return runTemplate(ctx, c, o, parallel, persist) }) + case len(o.Files) > 0: + files, err := ResolveFiles(o.Files, o.Recursive) + if err != nil { + return err + } + if o.Key != "" && len(files) > 1 { + return errors.New("If --key is specified, at most one --file can be specified.") + } + for _, file := range files { + runs = append(runs, func(parallel bool) (started, error) { return runFile(ctx, c, o, file, parallel, persist) }) + } + case o.Key != "": + runs = append(runs, func(parallel bool) (started, error) { return runKey(ctx, c, o.Key, parallel, persist) }) + default: + return errors.New("Either --key, --file or --template must be specified.") + } + + for _, run := range runs { + result, err := withRetries(o, run) + if err != nil { + return err + } + fmt.Println("Executing experiment:", result.Key) + fmt.Println("Experiment run API:", result.APILocation) + fmt.Println("Experiment run UI:", result.UILocation) + if o.Wait && result.APILocation != "" { + if err := wait(ctx, c, result.APILocation); err != nil { + return err + } + } + } + return nil +} + +// withRetries retries validation errors, which clear up once targets appear, and offers +// a parallel run when another experiment is already running. +func withRetries(o RunOptions, run func(parallel bool) (started, error)) (started, error) { + parallel := o.AllowParallel + for attempt := 0; ; attempt++ { + result, err := run(parallel) + if err == nil { + return result, nil + } + var apiErr *platform.APIError + if !errors.As(err, &apiErr) { + return result, err + } + if apiErr.Status == http.StatusUnprocessableEntity && attempt < o.Retries { + fmt.Printf("Experiment has validation errors (attempt %d/%d). Retrying in %ds...\n", attempt+1, o.Retries+1, o.RetryInterval) + time.Sleep(time.Duration(o.RetryInterval) * time.Second) + continue + } + if !parallel && apiErr.ProblemType() == anotherExperimentRunning { + ok := o.Yes + if !ok { + if ok, err = prompt.Confirm("There is already an experiment running. Do you want to start it in parallel?", false, false); err != nil { + return result, err + } + } + if ok { + parallel = true + attempt-- + continue + } + } + return result, fmt.Errorf("Failed to execute experiment: %w", err) + } +} + +func decodeStarted(body []byte, resp *http.Response, fallbackKey string) (started, error) { + var r api.ExecuteExperimentResponseAO + if len(body) > 0 { + if err := json.Unmarshal(body, &r); err != nil { + return started{}, err + } + } + s := started{Key: r.Key, APILocation: resp.Header.Get("Location"), UILocation: r.UiLocation} + if s.Key == "" { + s.Key = fallbackKey + } + if s.APILocation == "" { + s.APILocation = r.ApiLocation + } + return s, nil +} + +func runKey(ctx context.Context, c *platform.Client, key string, parallel, persist bool) (started, error) { + body, resp, err := read(c.ExecuteExperimentWithBody(ctx, key, + &api.ExecuteExperimentParams{AllowParallel: ¶llel, ForcePersist: &persist}, "application/json", nil)) + if err != nil { + return started{}, err + } + return decodeStarted(body, resp, key) +} + +func runFile(ctx context.Context, c *platform.Client, o RunOptions, file string, parallel, persist bool) (started, error) { + document, datatype, err := load(file) + if err != nil { + return started{}, err + } + key := o.Key + if key == "" { + key = keyOf(document) + } + if key != "" { + if err := update(ctx, c, key, document); err != nil { + return started{}, err + } + return runKey(ctx, c, key, parallel, persist) + } + reqBody, err := jsonBody(document) + if err != nil { + return started{}, err + } + body, resp, err := read(c.SaveAndRunWithBody(ctx, + &api.SaveAndRunParams{AllowParallel: ¶llel, ForcePersist: &persist}, "application/json", reqBody)) + if err != nil { + return started{}, err + } + result, err := decodeStarted(body, resp, "") + if err != nil { + return started{}, err + } + return result, writeBack(file, document, datatype, result.Key) +} + +func runTemplate(ctx context.Context, c *platform.Client, o RunOptions, parallel, persist bool) (started, error) { + if o.Team == "" { + return started{}, errors.New("--team is required to create an experiment from a template.") + } + var id openapi_types.UUID + if err := id.UnmarshalText([]byte(o.Template)); err != nil { + return started{}, fmt.Errorf("'%s' is not a template id: %w", o.Template, err) + } + placeholders := make([]api.ExperimentTemplatePlaceholderValueAO, 0, len(o.Placeholder)) + for key, value := range o.Placeholder { + placeholders = append(placeholders, api.ExperimentTemplatePlaceholderValueAO{Key: key, Value: value}) + } + request := api.CreateAndRunExperimentFromTemplateAO{Team: o.Team, Placeholders: &placeholders} + if o.Environment != "" { + request.Environment = &o.Environment + } + if o.ExternalID != "" { + request.ExternalId = &o.ExternalID + } + reset := true + body, resp, err := read(c.SaveAndRunFromTemplate(ctx, id, + &api.SaveAndRunFromTemplateParams{ResetProperties: &reset, AllowParallel: ¶llel, ForcePersist: &persist}, request)) + if isStatus(err, http.StatusNotFound) { + return started{}, fmt.Errorf("Experiment template %s not found.", o.Template) + } + if err != nil { + return started{}, err + } + return decodeStarted(body, resp, "") +} + +var terminal = map[string]bool{"FAILED": true, "ERRORED": true, "CANCELED": true, "COMPLETED": true} + +// wait polls the run until it ends. A run that did not complete exits non-zero, which is +// what lets a pipeline fail on it. +func wait(ctx context.Context, c *platform.Client, location string) error { + path := location + if i := strings.Index(location, "/api/"); i >= 0 { + path = location[i:] + } + for { + time.Sleep(5 * time.Second) + body, _, err := read(c.Get(ctx, path)) + if err != nil { + return fmt.Errorf("Failed to get experiment run: %w", err) + } + var run struct { + ID int64 `json:"id"` + Key string `json:"key"` + State string `json:"state"` + Reason string `json:"reason"` + } + if err := json.Unmarshal(body, &run); err != nil { + return err + } + fmt.Println("Current run state:", strings.ToLower(run.State)) + if !terminal[run.State] { + continue + } + if run.State != "COMPLETED" { + reason := "" + if run.Reason != "" { + reason = ", reason: " + run.Reason + } + return fmt.Errorf("Experiment %s (#%d) %s%s", run.Key, run.ID, strings.ToLower(run.State), reason) + } + return nil + } +} diff --git a/internal/output/document.go b/internal/output/document.go new file mode 100644 index 0000000..462cf8e --- /dev/null +++ b/internal/output/document.go @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package output + +import ( + "bytes" + "encoding/json" + "fmt" + "strconv" + "strings" + + "go.yaml.in/yaml/v3" +) + +// Document is a JSON or YAML document that keeps its field order. Files written by +// `get` are kept in Git, so the order the platform returns has to survive: a Go map +// would sort the keys and turn every upgrade of the CLI into a diff of every file. +type Document struct { + node *yaml.Node +} + +// ParseDocument reads JSON or YAML; JSON is valid YAML, so one parser handles both. +// Anchors and merge keys (`<<:`) are resolved when the document is turned into JSON. +func ParseDocument(content []byte) (*Document, error) { + var node yaml.Node + if err := yaml.Unmarshal(content, &node); err != nil { + return nil, err + } + if node.Kind != yaml.DocumentNode || len(node.Content) != 1 || node.Content[0].Kind != yaml.MappingNode { + return nil, fmt.Errorf("expected an object") + } + return &Document{node: node.Content[0]}, nil +} + +func (d *Document) Get(key string) (string, bool) { + for i := 0; i+1 < len(d.node.Content); i += 2 { + if d.node.Content[i].Value == key { + return d.node.Content[i+1].Value, true + } + } + return "", false +} + +func (d *Document) Delete(key string) { + for i := 0; i+1 < len(d.node.Content); i += 2 { + if d.node.Content[i].Value == key { + d.node.Content = append(d.node.Content[:i], d.node.Content[i+2:]...) + return + } + } +} + +// SetFirst sets a string field, moving it to the top, where `key` and `id` belong. +func (d *Document) SetFirst(key, value string) { + d.Delete(key) + d.node.Content = append([]*yaml.Node{ + {Kind: yaml.ScalarNode, Tag: "!!str", Value: key}, + {Kind: yaml.ScalarNode, Tag: "!!str", Value: value}, + }, d.node.Content...) +} + +func (d *Document) Render(datatype Datatype) ([]byte, error) { + if datatype == JSON { + var buf bytes.Buffer + if err := writeJSON(&buf, d.node, ""); err != nil { + return nil, err + } + buf.WriteByte('\n') + return buf.Bytes(), nil + } + restyle(d.node) + var buf bytes.Buffer + encoder := yaml.NewEncoder(&buf) + encoder.SetIndent(2) + if err := encoder.Encode(d.node); err != nil { + return nil, err + } + return buf.Bytes(), encoder.Close() +} + +// MarshalJSON lets a document be sent as a request body. +func (d *Document) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + err := writeJSON(&buf, d.node, "") + return buf.Bytes(), err +} + +// restyle drops the flow style and double quotes that parsing JSON leaves on every node, +// and quotes a string the way js-yaml did: single quotes when it would otherwise read as +// something else, double quotes only when it needs escapes. +func restyle(node *yaml.Node) { + node.Style = 0 + // JavaScript has one number type, so the platform's 1.0 was always written as 1. + if node.Kind == yaml.ScalarNode && node.ShortTag() == "!!float" { + if f, err := strconv.ParseFloat(node.Value, 64); err == nil { + node.Value = strconv.FormatFloat(f, 'f', -1, 64) + if f == float64(int64(f)) { + node.Tag = "!!int" + } + } + } + if node.Kind == yaml.ScalarNode && node.Tag == "!!str" && needsQuotes(node.Value) { + if strings.ContainsAny(node.Value, "\n\t\\") || !strconv.IsPrint(firstUnprintable(node.Value)) { + node.Style = yaml.DoubleQuotedStyle + } else { + node.Style = yaml.SingleQuotedStyle + } + } + if node.Kind == yaml.ScalarNode && node.Tag == "!!str" && strings.Contains(node.Value, "\n") { + node.Style = yaml.LiteralStyle + } + for _, child := range node.Content { + restyle(child) + } +} + +func firstUnprintable(s string) rune { + for _, r := range s { + if !strconv.IsPrint(r) { + return r + } + } + return 'a' +} + +// needsQuotes reports whether a plain scalar would not read back as this string. +func needsQuotes(value string) bool { + plain := &yaml.Node{Kind: yaml.ScalarNode, Value: value} + out, err := yaml.Marshal(plain) + if err != nil { + return true + } + rendered := strings.TrimSuffix(string(out), "\n") + if rendered != value { + return true + } + var resolved yaml.Node + if err := yaml.Unmarshal(out, &resolved); err != nil || len(resolved.Content) != 1 { + return true + } + return resolved.Content[0].Tag != "!!str" || isTimestamp(value) +} + +// js-yaml's default schema reads timestamps as dates, so it quoted them; so do we. +func isTimestamp(value string) bool { + return len(value) >= 10 && value[4] == '-' && value[7] == '-' && strings.IndexFunc(value[:4], func(r rune) bool { return r < '0' || r > '9' }) < 0 +} + +// writeJSON renders a node as indented JSON in its own field order, resolving aliases +// and merge keys on the way. +func writeJSON(buf *bytes.Buffer, node *yaml.Node, indent string) error { + switch node.Kind { + case yaml.AliasNode: + return writeJSON(buf, node.Alias, indent) + case yaml.MappingNode: + pairs := mergedPairs(node) + if len(pairs) == 0 { + buf.WriteString("{}") + return nil + } + buf.WriteString("{\n") + for i, pair := range pairs { + key := jsonString(pair[0].Value) + buf.WriteString(indent + " ") + buf.Write(key) + buf.WriteString(": ") + if err := writeJSON(buf, pair[1], indent+" "); err != nil { + return err + } + if i < len(pairs)-1 { + buf.WriteByte(',') + } + buf.WriteByte('\n') + } + buf.WriteString(indent + "}") + case yaml.SequenceNode: + if len(node.Content) == 0 { + buf.WriteString("[]") + return nil + } + buf.WriteString("[\n") + for i, item := range node.Content { + buf.WriteString(indent + " ") + if err := writeJSON(buf, item, indent+" "); err != nil { + return err + } + if i < len(node.Content)-1 { + buf.WriteByte(',') + } + buf.WriteByte('\n') + } + buf.WriteString(indent + "]") + case yaml.ScalarNode: + switch node.ShortTag() { + case "!!null": + buf.WriteString("null") + case "!!bool", "!!int", "!!float": + var v any + if err := node.Decode(&v); err != nil { + return err + } + b, _ := json.Marshal(v) + buf.Write(b) + default: + buf.Write(jsonString(node.Value)) + } + default: + return fmt.Errorf("unsupported YAML node") + } + return nil +} + +// jsonString encodes like JSON.stringify: `&`, `<` and `>` stay as they are. +func jsonString(value string) []byte { + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + encoder.SetEscapeHTML(false) + _ = encoder.Encode(value) + return bytes.TrimSuffix(buf.Bytes(), []byte("\n")) +} + +// mergedPairs returns a mapping's key/value pairs with `<<` merge keys expanded; keys +// written in the mapping itself win over merged ones, as YAML specifies. +func mergedPairs(node *yaml.Node) [][2]*yaml.Node { + var own, merged [][2]*yaml.Node + seen := map[string]bool{} + for i := 0; i+1 < len(node.Content); i += 2 { + key, value := node.Content[i], node.Content[i+1] + if key.Value == "<<" && key.Tag == "!!merge" { + sources := []*yaml.Node{value} + if resolve(value).Kind == yaml.SequenceNode { + sources = resolve(value).Content + } + for _, source := range sources { + merged = append(merged, mergedPairs(resolve(source))...) + } + continue + } + own = append(own, [2]*yaml.Node{key, value}) + seen[key.Value] = true + } + for _, pair := range merged { + if !seen[pair[0].Value] { + own = append(own, pair) + seen[pair[0].Value] = true + } + } + return own +} + +func resolve(node *yaml.Node) *yaml.Node { + for node.Kind == yaml.AliasNode { + node = node.Alias + } + return node +} diff --git a/internal/output/document_test.go b/internal/output/document_test.go new file mode 100644 index 0000000..5576694 --- /dev/null +++ b/internal/output/document_test.go @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package output + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func render(t *testing.T, input string, datatype Datatype) string { + t.Helper() + doc, err := ParseDocument([]byte(input)) + require.NoError(t, err) + out, err := doc.Render(datatype) + require.NoError(t, err) + return string(out) +} + +func TestKeepsThePlatformsFieldOrder(t *testing.T) { + assert.Equal(t, "name: x\nteam: ADM\nactive: true\n", render(t, `{"name":"x","team":"ADM","active":true}`, YAML)) +} + +func TestQuotesLikeJsYaml(t *testing.T) { + out := render(t, `{"a":"*","b":"2026-02-25T06:35:52Z","c":"true","d":"plain","e":"line1\nline2"}`, YAML) + assert.Equal(t, "a: '*'\nb: '2026-02-25T06:35:52Z'\nc: 'true'\nd: plain\ne: |-\n line1\n line2\n", out) +} + +func TestWritesWholeFloatsAsJavaScriptDid(t *testing.T) { + assert.Equal(t, "a: 1\nb: 1.5\n", render(t, `{"a":1.0,"b":1.5}`, YAML)) + assert.Equal(t, "{\n \"a\": 1,\n \"b\": 1.5\n}\n", render(t, `{"a":1.0,"b":1.5}`, JSON)) +} + +func TestIndentsSequencesUnderTheirKey(t *testing.T) { + assert.Equal(t, "tags:\n - a\n - b\n", render(t, `{"tags":["a","b"]}`, YAML)) +} + +func TestResolvesMergeKeysWhenSending(t *testing.T) { + doc, err := ParseDocument([]byte("base: &b\n x: 1\n y: 2\nderived:\n <<: *b\n y: 3\n")) + require.NoError(t, err) + out, err := doc.MarshalJSON() + require.NoError(t, err) + assert.JSONEq(t, `{"base":{"x":1,"y":2},"derived":{"y":3,"x":1}}`, string(out)) +} + +func TestDoesNotEscapeHTMLInJSON(t *testing.T) { + assert.Equal(t, "{\n \"u\": \"a?b=1&c=\"\n}\n", render(t, `{"u":"a?b=1&c="}`, JSON)) +} + +func TestSetFirstMovesTheKeyToTheTop(t *testing.T) { + doc, err := ParseDocument([]byte(`{"name":"x","key":"old"}`)) + require.NoError(t, err) + doc.SetFirst("key", "ADM-1") + out, err := doc.Render(YAML) + require.NoError(t, err) + assert.Equal(t, "key: ADM-1\nname: x\n", string(out)) +} diff --git a/internal/output/output.go b/internal/output/output.go new file mode 100644 index 0000000..ea1a6a9 --- /dev/null +++ b/internal/output/output.go @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package output formats documents and gates colour on stdout being a terminal, as the +// TypeScript CLI did: its output is routinely parsed by GitOps pipelines. +package output + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "strings" + + "go.yaml.in/yaml/v3" + "golang.org/x/term" +) + +var colorsEnabled = os.Getenv("NO_COLOR") == "" && + (os.Getenv("FORCE_COLOR") != "" || term.IsTerminal(int(os.Stdout.Fd()))) + +func style(code, s string) string { + if !colorsEnabled { + return s + } + return "\x1b[" + code + "m" + s + "\x1b[0m" +} + +func Bold(s string) string { return style("1", s) } +func Red(s string) string { return style("31", s) } +func Green(s string) string { return style("32", s) } + +type Datatype string + +const ( + JSON Datatype = "json" + YAML Datatype = "yaml" +) + +// ResolveDatatype: an explicit type first, then the file's extension, then YAML. +func ResolveDatatype(explicit, file string) (Datatype, error) { + switch explicit { + case "json", "yaml": + return Datatype(explicit), nil + case "": + if strings.HasSuffix(strings.ToLower(file), ".json") { + return JSON, nil + } + return YAML, nil + default: + return "", fmt.Errorf("unsupported output format '%s'. Use \"json\" or \"yaml\"", explicit) + } +} + +// Format renders a document. JSON is decoded with UseNumber first, so that large +// numbers survive the trip through Go values unchanged. +func Format(document any, datatype Datatype) ([]byte, error) { + if datatype == JSON { + return json.MarshalIndent(document, "", " ") + } + var buf bytes.Buffer + encoder := yaml.NewEncoder(&buf) + encoder.SetIndent(2) + if err := encoder.Encode(document); err != nil { + return nil, err + } + return buf.Bytes(), encoder.Close() +} + +// Parse reads JSON or YAML. YAML anchors and merge keys (`<<:`) are resolved, which is +// what the TypeScript CLI's schema was configured to do for experiment files. +func Parse(content []byte) (map[string]any, Datatype, error) { + var document map[string]any + decoder := json.NewDecoder(bytes.NewReader(content)) + decoder.UseNumber() + if err := decoder.Decode(&document); err == nil { + return document, JSON, nil + } + var node yaml.Node + if err := yaml.Unmarshal(content, &node); err != nil { + return nil, "", err + } + if err := node.Decode(&document); err != nil { + return nil, "", err + } + return normalize(document).(map[string]any), YAML, nil +} + +// yaml.v3 decodes nested maps as map[string]any already, but keeps integers as int, +// which is fine; this only exists to turn map[any]any from older documents into JSON-able maps. +func normalize(value any) any { + switch v := value.(type) { + case map[string]any: + for k, item := range v { + v[k] = normalize(item) + } + return v + case map[any]any: + m := make(map[string]any, len(v)) + for k, item := range v { + m[fmt.Sprint(k)] = normalize(item) + } + return m + case []any: + for i, item := range v { + v[i] = normalize(item) + } + return v + default: + return v + } +} diff --git a/internal/platform/client.go b/internal/platform/client.go new file mode 100644 index 0000000..d0e8152 --- /dev/null +++ b/internal/platform/client.go @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package platform builds the generated API client with what every request needs: +// authentication, a User-Agent, request logging, and retries that are safe to make. +package platform + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "math/rand/v2" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "time" + + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/config" + "github.com/steadybit/cli/internal/output" +) + +var Version = "dev" + +// ErrNoAccessToken is reported with the setup help, before any request is made. +var ErrNoAccessToken = errors.New("no API access token") + +func MissingTokenHelp() string { + return strings.TrimSpace(fmt.Sprintf(` +No API access token configuration was found for Steadybit platform access. +You can configure API access tokens through configuration profiles or +environment variables (%s). We recommend configuration profiles +for local CLI usage. You can add a configuration profile via + + %s +`, output.Bold("STEADYBIT_TOKEN"), output.Bold("steadybit config profile add"))) +} + +// APIError carries the status and the problem body of a failed request. +type APIError struct { + Method, URL string + Status int + Body []byte +} + +func (e *APIError) Error() string { + body := string(e.Body) + if body == "" { + body = "" + } + return fmt.Sprintf("Steadybit API at %s %s responded with unexpected status code: %d - %s", e.Method, e.URL, e.Status, body) +} + +// ProblemType is the `type` of an RFC 7807 problem body, if there is one. +func (e *APIError) ProblemType() string { + var problem struct { + Type string `json:"type"` + } + _ = json.Unmarshal(e.Body, &problem) + return problem.Type +} + +type Client struct { + *api.ClientWithResponses + BaseURL string + http *http.Client + authorize api.RequestEditorFn +} + +// Get fetches a path the spec has no operation for, such as the Location of a run. +func (c *Client) Get(ctx context.Context, path string) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.BaseURL+path, nil) + if err != nil { + return nil, err + } + if err := c.authorize(ctx, req); err != nil { + return nil, err + } + return c.http.Do(req) +} + +var Verbose bool + +func New() (*Client, error) { + cfg, err := config.Load() + if err != nil { + return nil, err + } + if cfg.APIAccessToken == "" { + return nil, ErrNoAccessToken + } + base, err := url.Parse(cfg.BaseURL) + if err != nil { + return nil, fmt.Errorf("invalid base URL '%s': %w", cfg.BaseURL, err) + } + httpClient := &http.Client{ + Transport: &transport{next: http.DefaultTransport, base: base}, + Timeout: 30 * time.Second, + // Requests carry the access token; following a redirect could hand it to another host. + CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }, + } + authorize := func(_ context.Context, req *http.Request) error { + req.Header.Set("Authorization", "accessToken "+cfg.APIAccessToken) + req.Header.Set("Accept", "application/json, */*") + req.Header.Set("User-Agent", "steadybit@"+Version) + return nil + } + client, err := api.NewClientWithResponses(cfg.BaseURL, api.WithHTTPClient(httpClient), api.WithRequestEditorFn(authorize)) + if err != nil { + return nil, err + } + return &Client{ClientWithResponses: client, BaseURL: cfg.BaseURL, http: httpClient, authorize: authorize}, nil +} + +// Check turns any non-2xx response into an APIError. +func Check(resp *http.Response, body []byte) error { + if resp.StatusCode >= 200 && resp.StatusCode < 300 { + return nil + } + return &APIError{Method: resp.Request.Method, URL: resp.Request.URL.String(), Status: resp.StatusCode, Body: body} +} + +const maxRateLimitWait = 2 * time.Minute + +var idempotent = map[string]bool{"GET": true, "HEAD": true, "OPTIONS": true, "PUT": true, "DELETE": true} + +// transport retries what is safe to retry: a 429 for any method, since the request was +// rejected rather than applied, and a transport failure only for idempotent methods, as +// a POST that failed in transit may still have started an experiment run. +type transport struct { + next http.RoundTripper + base *url.URL +} + +func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { + // Absolute URLs from the platform, such as the Location of a run, keep their path + // but are sent to the configured origin, so the token never leaves that host. + req.URL.Scheme, req.URL.Host = t.base.Scheme, t.base.Host + + var body []byte + if req.Body != nil { + body, _ = io.ReadAll(req.Body) + _ = req.Body.Close() + } + var waited time.Duration + for attempt := 1; ; attempt++ { + if body != nil { + req.Body = io.NopCloser(bytes.NewReader(body)) + } + logRequest(req, body) + resp, err := t.next.RoundTrip(req) + if err != nil { + if !idempotent[req.Method] || attempt >= 4 { + return nil, fmt.Errorf("failed to call Steadybit API at %s %s: %w", req.Method, req.URL, err) + } + time.Sleep(jitter(time.Duration(attempt) * time.Second)) + continue + } + logResponse(resp) + if resp.StatusCode != http.StatusTooManyRequests { + return resp, nil + } + wait := time.Second + for _, h := range []string{"RateLimit-Reset", "Retry-After"} { + if seconds, err := strconv.Atoi(resp.Header.Get(h)); err == nil && seconds > 0 { + wait = time.Duration(seconds) * time.Second + break + } + } + if waited+wait > maxRateLimitWait { + return resp, nil + } + _ = resp.Body.Close() + time.Sleep(wait) + waited += wait + } +} + +func jitter(d time.Duration) time.Duration { + return d/2 + time.Duration(rand.Int64N(int64(d/2)+1)) +} + +func logRequest(req *http.Request, body []byte) { + if !Verbose { + return + } + fmt.Printf("> HTTP %s %s\n", req.Method, req.URL) + for name, values := range req.Header { + value := strings.Join(values, ", ") + if strings.EqualFold(name, "Authorization") || strings.EqualFold(name, "Cookie") { + value = "" + } + fmt.Printf("> %s: %s\n", name, value) + } + fmt.Println(">") + if len(body) > 0 { + fmt.Println(string(body)) + } + fmt.Println() +} + +func logResponse(resp *http.Response) { + if !Verbose { + return + } + fmt.Fprintf(os.Stdout, "< HTTP %s\n\n", resp.Status) +} diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go new file mode 100644 index 0000000..a305016 --- /dev/null +++ b/internal/prompt/prompt.go @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package prompt asks questions on a terminal. Ctrl-C ends the process with 130 and +// without a stack trace, as the TypeScript CLI's prompts did. +package prompt + +import ( + "bufio" + "fmt" + "os" + "os/signal" + "strings" + "syscall" + + "golang.org/x/term" +) + +var ( + reader = bufio.NewReader(os.Stdin) + // Set while a password is being read, which turns echo off. An interrupt then has + // to turn it back on, or the user is left with a terminal that shows nothing typed. + restoreTerminal func() +) + +func init() { + interrupts := make(chan os.Signal, 1) + signal.Notify(interrupts, os.Interrupt, syscall.SIGTERM) + go func() { + <-interrupts + if restoreTerminal != nil { + restoreTerminal() + } + fmt.Println() + os.Exit(130) + }() +} + +type Validator func(string) error + +func NotBlank(value string) error { + if strings.TrimSpace(value) == "" { + return fmt.Errorf("a value is required") + } + return nil +} + +func HTTPURL(value string) error { + if !strings.HasPrefix(value, "http://") && !strings.HasPrefix(value, "https://") { + return fmt.Errorf("please enter an http:// or https:// URL") + } + return nil +} + +// Input asks until the answer is valid. An empty answer takes the default. +func Input(message, defaultValue string, validate Validator) (string, error) { + for { + if defaultValue != "" { + fmt.Printf("? %s (%s) ", message, defaultValue) + } else { + fmt.Printf("? %s ", message) + } + line, err := reader.ReadString('\n') + if err != nil { + return "", err + } + answer := strings.TrimSpace(line) + if answer == "" { + answer = defaultValue + } + if err := validate(answer); err != nil { + fmt.Printf("> %s\n", err) + continue + } + return answer, nil + } +} + +// Password reads without echo when stdin is a terminal. +func Password(message string, validate Validator) (string, error) { + for { + fmt.Printf("? %s ", message) + var answer string + if fd := int(os.Stdin.Fd()); term.IsTerminal(fd) { + state, err := term.GetState(fd) + if err != nil { + return "", err + } + restoreTerminal = func() { _ = term.Restore(fd, state) } + bytes, err := term.ReadPassword(fd) + restoreTerminal() + restoreTerminal = nil + fmt.Println() + if err != nil { + return "", err + } + answer = string(bytes) + } else { + line, err := reader.ReadString('\n') + if err != nil { + return "", err + } + answer = strings.TrimSpace(line) + } + if err := validate(answer); err != nil { + fmt.Printf("> %s\n", err) + continue + } + return answer, nil + } +} + +// Confirm asks a yes/no question. Without a terminal it answers nonInteractive. +func Confirm(message string, defaultYes, nonInteractive bool) (bool, error) { + if !term.IsTerminal(int(os.Stdin.Fd())) { + return nonInteractive, nil + } + hint := "y/N" + if defaultYes { + hint = "Y/n" + } + fmt.Printf("? %s (%s) ", message, hint) + line, err := reader.ReadString('\n') + if err != nil { + return false, err + } + switch strings.ToLower(strings.TrimSpace(line)) { + case "": + return defaultYes, nil + case "y", "yes": + return true, nil + default: + return false, nil + } +} From 920779501183b30dbcd9d4d18a8b18684f544142 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 13:40:22 +0200 Subject: [PATCH 2/9] feat(go): byte-compatible YAML/JSON output, rate-limit pacing, experiment dump internal/jsyaml ports the parts of js-yaml's dump the CLI used, and a JSON.stringify-compatible writer, so files written by the Go CLI are byte-identical to the TypeScript CLI's. Checked against a recorded corpus of 527 platform experiments (YAML, JSON and compact JSON) and a fixture of 123 edge cases generated from js-yaml itself. JSON is now read with a JSON decoder: yaml.v3 rejects characters JSON allows unescaped, such as DEL. Requests are paced by the same token bucket as before, honouring the STEADYBIT_RATE_LIMIT_* overrides, and each attempt gets its own deadline once admitted, so waiting for the limiter no longer counts as a timeout. experiment dump is ported; on dev it writes trees identical to the TypeScript CLI's. Flags that took space-separated values in commander (--team A B, -f a b) accept them again. --- internal/cli/experiment.go | 29 +- internal/cli/root.go | 8 +- internal/cli/variadic.go | 65 ++ internal/cli/variadic_test.go | 25 + internal/experiment/dump.go | 313 ++++++++ internal/experiment/experiment.go | 14 +- internal/jsyaml/dump.go | 433 ++++++++++ internal/jsyaml/json.go | 130 +++ internal/jsyaml/styles.go | 220 ++++++ internal/jsyaml/testdata/cases.json | 1048 +++++++++++++++++++++++++ internal/jsyaml/testdata/generate.mjs | 43 + internal/jsyaml/value.go | 137 ++++ internal/output/document.go | 334 ++++---- internal/output/document_test.go | 38 +- internal/output/parity_test.go | 78 ++ internal/platform/client.go | 41 +- internal/platform/ratelimit.go | 129 +++ internal/platform/ratelimit_test.go | 53 ++ 18 files changed, 2943 insertions(+), 195 deletions(-) create mode 100644 internal/cli/variadic.go create mode 100644 internal/cli/variadic_test.go create mode 100644 internal/experiment/dump.go create mode 100644 internal/jsyaml/dump.go create mode 100644 internal/jsyaml/json.go create mode 100644 internal/jsyaml/styles.go create mode 100644 internal/jsyaml/testdata/cases.json create mode 100644 internal/jsyaml/testdata/generate.mjs create mode 100644 internal/jsyaml/value.go create mode 100644 internal/output/parity_test.go create mode 100644 internal/platform/ratelimit.go create mode 100644 internal/platform/ratelimit_test.go diff --git a/internal/cli/experiment.go b/internal/cli/experiment.go index ee2aba5..f2da9f1 100644 --- a/internal/cli/experiment.go +++ b/internal/cli/experiment.go @@ -15,7 +15,7 @@ import ( func newExperiment() *cobra.Command { cmd := &cobra.Command{Use: "experiment", Short: "Check and run experiments."} - cmd.AddCommand(newExperimentRun(), newExperimentGet(), newExperimentApply()) + cmd.AddCommand(newExperimentRun(), newExperimentGet(), newExperimentApply(), newExperimentDump()) return cmd } @@ -58,7 +58,7 @@ func newExperimentRun() *cobra.Command { } f := cmd.Flags() f.StringVarP(&o.Key, "key", "k", "", "The experiment key.") - f.StringSliceVarP(&o.Files, "file", "f", nil, "The path to the experiment file or a directory containing multiple files.") + f.StringArrayVarP(&o.Files, "file", "f", nil, "The path to the experiment file or a directory containing multiple files.") f.BoolVarP(&o.Recursive, "recursive", "R", false, "Process the directory used in -f, --file recursively.") f.BoolVar(&noWait, "no-wait", false, "Do not wait for experiment run to finish.") f.BoolVar(&o.Yes, "yes", false, "Skip the prompt asking for experiment run confirmation. Not necessary when no TTY is attached.") @@ -72,6 +72,7 @@ func newExperimentRun() *cobra.Command { f.VarP(&placeholders, "placeholder", "p", "With --template: a placeholder value. Repeat for more.") cmd.MarkFlagsMutuallyExclusive("key", "file") cmd.MarkFlagsMutuallyExclusive("template", "file") + variadic(cmd, "file") return cmd } @@ -105,8 +106,30 @@ func newExperimentApply() *cobra.Command { }), } cmd.Flags().StringVarP(&o.Key, "key", "k", "", "The experiment key.") - cmd.Flags().StringSliceVarP(&o.Files, "file", "f", nil, "The path to the experiment file or a directory containing multiple files.") + cmd.Flags().StringArrayVarP(&o.Files, "file", "f", nil, "The path to the experiment file or a directory containing multiple files.") cmd.Flags().BoolVarP(&o.Recursive, "recursive", "R", false, "Process the directory used in -f, --file recursively.") _ = cmd.MarkFlagRequired("file") + variadic(cmd, "file") + return cmd +} + +func newExperimentDump() *cobra.Command { + var o experiment.DumpOptions + cmd := &cobra.Command{ + Use: "dump", + Short: "Dump all experiments and executions from all teams in Steadybit.", + Args: cobra.NoArgs, + Example: examples( + "steadybit experiment dump -d ./dump", + "steadybit experiment dump -d ./dump -t json --team ADM WEBHOOK", + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return experiment.Dump(ctx, c, o) + }), + } + cmd.Flags().StringVarP(&o.Directory, "directory", "d", ".", "The path to dump all the experiments to") + cmd.Flags().StringVarP(&o.Type, "type", "t", "yaml", `The output format of the experiment ("json" or "yaml").`) + cmd.Flags().StringArrayVar(&o.Teams, "team", nil, "Only dump the given teams, by team key. Defaults to every accessible team.") + variadic(cmd, "team") return cmd } diff --git a/internal/cli/root.go b/internal/cli/root.go index db9572e..914d4cd 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/experiment" "github.com/steadybit/cli/internal/output" "github.com/steadybit/cli/internal/platform" ) @@ -82,10 +83,15 @@ func setUsage(cmd *cobra.Command) { } func Execute() int { - err := newRoot().ExecuteContext(context.Background()) + root := newRoot() + root.SetArgs(expandVariadic(root, os.Args[1:])) + err := root.ExecuteContext(context.Background()) if err == nil { return 0 } + if errors.Is(err, experiment.ErrIncomplete) { + return 1 // already reported, with what was missing + } if errors.Is(err, platform.ErrNoAccessToken) { fmt.Fprintln(os.Stderr, platform.MissingTokenHelp()) } else { diff --git a/internal/cli/variadic.go b/internal/cli/variadic.go new file mode 100644 index 0000000..976857b --- /dev/null +++ b/internal/cli/variadic.go @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +const variadicAnnotation = "steadybit_variadic" + +// variadic marks flags that take several space-separated values, as commander's +// `` options did: `--team ADM WEBHOOK` and `-f a.yml b.yml`. pflag only takes +// a repeated flag or a comma-separated list, and pipelines written for the TypeScript CLI +// use the space-separated form. +func variadic(cmd *cobra.Command, names ...string) { + for _, name := range names { + _ = cmd.Flags().SetAnnotation(name, variadicAnnotation, []string{"true"}) + } +} + +// expandVariadic rewrites `--flag a b` into `--flag a --flag b` for the command the +// arguments address. Like commander, it takes values until the next one starting with '-'. +func expandVariadic(root *cobra.Command, args []string) []string { + cmd, _, err := root.Find(args) + if err != nil || cmd == nil { + return args + } + isVariadic := func(arg string) (string, bool) { + var flag *pflag.Flag + switch { + case strings.HasPrefix(arg, "--"): + flag = cmd.Flags().Lookup(strings.TrimPrefix(arg, "--")) + case strings.HasPrefix(arg, "-") && len(arg) == 2: + flag = cmd.Flags().ShorthandLookup(arg[1:]) + } + if flag == nil || flag.Annotations[variadicAnnotation] == nil { + return "", false + } + return arg, true + } + + var out []string + for i := 0; i < len(args); i++ { + arg := args[i] + out = append(out, arg) + if arg == "--" { + return append(out, args[i+1:]...) + } + name, ok := isVariadic(arg) + if !ok || i+1 >= len(args) { + continue + } + out = append(out, args[i+1]) + i++ + for i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") { + out = append(out, name, args[i+1]) + i++ + } + } + return out +} diff --git a/internal/cli/variadic_test.go b/internal/cli/variadic_test.go new file mode 100644 index 0000000..e912ca3 --- /dev/null +++ b/internal/cli/variadic_test.go @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestExpandsSpaceSeparatedValuesAsCommanderDid(t *testing.T) { + root := newRoot() + + assert.Equal(t, + []string{"experiment", "dump", "-d", "x", "--team", "A", "--team", "B", "--team", "C", "-t", "json"}, + expandVariadic(root, []string{"experiment", "dump", "-d", "x", "--team", "A", "B", "C", "-t", "json"})) + assert.Equal(t, + []string{"experiment", "apply", "-f", "a.yml", "-f", "b.yml", "-R"}, + expandVariadic(root, []string{"experiment", "apply", "-f", "a.yml", "b.yml", "-R"})) + // `get -f` takes a single file, so a following word is left alone. + assert.Equal(t, + []string{"experiment", "get", "-k", "ADM-1", "-f", "x.yml"}, + expandVariadic(root, []string{"experiment", "get", "-k", "ADM-1", "-f", "x.yml"})) +} diff --git a/internal/experiment/dump.go b/internal/experiment/dump.go new file mode 100644 index 0000000..0162115 --- /dev/null +++ b/internal/experiment/dump.go @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package experiment + +import ( + "context" + "encoding/json" + "fmt" + "math" + "net/http" + "os" + "path/filepath" + "sort" + "strings" + "sync" + + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" +) + +// A dump walks every experiment of every team and every execution of every experiment. +// Both levels are bounded so the request volume stays predictable instead of scaling +// with the size of the tenant. +const ( + experimentConcurrency = 4 + executionConcurrency = 16 + largeDumpExperiments = 100 +) + +type DumpOptions struct { + Directory string + Type string + Teams []string +} + +type team struct { + Key string `json:"key"` + Name string `json:"name"` +} + +type listed struct { + Key string `json:"key"` +} + +// ErrIncomplete makes the command exit non-zero after everything that could be fetched +// has been written, so that a pipeline does not mistake a partial dump for a full one. +var ErrIncomplete = fmt.Errorf("incomplete dump") + +func Dump(ctx context.Context, c *platform.Client, o DumpOptions) error { + datatype := output.YAML + if o.Type == "json" { + datatype = output.JSON + } + if err := os.MkdirAll(o.Directory, 0o755); err != nil { + return err + } + + var teamList struct { + Teams []team `json:"teams"` + } + onlyAccessible := false + if err := getJSON(c.GetTeams(ctx, &api.GetTeamsParams{OnlyAccessible: &onlyAccessible}))(&teamList); err != nil { + return fmt.Errorf("Failed to get teams: %w", err) + } + teams, err := selectTeams(teamList.Teams, o.Teams) + if err != nil { + return err + } + + // The lists are fetched up front, which costs nothing extra because each team needs + // one anyway, so the size of the walk is known before it starts. + plural := "teams" + if len(teams) == 1 { + plural = "team" + } + fmt.Printf("Listing experiments for %d %s", len(teams), plural) + lists := map[string][]listed{} + total := 0 + for _, t := range teams { + var list struct { + Experiments []listed `json:"experiments"` + } + key := []string{t.Key} + if err := getJSON(c.GetExperiments(ctx, &api.GetExperimentsParams{Team: &key}))(&list); err != nil { + fmt.Println() + return fmt.Errorf("Failed to get the experiments. HTTP request failed: %w", err) + } + lists[t.Key] = list.Experiments + total += len(list.Experiments) + fmt.Print(".") + } + fmt.Println() + if total > largeDumpExperiments { + minutes := int(math.Ceil(platform.Limiter().DurationFor(total * 2).Minutes())) + fmt.Fprintf(os.Stderr, "Dumping %d experiments. Requests are paced to the platform's rate limit, so this takes at least %d minutes, longer with executions.\n\n", total, minutes) + } + + var experiments, executions, failedExperiments, failedExecutions int + for _, t := range teams { + fmt.Printf("Fetching experiments for team %s (%s)... ", t.Name, t.Key) + d := dumpTeam(ctx, c, lists[t.Key], o.Directory, datatype) + experiments += d.experiments + executions += d.executions + failedExperiments += d.failedExperiments + failedExecutions += d.failedExecutions + failed := "" + if n := d.failedExperiments + d.failedExecutions; n > 0 { + failed = fmt.Sprintf(", failed: %d", n) + } + fmt.Printf("experiments: %d, executions: %d%s\n", d.experiments, d.executions, failed) + // Only once the progress line is finished, so the two streams stay readable + // when redirected to different places. + for _, problem := range d.problems { + fmt.Fprintf(os.Stderr, " %s\n", problem) + } + } + fmt.Printf("Written %d experiments with %d executions\n", experiments, executions) + if failedExperiments > 0 || failedExecutions > 0 { + fmt.Fprintf(os.Stderr, "Incomplete: %d experiments and %d executions could not be dumped\n", failedExperiments, failedExecutions) + return ErrIncomplete + } + return nil +} + +// getJSON reads a response into target once the request has been checked. +func getJSON(resp *http.Response, err error) func(target any) error { + return func(target any) error { + body, _, err := read(resp, err) + if err != nil { + return err + } + return json.Unmarshal(body, target) + } +} + +// Keys are matched case-insensitively, as they are shown and typed. An unknown one +// aborts: a dump that quietly covers less than asked is indistinguishable from a full one. +func selectTeams(teams []team, keys []string) ([]team, error) { + if len(keys) == 0 { + return teams, nil + } + wanted := map[string]bool{} + var order []string + for _, k := range keys { + upper := strings.ToUpper(k) + if !wanted[upper] { + order = append(order, upper) + } + wanted[upper] = true + } + var selected []team + found := map[string]bool{} + for _, t := range teams { + if wanted[strings.ToUpper(t.Key)] { + selected = append(selected, t) + found[strings.ToUpper(t.Key)] = true + } + } + var missing []string + for _, k := range order { + if !found[k] { + missing = append(missing, k) + } + } + if len(missing) > 0 { + available := make([]string, 0, len(teams)) + for _, t := range teams { + available = append(available, t.Key) + } + sort.Strings(available) + return nil, fmt.Errorf("No accessible team with key %s. Available: %s", strings.Join(missing, ", "), strings.Join(available, ", ")) + } + return selected, nil +} + +type teamDump struct { + experiments, executions, failedExperiments, failedExecutions int + problems []string +} + +type experimentDump struct { + executions, failedExecutions int + failed bool + problem string +} + +func dumpTeam(ctx context.Context, c *platform.Client, list []listed, dir string, datatype output.Datatype) teamDump { + results := make([]experimentDump, len(list)) + forEach(len(list), experimentConcurrency, func(i int) { + results[i] = dumpExperiment(ctx, c, list[i].Key, filepath.Join(dir, list[i].Key), datatype) + }) + var d teamDump + for _, r := range results { + if r.failed { + d.failedExperiments++ + } else { + d.experiments++ + } + d.executions += r.executions + d.failedExecutions += r.failedExecutions + if r.problem != "" { + d.problems = append(d.problems, r.problem) + } + } + return d +} + +// A single unlucky request must not discard the whole walk: failures are counted and +// reported instead of ending the command. +func dumpExperiment(ctx context.Context, c *platform.Client, key, dir string, datatype output.Datatype) experimentDump { + if err := os.MkdirAll(dir, 0o755); err != nil { + return experimentDump{failed: true, problem: fmt.Sprintf("%s: %s", key, err)} + } + var ( + wg sync.WaitGroup + document Document + designErr error + executions struct { + Executions []struct { + ID int64 `json:"id"` + } `json:"executions"` + } + listErr error + ) + wg.Add(2) + go func() { defer wg.Done(); document, designErr = Fetch(ctx, c, key) }() + go func() { + defer wg.Done() + listErr = getJSON(c.GetExperimentExecutions3(ctx, key, nil))(&executions) + }() + wg.Wait() + if designErr != nil { + return experimentDump{failed: true, problem: fmt.Sprintf("%s: %s", key, designErr)} + } + if listErr != nil { + return experimentDump{failed: true, problem: fmt.Sprintf("%s: Failed to get the executions. HTTP request failed: %s", key, listErr)} + } + removeDeprecatedFields(document.Value()) + if err := os.WriteFile(filepath.Join(dir, "experiment."+string(datatype)), document.RenderFile(datatype), 0o644); err != nil { + return experimentDump{failed: true, problem: fmt.Sprintf("%s: %s", key, err)} + } + + ok := make([]bool, len(executions.Executions)) + forEach(len(ok), executionConcurrency, func(i int) { + id := executions.Executions[i].ID + body, _, err := read(c.GetExperimentExecution(ctx, id, nil)) + if err != nil { + return + } + execution, err := output.ParseDocument(body) + if err != nil { + return + } + ok[i] = os.WriteFile(filepath.Join(dir, fmt.Sprintf("execution-%d.%s", id, datatype)), execution.RenderFile(datatype), 0o644) == nil + }) + r := experimentDump{} + for _, written := range ok { + if written { + r.executions++ + } else { + r.failedExecutions++ + } + } + if r.failedExecutions > 0 { + r.problem = fmt.Sprintf("%s: %d of %d executions could not be fetched", key, r.failedExecutions, len(ok)) + } + return r +} + +// The query and list of a step's radius are deprecated and left out of dumps. +func removeDeprecatedFields(experiment *jsyaml.Map) { + lanes, _ := experiment.Get("lanes") + laneList, _ := lanes.([]any) + for _, lane := range laneList { + laneMap, _ := lane.(*jsyaml.Map) + if laneMap == nil { + continue + } + steps, _ := laneMap.Get("steps") + stepList, _ := steps.([]any) + for _, step := range stepList { + stepMap, _ := step.(*jsyaml.Map) + if stepMap == nil { + continue + } + if radius, ok := stepMap.Get("radius"); ok { + if radiusMap, ok := radius.(*jsyaml.Map); ok { + radiusMap.Delete("query") + radiusMap.Delete("list") + } + } + } + } +} + +// forEach runs fn for 0..n-1 with at most limit calls in flight. +func forEach(n, limit int, fn func(int)) { + slots := make(chan struct{}, max(1, limit)) + var wg sync.WaitGroup + for i := range n { + wg.Add(1) + slots <- struct{}{} + go func() { + defer wg.Done() + defer func() { <-slots }() + fn(i) + }() + } + wg.Wait() +} diff --git a/internal/experiment/experiment.go b/internal/experiment/experiment.go index 10740a1..ce18b31 100644 --- a/internal/experiment/experiment.go +++ b/internal/experiment/experiment.go @@ -98,15 +98,7 @@ func Get(ctx context.Context, c *platform.Client, o GetOptions) error { } return nil } - if datatype == output.JSON { - // Files were written as JSON.stringify left them, on a single line. - var compact bytes.Buffer - if err := json.Compact(&compact, rendered); err != nil { - return err - } - rendered = compact.Bytes() - } - if err := os.WriteFile(o.File, rendered, 0o644); err != nil { + if err := os.WriteFile(o.File, document.RenderFile(datatype), 0o644); err != nil { return err } fmt.Printf("Experiment %s written to %s.\n", o.Key, o.File) @@ -182,9 +174,7 @@ func writeBack(file string, document Document, datatype output.Datatype, key str rendered = append([]byte("key: "+key+"\n"), content...) } else { document.SetFirst("key", key) - if rendered, err = document.Render(datatype); err != nil { - return err - } + rendered = document.RenderFile(datatype) } return os.WriteFile(file, rendered, 0o644) } diff --git a/internal/jsyaml/dump.go b/internal/jsyaml/dump.go new file mode 100644 index 0000000..44f5b76 --- /dev/null +++ b/internal/jsyaml/dump.go @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package jsyaml + +import ( + "fmt" + "math" + "regexp" + "strconv" + "strings" + "unicode/utf16" +) + +// A port of js-yaml 5's dump with the options the TypeScript CLI used: indent 2, line +// width 80, single quotes preferred, and a schema of the YAML core tags plus merge keys +// and timestamps. Only what JSON-shaped values need is ported; there are no tags, +// anchors or flow collections beyond the empty [] and {}. + +const ( + indentStep = 2 + lineWidth = 80 +) + +type style int + +const ( + plain style = iota + singleQuoted + doubleQuoted + literal + folded +) + +type dumper struct { + openEnded bool +} + +// Dump renders a value as js-yaml's dump did, trailing newline included. +func Dump(value any) string { + d := &dumper{} + out := d.node(0, value, false, true, true) + "\n" + if d.openEnded { + out += "...\n" + } + return out +} + +func nextLine(level int) string { + return "\n" + strings.Repeat(" ", indentStep*level) +} + +func (d *dumper) node(level int, value any, isKey, block, compact bool) string { + switch v := value.(type) { + case *Map: + if block && v.Len() > 0 { + return d.blockMapping(level, v, compact) + } + d.openEnded = false + return "{}" + case []any: + if block && len(v) > 0 { + return d.blockSequence(level, v, compact) + } + d.openEnded = false + return "[]" + case string: + return d.scalar(level, v, "str", isKey, !block) + case nil: + return d.scalar(level, "null", "null", isKey, !block) + case bool: + return d.scalar(level, strconv.FormatBool(v), "bool", isKey, !block) + case float64: + text, tag := representNumber(v) + return d.scalar(level, text, tag, isKey, !block) + case Timestamp: + return d.scalar(level, v.ISO(), "timestamp", isKey, !block) + default: + return d.scalar(level, fmt.Sprint(v), "str", isKey, !block) + } +} + +// representNumber picks js-yaml's int or float tag and text for a JavaScript number. +func representNumber(f float64) (string, string) { + s := NumberString(f) + if f == math.Trunc(f) && !math.IsInf(f, 0) && !(f == 0 && math.Signbit(f)) && !strings.Contains(s, "e") { + return s, "int" + } + switch { + case math.IsNaN(f): + return ".nan", "float" + case math.IsInf(f, 1): + return ".inf", "float" + case math.IsInf(f, -1): + return "-.inf", "float" + case f == 0 && math.Signbit(f): + return "-0.0", "float" + } + if leadingExponent.MatchString(s) { + s = strings.Replace(s, "e", ".e", 1) + } + return s, "float" +} + +var leadingExponent = regexp.MustCompile(`^[-+]?[0-9]+e`) + +func (d *dumper) blockSequence(level int, items []any, compact bool) string { + var result strings.Builder + for _, item := range items { + text := d.node(level+1, item, false, true, true) + if !compact || result.Len() > 0 { + result.WriteString(nextLine(level)) + } + if text == "" || text[0] == '\n' { + result.WriteString("-") + } else { + result.WriteString("- ") + } + result.WriteString(text) + } + return result.String() +} + +func (d *dumper) blockMapping(level int, m *Map, compact bool) string { + var result strings.Builder + for _, key := range m.Keys() { + var pair strings.Builder + if !compact || result.Len() > 0 { + pair.WriteString(nextLine(level)) + } + keyText := d.node(level+1, key, true, true, true) + explicit := strings.Contains(key, "\n") || length(keyText) > 1024 + if explicit { + if keyText != "" && keyText[0] == '\n' { + pair.WriteString("?") + } else { + pair.WriteString("? ") + } + } + pair.WriteString(keyText) + if explicit { + pair.WriteString(nextLine(level)) + } + valueText := d.node(level+1, m.values[key], false, true, explicit) + if valueText == "" || valueText[0] == '\n' { + pair.WriteString(":") + } else { + pair.WriteString(": ") + } + pair.WriteString(valueText) + result.WriteString(pair.String()) + } + return result.String() +} + +type layout struct { + value string + tag string + isKey, flowOnly bool + shiftOfParent, shiftOfContent, shiftOfFirst int + allowPlain, allowSingle, allowBlock bool +} + +func (d *dumper) scalar(level int, value, tag string, isKey, flowOnly bool) string { + l := layout{value: value, tag: tag, isKey: isKey, flowOnly: flowOnly} + if level == 0 { + l.shiftOfParent = -1 + } else { + l.shiftOfParent = indentStep * (level - 1) + l.shiftOfFirst = indentStep * level + } + l.shiftOfContent = indentStep * max(1, level) + l.allowPlain = canUsePlain(l) + l.allowSingle = canUseSingleQuoted(l) + l.allowBlock = canUseBlock(l) + + s := chooseStyle(l) + d.openEnded = (s == literal || s == folded) && (value == "\n" || strings.HasSuffix(value, "\n\n")) + return render(l, s) +} + +func (l layout) allowed(s style) bool { + switch s { + case plain: + return l.allowPlain + case singleQuoted: + return l.allowSingle + case literal, folded: + return l.allowBlock + } + return true +} + +var ( + invisibles = regexp.MustCompile(`[\t\x{7F}-\x{A0}\x{2028}\x{2029}\x{FEFF}\x{FFFE}\x{FFFF}]`) + onlyWhitespace = regexp.MustCompile(`^[\t\n\v\f\r \x{A0}\x{1680}\x{2000}-\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}\x{FEFF}]+$`) + foldableSpace = regexp.MustCompile(` [^ \t]`) +) + +// chooseStyle applies js-yaml's default scalar style rules, in their order. +func chooseStyle(l layout) style { + s := plain + if invisibles.MatchString(l.value) || onlyWhitespace.MatchString(l.value) { + s = doubleQuoted + } + if s == plain && !l.isKey { + multiline := strings.Contains(l.value, "\n") + if !l.allowBlock { + if multiline { + s = doubleQuoted + } + } else { + available := max(min(lineWidth, 40), lineWidth-l.shiftOfContent) + fold := false + for _, line := range strings.Split(l.value, "\n") { + if length(line) > available && !strings.HasPrefix(line, " ") && foldableSpace.MatchString(line) { + fold = true + } + } + if fold { + s = folded + } else if multiline { + s = literal + } + } + } + if s == plain && !l.allowPlain { + if l.allowSingle { + s = singleQuoted + } else { + s = doubleQuoted + } + } + if !l.allowed(s) { + s = doubleQuoted + } + return s +} + +func render(l layout, s style) string { + switch s { + case plain: + return encodeFlowBreaks(l.value, l.shiftOfContent) + case singleQuoted: + return "'" + strings.ReplaceAll(encodeFlowBreaks(l.value, l.shiftOfContent), "'", "''") + "'" + case literal: + return "|" + blockHeader(l.value, l.shiftOfParent, l.shiftOfContent) + + dropEndingNewline(indentString(l.value, l.shiftOfContent)) + case folded: + available := max(min(lineWidth, 40), lineWidth-l.shiftOfContent) + return ">" + blockHeader(l.value, l.shiftOfParent, l.shiftOfContent) + + dropEndingNewline(indentString(foldBlockScalar(l.value, available), l.shiftOfContent)) + default: + return `"` + escapeDoubleQuoted(l.value) + `"` + } +} + +var lineBreakRun = regexp.MustCompile(`(\n+)([^\n]*)`) + +func encodeFlowBreaks(s string, shift int) string { + first := strings.Index(s, "\n") + if first == -1 { + return s + } + pad := strings.Repeat(" ", shift) + var b strings.Builder + b.WriteString(s[:first]) + for _, m := range lineBreakRun.FindAllStringSubmatch(s[first:], -1) { + b.WriteString(strings.Repeat("\n", len(m[1])+1) + pad + m[2]) + } + return b.String() +} + +func indentString(s string, spaces int) string { + indent := strings.Repeat(" ", spaces) + var b strings.Builder + for len(s) > 0 { + var line string + if i := strings.Index(s, "\n"); i == -1 { + line, s = s, "" + } else { + line, s = s[:i+1], s[i+1:] + } + if line != "" && line != "\n" { + b.WriteString(indent) + } + b.WriteString(line) + } + return b.String() +} + +var leadingSpaceAfterBreaks = regexp.MustCompile(`^\n* `) + +func blockHeader(s string, shiftOfParent, shiftOfContent int) string { + indicator := "" + if leadingSpaceAfterBreaks.MatchString(s) { + indicator = strconv.Itoa(shiftOfContent - shiftOfParent) + } + clip := strings.HasSuffix(s, "\n") + chomp := "-" + if clip { + chomp = "" + if strings.HasSuffix(s, "\n\n") || s == "\n" { + chomp = "+" + } + } + return indicator + chomp + "\n" +} + +func dropEndingNewline(s string) string { + return strings.TrimSuffix(s, "\n") +} + +func isMoreIndented(c uint16) bool { return c == ' ' || c == '\t' } + +// foldLine works in UTF-16 code units, which is what JavaScript's string indices and +// lengths count, so that lines with astral characters break where js-yaml broke them. +func foldLine(line []uint16, width int) []uint16 { + if len(line) == 0 || isMoreIndented(line[0]) { + return line + } + var result []uint16 + start, curr, next := 0, 0, 0 + for i := 0; i+1 < len(line); i++ { + if line[i] != ' ' || line[i+1] == ' ' || line[i+1] == '\t' { + continue + } + next = i + if next-start > width { + end := next + if curr > start { + end = curr + } + result = append(append(result, '\n'), line[start:end]...) + start = end + 1 + } + curr = next + } + result = append(result, '\n') + if len(line)-start > width && curr > start { + result = append(append(append(result, line[start:curr]...), '\n'), line[curr+1:]...) + } else { + result = append(result, line[start:]...) + } + return result[1:] +} + +func foldBlockScalar(s string, width int) string { + units := utf16.Encode([]rune(s)) + firstBreak := indexOf(units, '\n', 0) + if firstBreak == -1 { + firstBreak = len(units) + } + result := foldLine(units[:firstBreak], width) + prevMoreIndented := len(units) > 0 && (units[0] == '\n' || isMoreIndented(units[0])) + for pos := firstBreak; pos < len(units); { + breaksEnd := pos + for breaksEnd < len(units) && units[breaksEnd] == '\n' { + breaksEnd++ + } + lineEnd := indexOf(units, '\n', breaksEnd) + if lineEnd == -1 { + lineEnd = len(units) + } + prefix, line := units[pos:breaksEnd], units[breaksEnd:lineEnd] + moreIndented := len(line) > 0 && isMoreIndented(line[0]) + result = append(result, prefix...) + if !prevMoreIndented && !moreIndented && len(line) > 0 { + result = append(result, '\n') + } + result = append(result, foldLine(line, width)...) + prevMoreIndented = moreIndented + pos = lineEnd + } + return string(utf16.Decode(result)) +} + +func indexOf(units []uint16, c uint16, from int) int { + for i := from; i < len(units); i++ { + if units[i] == c { + return i + } + } + return -1 +} + +func escapeDoubleQuoted(s string) string { + var b strings.Builder + for _, r := range s { + switch r { + case 0: + b.WriteString(`\0`) + case 7: + b.WriteString(`\a`) + case '\b': + b.WriteString(`\b`) + case '\t': + b.WriteString(`\t`) + case '\n': + b.WriteString(`\n`) + case '\v': + b.WriteString(`\v`) + case '\f': + b.WriteString(`\f`) + case '\r': + b.WriteString(`\r`) + case 0x1B: + b.WriteString(`\e`) + case '"': + b.WriteString(`\"`) + case '\\': + b.WriteString(`\\`) + case 0x85: + b.WriteString(`\N`) + case 0xA0: + b.WriteString(`\_`) + case 0x2028: + b.WriteString(`\L`) + case 0x2029: + b.WriteString(`\P`) + default: + switch { + case r < 0x20 || (r >= 0x7F && r <= 0xA0): + fmt.Fprintf(&b, `\x%02X`, r) + case r == 0xFEFF || r == 0xFFFE || r == 0xFFFF: + fmt.Fprintf(&b, `\u%04X`, r) + default: + b.WriteRune(r) + } + } + } + return b.String() +} diff --git a/internal/jsyaml/json.go b/internal/jsyaml/json.go new file mode 100644 index 0000000..1fbc43a --- /dev/null +++ b/internal/jsyaml/json.go @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package jsyaml + +import ( + "fmt" + "math" + "strings" + "unicode/utf16" + "unicode/utf8" +) + +// JSON renders like JSON.stringify(value, undefined, 2). +func JSON(value any) string { + var b strings.Builder + writeJSON(&b, value, " ", "") + return b.String() +} + +// CompactJSON renders like JSON.stringify(value). +func CompactJSON(value any) string { + var b strings.Builder + writeJSON(&b, value, "", "") + return b.String() +} + +func writeJSON(b *strings.Builder, value any, step, indent string) { + newline, space := "", "" + if step != "" { + newline, space = "\n", " " + } + switch v := value.(type) { + case nil: + b.WriteString("null") + case bool: + if v { + b.WriteString("true") + } else { + b.WriteString("false") + } + case float64: + if math.IsNaN(v) || math.IsInf(v, 0) { + b.WriteString("null") + } else { + b.WriteString(NumberString(v)) + } + case string: + b.WriteString(quoteJSON(v)) + case Timestamp: + b.WriteString(quoteJSON(v.ISO())) + case []any: + if len(v) == 0 { + b.WriteString("[]") + return + } + b.WriteString("[" + newline) + for i, item := range v { + b.WriteString(indent + step) + writeJSON(b, item, step, indent+step) + if i < len(v)-1 { + b.WriteString(",") + } + b.WriteString(newline) + } + b.WriteString(indent + "]") + case *Map: + if v.Len() == 0 { + b.WriteString("{}") + return + } + b.WriteString("{" + newline) + keys := v.Keys() + for i, k := range keys { + b.WriteString(indent + step + quoteJSON(k) + ":" + space) + writeJSON(b, v.values[k], step, indent+step) + if i < len(keys)-1 { + b.WriteString(",") + } + b.WriteString(newline) + } + b.WriteString(indent + "}") + default: + b.WriteString(quoteJSON(fmt.Sprint(v))) + } +} + +// quoteJSON escapes as JSON.stringify does: only quotes, backslashes and control +// characters. Unlike Go's encoder it leaves <, >, & and U+2028/U+2029 as they are. +func quoteJSON(s string) string { + var b strings.Builder + b.WriteByte('"') + for len(s) > 0 { + r, size := utf8.DecodeRuneInString(s) + s = s[size:] + switch { + case r == '"': + b.WriteString(`\"`) + case r == '\\': + b.WriteString(`\\`) + case r == '\b': + b.WriteString(`\b`) + case r == '\f': + b.WriteString(`\f`) + case r == '\n': + b.WriteString(`\n`) + case r == '\r': + b.WriteString(`\r`) + case r == '\t': + b.WriteString(`\t`) + case r < 0x20: + fmt.Fprintf(&b, `\u%04x`, r) + case r == utf8.RuneError && size == 1: + b.WriteString(`�`) + default: + b.WriteRune(r) + } + } + b.WriteByte('"') + return b.String() +} + +// length is a string's length in JavaScript, in UTF-16 code units. +func length(s string) int { + n := 0 + for _, r := range s { + n += utf16.RuneLen(r) + } + return n +} diff --git a/internal/jsyaml/styles.go b/internal/jsyaml/styles.go new file mode 100644 index 0000000..6ad6940 --- /dev/null +++ b/internal/jsyaml/styles.go @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package jsyaml + +import ( + "math" + "regexp" + "strconv" + "strings" + "time" +) + +func isPrintable(c rune) bool { + return c == 0x09 || c == 0x0A || c == 0x0D || (c >= 0x20 && c <= 0x7E) || c == 0x85 || + (c >= 0xA0 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD) || (c >= 0x10000 && c <= 0x10FFFF) +} + +func isNbChar(c rune) bool { return isPrintable(c) && c != '\n' && c != '\r' && c != 0xFEFF } +func isNsChar(c rune) bool { return isNbChar(c) && c != ' ' && c != '\t' } +func isWhite(c rune) bool { return c == ' ' || c == '\t' } + +func isIndicator(c rune) bool { return strings.ContainsRune("-?:,[]{}#&*!|>'\"%@`", c) } + +// isPlainSafe reports whether s matches YAML's ns-plain-multi-line (or, for keys, the +// one-line form) in block context: the regular expressions js-yaml checks, written as +// a scanner because Go's regexp has no lookahead. +func isPlainSafe(s string, oneLine bool) bool { + r := []rune(s) + i := 0 + at := func(j int) (rune, bool) { + if j < len(r) { + return r[j], true + } + return 0, false + } + safeAt := func(j int) bool { + c, ok := at(j) + return ok && isNsChar(c) + } + // plainChar consumes one ns-plain-char and the '#' characters that may follow it. + plainChar := func() bool { + c, ok := at(i) + if !ok { + return false + } + switch { + case c == ':': + if !safeAt(i + 1) { + return false + } + case c == '#' || !isNsChar(c): + return false + } + i++ + for c, ok := at(i); ok && c == '#'; c, ok = at(i) { + i++ + } + return true + } + + c, ok := at(0) + if !ok { + return false + } + switch { + case isNsChar(c) && !isIndicator(c): + i++ + case (c == '?' || c == ':' || c == '-') && safeAt(1): + i++ + default: + return false + } + for c, ok := at(i); ok && c == '#'; c, ok = at(i) { + i++ + } + for i < len(r) { + j := i + for j < len(r) && isWhite(r[j]) { + j++ + } + if j == len(r) { + return false // trailing whitespace + } + if r[j] == '\n' { + if j != i || oneLine { + return false + } + for i < len(r) && r[i] == '\n' { + i++ + } + if !plainChar() { + return false + } + continue + } + i = j + if !plainChar() { + return false + } + } + return true +} + +var forbiddenFirstLine = regexp.MustCompile(`^(?:---|\.\.\.)(?:$|[ \t\n\r])`) + +func canUsePlain(l layout) bool { + if l.value != "" { + if !isPlainSafe(l.value, l.isKey) { + return false + } + if l.shiftOfFirst == 0 && forbiddenFirstLine.MatchString(l.value) { + return false + } + } + resolved := resolveImplicit(l.value) + if resolved != l.tag { + return false + } + return !(l.value == "=" && resolved == "str") +} + +func canUseSingleQuoted(l layout) bool { + for _, c := range l.value { + ok := c == 0x09 || (c >= 0x20 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFF) || c >= 0x10000 + if !ok && !(c == '\n' && !l.isKey) { + return false + } + } + return !strings.Contains(l.value, " \n") && !strings.Contains(l.value, "\t\n") && + !strings.Contains(l.value, "\n ") && !strings.Contains(l.value, "\n\t") +} + +var startsWithSpaceAfterBreaks = regexp.MustCompile(`^\n* `) + +func canUseBlock(l layout) bool { + if l.flowOnly { + return false + } + for _, c := range l.value { + if !isNbChar(c) && c != '\n' { + return false + } + } + contentIndent := l.shiftOfContent - l.shiftOfParent + if contentIndent < 1 { + return false + } + return !(contentIndent > 9 && startsWithSpaceAfterBreaks.MatchString(l.value)) +} + +var ( + coreInt = regexp.MustCompile(`^(?:0o[0-7]+|0x[0-9a-fA-F]+|[-+]?[0-9]+)$`) + coreFloat = regexp.MustCompile(`^(?:[-+]?[0-9]+(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?|[-+]?\.[0-9]+(?:[eE][-+]?[0-9]+)?|[-+]?\.(?:inf|Inf|INF)|\.(?:nan|NaN|NAN))$`) + special = regexp.MustCompile(`^(?:[-+]?\.(?:inf|Inf|INF)|\.(?:nan|NaN|NAN))$`) + date = regexp.MustCompile(`^([0-9]{4})-([0-9]{2})-([0-9]{2})$`) + timestamp = regexp.MustCompile(`^([0-9]{4})-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \t]+)([0-9][0-9]?):([0-9]{2}):([0-9]{2})(?:\.([0-9]*))?(?:[ \t]*(Z|([-+])([0-9][0-9]?)(?::([0-9]{2}))?))?$`) +) + +// resolveImplicit is the tag a plain scalar would read back as under the CLI's schema: +// the YAML core tags, merge keys and timestamps. +func resolveImplicit(s string) string { + switch s { + case "", "~", "null", "Null", "NULL": + return "null" + case "true", "True", "TRUE", "false", "False", "FALSE": + return "bool" + case "<<": + return "merge" + } + if coreInt.MatchString(s) { + return "int" + } + if coreFloat.MatchString(s) { + // JavaScript's parseFloat overflows to Infinity, which only resolves when it was + // written as .inf; otherwise the text stays a string. + if special.MatchString(s) { + return "float" + } + if f, _ := strconv.ParseFloat(s, 64); !math.IsInf(f, 0) { + return "float" + } + } + if isTimestamp(s) { + return "timestamp" + } + return "str" +} + +func isTimestamp(s string) bool { + if m := date.FindStringSubmatch(s); m != nil { + return validDate(m[1], m[2], m[3]) + } + m := timestamp.FindStringSubmatch(s) + if m == nil || !validDate(m[1], m[2], m[3]) { + return false + } + h, _ := strconv.Atoi(m[4]) + mi, _ := strconv.Atoi(m[5]) + sec, _ := strconv.Atoi(m[6]) + if h > 23 || mi > 59 || sec > 59 { + return false + } + if m[9] != "" { + oh, _ := strconv.Atoi(m[10]) + om, _ := strconv.Atoi(m[11]) + if oh > 23 || om > 59 { + return false + } + } + return true +} + +func validDate(y, m, d string) bool { + year, _ := strconv.Atoi(y) + month, _ := strconv.Atoi(m) + day, _ := strconv.Atoi(d) + t := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC) + return t.Year() == year && int(t.Month()) == month && t.Day() == day +} diff --git a/internal/jsyaml/testdata/cases.json b/internal/jsyaml/testdata/cases.json new file mode 100644 index 0000000..69cebcd --- /dev/null +++ b/internal/jsyaml/testdata/cases.json @@ -0,0 +1,1048 @@ +[ + { + "value": { + "s": "" + }, + "yaml": "s: ''\n", + "json": "{\n \"s\": \"\"\n}", + "compact": "{\"s\":\"\"}" + }, + { + "value": { + "s": " " + }, + "yaml": "s: \" \"\n", + "json": "{\n \"s\": \" \"\n}", + "compact": "{\"s\":\" \"}" + }, + { + "value": { + "s": " lead" + }, + "yaml": "s: ' lead'\n", + "json": "{\n \"s\": \" lead\"\n}", + "compact": "{\"s\":\" lead\"}" + }, + { + "value": { + "s": "trail " + }, + "yaml": "s: 'trail '\n", + "json": "{\n \"s\": \"trail \"\n}", + "compact": "{\"s\":\"trail \"}" + }, + { + "value": { + "s": "plain" + }, + "yaml": "s: plain\n", + "json": "{\n \"s\": \"plain\"\n}", + "compact": "{\"s\":\"plain\"}" + }, + { + "value": { + "s": "with: colon" + }, + "yaml": "s: 'with: colon'\n", + "json": "{\n \"s\": \"with: colon\"\n}", + "compact": "{\"s\":\"with: colon\"}" + }, + { + "value": { + "s": "a:b" + }, + "yaml": "s: a:b\n", + "json": "{\n \"s\": \"a:b\"\n}", + "compact": "{\"s\":\"a:b\"}" + }, + { + "value": { + "s": "key #not comment" + }, + "yaml": "s: 'key #not comment'\n", + "json": "{\n \"s\": \"key #not comment\"\n}", + "compact": "{\"s\":\"key #not comment\"}" + }, + { + "value": { + "s": "a #comment" + }, + "yaml": "s: 'a #comment'\n", + "json": "{\n \"s\": \"a #comment\"\n}", + "compact": "{\"s\":\"a #comment\"}" + }, + { + "value": { + "s": "#start" + }, + "yaml": "s: '#start'\n", + "json": "{\n \"s\": \"#start\"\n}", + "compact": "{\"s\":\"#start\"}" + }, + { + "value": { + "s": "*" + }, + "yaml": "s: '*'\n", + "json": "{\n \"s\": \"*\"\n}", + "compact": "{\"s\":\"*\"}" + }, + { + "value": { + "s": "&anchor" + }, + "yaml": "s: '&anchor'\n", + "json": "{\n \"s\": \"&anchor\"\n}", + "compact": "{\"s\":\"&anchor\"}" + }, + { + "value": { + "s": "!tag" + }, + "yaml": "s: '!tag'\n", + "json": "{\n \"s\": \"!tag\"\n}", + "compact": "{\"s\":\"!tag\"}" + }, + { + "value": { + "s": "|pipe" + }, + "yaml": "s: '|pipe'\n", + "json": "{\n \"s\": \"|pipe\"\n}", + "compact": "{\"s\":\"|pipe\"}" + }, + { + "value": { + "s": ">gt" + }, + "yaml": "s: '>gt'\n", + "json": "{\n \"s\": \">gt\"\n}", + "compact": "{\"s\":\">gt\"}" + }, + { + "value": { + "s": "'q'" + }, + "yaml": "s: '''q'''\n", + "json": "{\n \"s\": \"'q'\"\n}", + "compact": "{\"s\":\"'q'\"}" + }, + { + "value": { + "s": "\"dq\"" + }, + "yaml": "s: '\"dq\"'\n", + "json": "{\n \"s\": \"\\\"dq\\\"\"\n}", + "compact": "{\"s\":\"\\\"dq\\\"\"}" + }, + { + "value": { + "s": "%pct" + }, + "yaml": "s: '%pct'\n", + "json": "{\n \"s\": \"%pct\"\n}", + "compact": "{\"s\":\"%pct\"}" + }, + { + "value": { + "s": "@at" + }, + "yaml": "s: '@at'\n", + "json": "{\n \"s\": \"@at\"\n}", + "compact": "{\"s\":\"@at\"}" + }, + { + "value": { + "s": "`tick`" + }, + "yaml": "s: '`tick`'\n", + "json": "{\n \"s\": \"`tick`\"\n}", + "compact": "{\"s\":\"`tick`\"}" + }, + { + "value": { + "s": "-" + }, + "yaml": "s: '-'\n", + "json": "{\n \"s\": \"-\"\n}", + "compact": "{\"s\":\"-\"}" + }, + { + "value": { + "s": "- dash" + }, + "yaml": "s: '- dash'\n", + "json": "{\n \"s\": \"- dash\"\n}", + "compact": "{\"s\":\"- dash\"}" + }, + { + "value": { + "s": "-x" + }, + "yaml": "s: -x\n", + "json": "{\n \"s\": \"-x\"\n}", + "compact": "{\"s\":\"-x\"}" + }, + { + "value": { + "s": "?" + }, + "yaml": "s: '?'\n", + "json": "{\n \"s\": \"?\"\n}", + "compact": "{\"s\":\"?\"}" + }, + { + "value": { + "s": "? q" + }, + "yaml": "s: '? q'\n", + "json": "{\n \"s\": \"? q\"\n}", + "compact": "{\"s\":\"? q\"}" + }, + { + "value": { + "s": ":x" + }, + "yaml": "s: :x\n", + "json": "{\n \"s\": \":x\"\n}", + "compact": "{\"s\":\":x\"}" + }, + { + "value": { + "s": "[br" + }, + "yaml": "s: '[br'\n", + "json": "{\n \"s\": \"[br\"\n}", + "compact": "{\"s\":\"[br\"}" + }, + { + "value": { + "s": "]" + }, + "yaml": "s: ']'\n", + "json": "{\n \"s\": \"]\"\n}", + "compact": "{\"s\":\"]\"}" + }, + { + "value": { + "s": "{x" + }, + "yaml": "s: '{x'\n", + "json": "{\n \"s\": \"{x\"\n}", + "compact": "{\"s\":\"{x\"}" + }, + { + "value": { + "s": "}" + }, + "yaml": "s: '}'\n", + "json": "{\n \"s\": \"}\"\n}", + "compact": "{\"s\":\"}\"}" + }, + { + "value": { + "s": ",comma" + }, + "yaml": "s: ',comma'\n", + "json": "{\n \"s\": \",comma\"\n}", + "compact": "{\"s\":\",comma\"}" + }, + { + "value": { + "s": "a,b" + }, + "yaml": "s: a,b\n", + "json": "{\n \"s\": \"a,b\"\n}", + "compact": "{\"s\":\"a,b\"}" + }, + { + "value": { + "s": "a[b]" + }, + "yaml": "s: a[b]\n", + "json": "{\n \"s\": \"a[b]\"\n}", + "compact": "{\"s\":\"a[b]\"}" + }, + { + "value": { + "s": "x{y}" + }, + "yaml": "s: x{y}\n", + "json": "{\n \"s\": \"x{y}\"\n}", + "compact": "{\"s\":\"x{y}\"}" + }, + { + "value": { + "s": "true" + }, + "yaml": "s: 'true'\n", + "json": "{\n \"s\": \"true\"\n}", + "compact": "{\"s\":\"true\"}" + }, + { + "value": { + "s": "True" + }, + "yaml": "s: 'True'\n", + "json": "{\n \"s\": \"True\"\n}", + "compact": "{\"s\":\"True\"}" + }, + { + "value": { + "s": "TRUE" + }, + "yaml": "s: 'TRUE'\n", + "json": "{\n \"s\": \"TRUE\"\n}", + "compact": "{\"s\":\"TRUE\"}" + }, + { + "value": { + "s": "false" + }, + "yaml": "s: 'false'\n", + "json": "{\n \"s\": \"false\"\n}", + "compact": "{\"s\":\"false\"}" + }, + { + "value": { + "s": "yes" + }, + "yaml": "s: yes\n", + "json": "{\n \"s\": \"yes\"\n}", + "compact": "{\"s\":\"yes\"}" + }, + { + "value": { + "s": "no" + }, + "yaml": "s: no\n", + "json": "{\n \"s\": \"no\"\n}", + "compact": "{\"s\":\"no\"}" + }, + { + "value": { + "s": "on" + }, + "yaml": "s: on\n", + "json": "{\n \"s\": \"on\"\n}", + "compact": "{\"s\":\"on\"}" + }, + { + "value": { + "s": "off" + }, + "yaml": "s: off\n", + "json": "{\n \"s\": \"off\"\n}", + "compact": "{\"s\":\"off\"}" + }, + { + "value": { + "s": "null" + }, + "yaml": "s: 'null'\n", + "json": "{\n \"s\": \"null\"\n}", + "compact": "{\"s\":\"null\"}" + }, + { + "value": { + "s": "Null" + }, + "yaml": "s: 'Null'\n", + "json": "{\n \"s\": \"Null\"\n}", + "compact": "{\"s\":\"Null\"}" + }, + { + "value": { + "s": "~" + }, + "yaml": "s: '~'\n", + "json": "{\n \"s\": \"~\"\n}", + "compact": "{\"s\":\"~\"}" + }, + { + "value": { + "s": "y" + }, + "yaml": "s: y\n", + "json": "{\n \"s\": \"y\"\n}", + "compact": "{\"s\":\"y\"}" + }, + { + "value": { + "s": "n" + }, + "yaml": "s: n\n", + "json": "{\n \"s\": \"n\"\n}", + "compact": "{\"s\":\"n\"}" + }, + { + "value": { + "s": "0" + }, + "yaml": "s: '0'\n", + "json": "{\n \"s\": \"0\"\n}", + "compact": "{\"s\":\"0\"}" + }, + { + "value": { + "s": "42" + }, + "yaml": "s: '42'\n", + "json": "{\n \"s\": \"42\"\n}", + "compact": "{\"s\":\"42\"}" + }, + { + "value": { + "s": "-7" + }, + "yaml": "s: '-7'\n", + "json": "{\n \"s\": \"-7\"\n}", + "compact": "{\"s\":\"-7\"}" + }, + { + "value": { + "s": "+3" + }, + "yaml": "s: '+3'\n", + "json": "{\n \"s\": \"+3\"\n}", + "compact": "{\"s\":\"+3\"}" + }, + { + "value": { + "s": "007" + }, + "yaml": "s: '007'\n", + "json": "{\n \"s\": \"007\"\n}", + "compact": "{\"s\":\"007\"}" + }, + { + "value": { + "s": "0x1F" + }, + "yaml": "s: '0x1F'\n", + "json": "{\n \"s\": \"0x1F\"\n}", + "compact": "{\"s\":\"0x1F\"}" + }, + { + "value": { + "s": "0o17" + }, + "yaml": "s: '0o17'\n", + "json": "{\n \"s\": \"0o17\"\n}", + "compact": "{\"s\":\"0o17\"}" + }, + { + "value": { + "s": "0b101" + }, + "yaml": "s: 0b101\n", + "json": "{\n \"s\": \"0b101\"\n}", + "compact": "{\"s\":\"0b101\"}" + }, + { + "value": { + "s": "1_000" + }, + "yaml": "s: 1_000\n", + "json": "{\n \"s\": \"1_000\"\n}", + "compact": "{\"s\":\"1_000\"}" + }, + { + "value": { + "s": "1.5" + }, + "yaml": "s: '1.5'\n", + "json": "{\n \"s\": \"1.5\"\n}", + "compact": "{\"s\":\"1.5\"}" + }, + { + "value": { + "s": ".5" + }, + "yaml": "s: '.5'\n", + "json": "{\n \"s\": \".5\"\n}", + "compact": "{\"s\":\".5\"}" + }, + { + "value": { + "s": "1." + }, + "yaml": "s: '1.'\n", + "json": "{\n \"s\": \"1.\"\n}", + "compact": "{\"s\":\"1.\"}" + }, + { + "value": { + "s": "1e3" + }, + "yaml": "s: '1e3'\n", + "json": "{\n \"s\": \"1e3\"\n}", + "compact": "{\"s\":\"1e3\"}" + }, + { + "value": { + "s": "1E-3" + }, + "yaml": "s: '1E-3'\n", + "json": "{\n \"s\": \"1E-3\"\n}", + "compact": "{\"s\":\"1E-3\"}" + }, + { + "value": { + "s": ".inf" + }, + "yaml": "s: '.inf'\n", + "json": "{\n \"s\": \".inf\"\n}", + "compact": "{\"s\":\".inf\"}" + }, + { + "value": { + "s": "-.Inf" + }, + "yaml": "s: '-.Inf'\n", + "json": "{\n \"s\": \"-.Inf\"\n}", + "compact": "{\"s\":\"-.Inf\"}" + }, + { + "value": { + "s": ".nan" + }, + "yaml": "s: '.nan'\n", + "json": "{\n \"s\": \".nan\"\n}", + "compact": "{\"s\":\".nan\"}" + }, + { + "value": { + "s": "1e999" + }, + "yaml": "s: 1e999\n", + "json": "{\n \"s\": \"1e999\"\n}", + "compact": "{\"s\":\"1e999\"}" + }, + { + "value": { + "s": "2026-02-25" + }, + "yaml": "s: '2026-02-25'\n", + "json": "{\n \"s\": \"2026-02-25\"\n}", + "compact": "{\"s\":\"2026-02-25\"}" + }, + { + "value": { + "s": "2026-02-30" + }, + "yaml": "s: 2026-02-30\n", + "json": "{\n \"s\": \"2026-02-30\"\n}", + "compact": "{\"s\":\"2026-02-30\"}" + }, + { + "value": { + "s": "2026-02-25T06:35:52.676257Z" + }, + "yaml": "s: '2026-02-25T06:35:52.676257Z'\n", + "json": "{\n \"s\": \"2026-02-25T06:35:52.676257Z\"\n}", + "compact": "{\"s\":\"2026-02-25T06:35:52.676257Z\"}" + }, + { + "value": { + "s": "2026-02-25 06:35:52" + }, + "yaml": "s: '2026-02-25 06:35:52'\n", + "json": "{\n \"s\": \"2026-02-25 06:35:52\"\n}", + "compact": "{\"s\":\"2026-02-25 06:35:52\"}" + }, + { + "value": { + "s": "2026-2-5T1:02:03Z" + }, + "yaml": "s: '2026-2-5T1:02:03Z'\n", + "json": "{\n \"s\": \"2026-2-5T1:02:03Z\"\n}", + "compact": "{\"s\":\"2026-2-5T1:02:03Z\"}" + }, + { + "value": { + "s": "12:30" + }, + "yaml": "s: 12:30\n", + "json": "{\n \"s\": \"12:30\"\n}", + "compact": "{\"s\":\"12:30\"}" + }, + { + "value": { + "s": "1:20:30" + }, + "yaml": "s: 1:20:30\n", + "json": "{\n \"s\": \"1:20:30\"\n}", + "compact": "{\"s\":\"1:20:30\"}" + }, + { + "value": { + "s": "<<" + }, + "yaml": "s: '<<'\n", + "json": "{\n \"s\": \"<<\"\n}", + "compact": "{\"s\":\"<<\"}" + }, + { + "value": { + "s": "=" + }, + "yaml": "s: '='\n", + "json": "{\n \"s\": \"=\"\n}", + "compact": "{\"s\":\"=\"}" + }, + { + "value": { + "s": "---" + }, + "yaml": "s: ---\n", + "json": "{\n \"s\": \"---\"\n}", + "compact": "{\"s\":\"---\"}" + }, + { + "value": { + "s": "--- x" + }, + "yaml": "s: --- x\n", + "json": "{\n \"s\": \"--- x\"\n}", + "compact": "{\"s\":\"--- x\"}" + }, + { + "value": { + "s": "..." + }, + "yaml": "s: ...\n", + "json": "{\n \"s\": \"...\"\n}", + "compact": "{\"s\":\"...\"}" + }, + { + "value": { + "s": "a---" + }, + "yaml": "s: a---\n", + "json": "{\n \"s\": \"a---\"\n}", + "compact": "{\"s\":\"a---\"}" + }, + { + "value": { + "s": "tab\there" + }, + "yaml": "s: \"tab\\there\"\n", + "json": "{\n \"s\": \"tab\\there\"\n}", + "compact": "{\"s\":\"tab\\there\"}" + }, + { + "value": { + "s": "ctrl\u0001" + }, + "yaml": "s: \"ctrl\\x01\"\n", + "json": "{\n \"s\": \"ctrl\\u0001\"\n}", + "compact": "{\"s\":\"ctrl\\u0001\"}" + }, + { + "value": { + "s": "del" + }, + "yaml": "s: \"del\\x7F\"\n", + "json": "{\n \"s\": \"del\"\n}", + "compact": "{\"s\":\"del\"}" + }, + { + "value": { + "s": "nbsp x" + }, + "yaml": "s: \"nbsp\\_x\"\n", + "json": "{\n \"s\": \"nbsp x\"\n}", + "compact": "{\"s\":\"nbsp x\"}" + }, + { + "value": { + "s": "bom" + }, + "yaml": "s: \"bom\\uFEFF\"\n", + "json": "{\n \"s\": \"bom\"\n}", + "compact": "{\"s\":\"bom\"}" + }, + { + "value": { + "s": "line
sep" + }, + "yaml": "s: \"line\\Lsep\"\n", + "json": "{\n \"s\": \"line
sep\"\n}", + "compact": "{\"s\":\"line
sep\"}" + }, + { + "value": { + "s": "multi\nline" + }, + "yaml": "s: |-\n multi\n line\n", + "json": "{\n \"s\": \"multi\\nline\"\n}", + "compact": "{\"s\":\"multi\\nline\"}" + }, + { + "value": { + "s": "multi\nline\n" + }, + "yaml": "s: |\n multi\n line\n", + "json": "{\n \"s\": \"multi\\nline\\n\"\n}", + "compact": "{\"s\":\"multi\\nline\\n\"}" + }, + { + "value": { + "s": "trailing\n\n" + }, + "yaml": "s: |+\n trailing\n\n...\n", + "json": "{\n \"s\": \"trailing\\n\\n\"\n}", + "compact": "{\"s\":\"trailing\\n\\n\"}" + }, + { + "value": { + "s": "\n" + }, + "yaml": "s: \"\\n\"\n", + "json": "{\n \"s\": \"\\n\"\n}", + "compact": "{\"s\":\"\\n\"}" + }, + { + "value": { + "s": "\nlead" + }, + "yaml": "s: |-\n\n lead\n", + "json": "{\n \"s\": \"\\nlead\"\n}", + "compact": "{\"s\":\"\\nlead\"}" + }, + { + "value": { + "s": " \nx" + }, + "yaml": "s: |2-\n \n x\n", + "json": "{\n \"s\": \" \\nx\"\n}", + "compact": "{\"s\":\" \\nx\"}" + }, + { + "value": { + "s": "x\n " + }, + "yaml": "s: |-\n x\n \n", + "json": "{\n \"s\": \"x\\n \"\n}", + "compact": "{\"s\":\"x\\n \"}" + }, + { + "value": { + "s": "a\n\n\nb" + }, + "yaml": "s: |-\n a\n\n\n b\n", + "json": "{\n \"s\": \"a\\n\\n\\nb\"\n}", + "compact": "{\"s\":\"a\\n\\n\\nb\"}" + }, + { + "value": { + "s": " indented\nnext" + }, + "yaml": "s: |2-\n indented\n next\n", + "json": "{\n \"s\": \" indented\\nnext\"\n}", + "compact": "{\"s\":\" indented\\nnext\"}" + }, + { + "value": { + "s": "When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready." + }, + "yaml": "s: >-\n When a single container from steadybit-demo/toys-bestseller fails then within\n 2m all pods are ready.\n", + "json": "{\n \"s\": \"When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\"\n}", + "compact": "{\"s\":\"When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\"}" + }, + { + "value": { + "s": "When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\nWhen a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready." + }, + "yaml": "s: >-\n When a single container from steadybit-demo/toys-bestseller fails then within\n 2m all pods are ready.\n\n When a single container from steadybit-demo/toys-bestseller fails then within\n 2m all pods are ready.\n", + "json": "{\n \"s\": \"When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\\nWhen a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\"\n}", + "compact": "{\"s\":\"When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\\nWhen a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\"}" + }, + { + "value": { + "s": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + }, + "yaml": "s: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", + "json": "{\n \"s\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n}", + "compact": "{\"s\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}" + }, + { + "value": { + "s": "nospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospaceshere tail" + }, + "yaml": "s: >-\n nospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospaceshere\n tail\n", + "json": "{\n \"s\": \"nospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospaceshere tail\"\n}", + "compact": "{\"s\":\"nospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospacesherenospaceshere tail\"}" + }, + { + "value": { + "s": "a b word word word word word word word word word word word word word word word word word word word word " + }, + "yaml": "s: >-\n a b word word word word word word word word word word word word word word\n word word word word word word \n", + "json": "{\n \"s\": \"a b word word word word word word word word word word word word word word word word word word word word \"\n}", + "compact": "{\"s\":\"a b word word word word word word word word word word word word word word word word word word word word \"}" + }, + { + "value": { + "s": "emoji 😀 word word word word word word word word word word word word word word word word word word " + }, + "yaml": "s: >-\n emoji 😀 word word word word word word word word word word word word word word\n word word word word \n", + "json": "{\n \"s\": \"emoji 😀 word word word word word word word word word word word word word word word word word word \"\n}", + "compact": "{\"s\":\"emoji 😀 word word word word word word word word word word word word word word word word word word \"}" + }, + { + "value": { + "s": "ünïcödé text wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd " + }, + "yaml": "s: >-\n ünïcödé text wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd\n wörd wörd wörd \n", + "json": "{\n \"s\": \"ünïcödé text wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd \"\n}", + "compact": "{\"s\":\"ünïcödé text wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd wörd \"}" + }, + { + "value": { + "s": "quote's" + }, + "yaml": "s: quote's\n", + "json": "{\n \"s\": \"quote's\"\n}", + "compact": "{\"s\":\"quote's\"}" + }, + { + "value": { + "s": "back\\slash" + }, + "yaml": "s: back\\slash\n", + "json": "{\n \"s\": \"back\\\\slash\"\n}", + "compact": "{\"s\":\"back\\\\slash\"}" + }, + { + "value": { + "s": "http://x?a=b&c=" + }, + "yaml": "s: http://x?a=b&c=\n", + "json": "{\n \"s\": \"http://x?a=b&c=\"\n}", + "compact": "{\"s\":\"http://x?a=b&c=\"}" + }, + { + "value": { + "s": "k8s.namespace=\"steadybit-demo\" AND k8s.deployment=\"hot-deals\" AND host.hostname=\"very-long-host-name-value\"" + }, + "yaml": "s: >-\n k8s.namespace=\"steadybit-demo\" AND k8s.deployment=\"hot-deals\" AND\n host.hostname=\"very-long-host-name-value\"\n", + "json": "{\n \"s\": \"k8s.namespace=\\\"steadybit-demo\\\" AND k8s.deployment=\\\"hot-deals\\\" AND host.hostname=\\\"very-long-host-name-value\\\"\"\n}", + "compact": "{\"s\":\"k8s.namespace=\\\"steadybit-demo\\\" AND k8s.deployment=\\\"hot-deals\\\" AND host.hostname=\\\"very-long-host-name-value\\\"\"}" + }, + { + "value": { + "n": 0 + }, + "yaml": "n: 0\n", + "json": "{\n \"n\": 0\n}", + "compact": "{\"n\":0}" + }, + { + "value": { + "n": 1 + }, + "yaml": "n: 1\n", + "json": "{\n \"n\": 1\n}", + "compact": "{\"n\":1}" + }, + { + "value": { + "n": -1 + }, + "yaml": "n: -1\n", + "json": "{\n \"n\": -1\n}", + "compact": "{\"n\":-1}" + }, + { + "value": { + "n": 1.5 + }, + "yaml": "n: 1.5\n", + "json": "{\n \"n\": 1.5\n}", + "compact": "{\"n\":1.5}" + }, + { + "value": { + "n": 0.1 + }, + "yaml": "n: 0.1\n", + "json": "{\n \"n\": 0.1\n}", + "compact": "{\"n\":0.1}" + }, + { + "value": { + "n": 1e+21 + }, + "yaml": "n: 1.e+21\n", + "json": "{\n \"n\": 1e+21\n}", + "compact": "{\"n\":1e+21}" + }, + { + "value": { + "n": 1e-7 + }, + "yaml": "n: 1.e-7\n", + "json": "{\n \"n\": 1e-7\n}", + "compact": "{\"n\":1e-7}" + }, + { + "value": { + "n": 123456789012345680000 + }, + "yaml": "n: 123456789012345680000\n", + "json": "{\n \"n\": 123456789012345680000\n}", + "compact": "{\"n\":123456789012345680000}" + }, + { + "value": { + "n": 5e-324 + }, + "yaml": "n: 5.e-324\n", + "json": "{\n \"n\": 5e-324\n}", + "compact": "{\"n\":5e-324}" + }, + { + "value": { + "n": 9007199254740992 + }, + "yaml": "n: 9007199254740992\n", + "json": "{\n \"n\": 9007199254740992\n}", + "compact": "{\"n\":9007199254740992}" + }, + { + "value": { + "n": 1 + }, + "yaml": "n: 1\n", + "json": "{\n \"n\": 1\n}", + "compact": "{\"n\":1}" + }, + { + "value": { + "n": 100 + }, + "yaml": "n: 100\n", + "json": "{\n \"n\": 100\n}", + "compact": "{\"n\":100}" + }, + { + "value": { + "n": 3.14159 + }, + "yaml": "n: 3.14159\n", + "json": "{\n \"n\": 3.14159\n}", + "compact": "{\"n\":3.14159}" + }, + { + "value": { + "b": true, + "f": false, + "z": null + }, + "yaml": "b: true\nf: false\nz: null\n", + "json": "{\n \"b\": true,\n \"f\": false,\n \"z\": null\n}", + "compact": "{\"b\":true,\"f\":false,\"z\":null}" + }, + { + "value": { + "empty": {}, + "list": [], + "nested": { + "deep": { + "deeper": [ + 1, + "two", + { + "three": 3 + } + ] + } + } + }, + "yaml": "empty: {}\nlist: []\nnested:\n deep:\n deeper:\n - 1\n - two\n - three: 3\n", + "json": "{\n \"empty\": {},\n \"list\": [],\n \"nested\": {\n \"deep\": {\n \"deeper\": [\n 1,\n \"two\",\n {\n \"three\": 3\n }\n ]\n }\n }\n}", + "compact": "{\"empty\":{},\"list\":[],\"nested\":{\"deep\":{\"deeper\":[1,\"two\",{\"three\":3}]}}}" + }, + { + "value": { + "list": [ + [ + 1, + 2 + ], + [], + [ + {} + ], + { + "a": [] + } + ] + }, + "yaml": "list:\n - - 1\n - 2\n - []\n - - {}\n - a: []\n", + "json": "{\n \"list\": [\n [\n 1,\n 2\n ],\n [],\n [\n {}\n ],\n {\n \"a\": []\n }\n ]\n}", + "compact": "{\"list\":[[1,2],[],[{}],{\"a\":[]}]}" + }, + { + "value": { + "2": "two", + "10": "ten", + "b": "b", + "01": "zero-one", + "a": "a", + "-1": "minus" + }, + "yaml": "'2': two\n'10': ten\nb: b\n'01': zero-one\na: a\n'-1': minus\n", + "json": "{\n \"2\": \"two\",\n \"10\": \"ten\",\n \"b\": \"b\",\n \"01\": \"zero-one\",\n \"a\": \"a\",\n \"-1\": \"minus\"\n}", + "compact": "{\"2\":\"two\",\"10\":\"ten\",\"b\":\"b\",\"01\":\"zero-one\",\"a\":\"a\",\"-1\":\"minus\"}" + }, + { + "value": { + "123": 3, + "key with: colon": 1, + "true": 2, + "": 4, + "multi\nkey": 5, + "# hash": 6 + }, + "yaml": "'123': 3\n'key with: colon': 1\n'true': 2\n'': 4\n? \"multi\\nkey\"\n: 5\n'# hash': 6\n", + "json": "{\n \"123\": 3,\n \"key with: colon\": 1,\n \"true\": 2,\n \"\": 4,\n \"multi\\nkey\": 5,\n \"# hash\": 6\n}", + "compact": "{\"123\":3,\"key with: colon\":1,\"true\":2,\"\":4,\"multi\\nkey\":5,\"# hash\":6}" + }, + { + "value": { + "lanes": [ + { + "steps": [ + { + "type": "action", + "parameters": { + "duration": "30s", + "note": "When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready." + }, + "radius": { + "predicate": { + "operator": "AND", + "predicates": [ + { + "key": "k8s.namespace", + "operator": "EQUALS", + "values": [ + "steadybit-demo" + ] + } + ] + } + } + } + ] + } + ] + }, + "yaml": "lanes:\n - steps:\n - type: action\n parameters:\n duration: 30s\n note: >-\n When a single container from steadybit-demo/toys-bestseller fails\n then within 2m all pods are ready.\n radius:\n predicate:\n operator: AND\n predicates:\n - key: k8s.namespace\n operator: EQUALS\n values:\n - steadybit-demo\n", + "json": "{\n \"lanes\": [\n {\n \"steps\": [\n {\n \"type\": \"action\",\n \"parameters\": {\n \"duration\": \"30s\",\n \"note\": \"When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\"\n },\n \"radius\": {\n \"predicate\": {\n \"operator\": \"AND\",\n \"predicates\": [\n {\n \"key\": \"k8s.namespace\",\n \"operator\": \"EQUALS\",\n \"values\": [\n \"steadybit-demo\"\n ]\n }\n ]\n }\n }\n }\n ]\n }\n ]\n}", + "compact": "{\"lanes\":[{\"steps\":[{\"type\":\"action\",\"parameters\":{\"duration\":\"30s\",\"note\":\"When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.\"},\"radius\":{\"predicate\":{\"operator\":\"AND\",\"predicates\":[{\"key\":\"k8s.namespace\",\"operator\":\"EQUALS\",\"values\":[\"steadybit-demo\"]}]}}}]}]}" + } +] diff --git a/internal/jsyaml/testdata/generate.mjs b/internal/jsyaml/testdata/generate.mjs new file mode 100644 index 0000000..35db336 --- /dev/null +++ b/internal/jsyaml/testdata/generate.mjs @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Regenerates cases.json: values and what the TypeScript CLI rendered for them with +// js-yaml and JSON.stringify. Run from the repository root after `npm run build`: +// node internal/jsyaml/testdata/generate.mjs +import fs from 'node:fs'; +const { dump } = await import(new URL('../../../dist/yaml.js', import.meta.url)); + +const long = 'When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.'; +const strings = [ + '', ' ', ' lead', 'trail ', 'plain', 'with: colon', 'a:b', 'key #not comment', 'a #comment', '#start', + '*', '&anchor', '!tag', '|pipe', '>gt', "'q'", '"dq"', '%pct', '@at', '`tick`', '-', '- dash', '-x', '?', '? q', ':x', + '[br', ']', '{x', '}', ',comma', 'a,b', 'a[b]', 'x{y}', + 'true', 'True', 'TRUE', 'false', 'yes', 'no', 'on', 'off', 'null', 'Null', '~', 'y', 'n', + '0', '42', '-7', '+3', '007', '0x1F', '0o17', '0b101', '1_000', '1.5', '.5', '1.', '1e3', '1E-3', '.inf', '-.Inf', '.nan', '1e999', + '2026-02-25', '2026-02-30', '2026-02-25T06:35:52.676257Z', '2026-02-25 06:35:52', '2026-2-5T1:02:03Z', '12:30', '1:20:30', + '<<', '=', '---', '--- x', '...', 'a---', 'tab\there', 'ctrl\u0001', 'del\u007f', 'nbsp x', 'bom', 'line
sep', + 'multi\nline', 'multi\nline\n', 'trailing\n\n', '\n', '\nlead', ' \nx', 'x\n ', 'a\n\n\nb', ' indented\nnext', + long, long + '\n' + long, 'x'.repeat(100), 'nospaceshere'.repeat(9) + ' tail', 'a b ' + 'word '.repeat(20), + 'emoji 😀 ' + 'word '.repeat(18), 'ünïcödé text ' + 'wörd '.repeat(16), 'quote\'s', 'back\\slash', 'http://x?a=b&c=', + 'k8s.namespace="steadybit-demo" AND k8s.deployment="hot-deals" AND host.hostname="very-long-host-name-value"', +]; +// -0 is left out: JSON.stringify writes it as 0, so it cannot round-trip through this file. +const numbers = [0, 1, -1, 1.5, 0.1, 1e21, 1e-7, 123456789012345680000, 5e-324, 2 ** 53, 1.0, 100, 3.14159]; +const values = [ + ...strings.map(s => ({ s })), + ...numbers.map(n => ({ n })), + { b: true, f: false, z: null }, + { empty: {}, list: [], nested: { deep: { deeper: [1, 'two', { three: 3 }] } } }, + { list: [[1, 2], [], [{}], { a: [] }] }, + { '10': 'ten', '2': 'two', b: 'b', '01': 'zero-one', a: 'a', '-1': 'minus' }, + { 'key with: colon': 1, 'true': 2, '123': 3, '': 4, 'multi\nkey': 5, '# hash': 6 }, + { lanes: [{ steps: [{ type: 'action', parameters: { duration: '30s', note: long }, radius: { predicate: { operator: 'AND', predicates: [{ key: 'k8s.namespace', operator: 'EQUALS', values: ['steadybit-demo'] }] } } }] }] }, +]; +const cases = values.map(value => ({ + value, + yaml: dump(value), + json: JSON.stringify(value, undefined, 2), + compact: JSON.stringify(value), +})); +fs.writeFileSync(new URL('./cases.json', import.meta.url), JSON.stringify(cases, undefined, 2) + '\n'); +console.log(`wrote ${cases.length} cases`); diff --git a/internal/jsyaml/value.go b/internal/jsyaml/value.go new file mode 100644 index 0000000..541295a --- /dev/null +++ b/internal/jsyaml/value.go @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package jsyaml writes YAML and JSON exactly as the TypeScript CLI did with js-yaml's +// dump and JSON.stringify. Users keep the files `get` writes in Git; any difference in +// formatting would show up as a change to every one of them after upgrading. +package jsyaml + +import ( + "math" + "sort" + "strconv" + "strings" + "time" +) + +// Map is a JavaScript object: string keys in JavaScript's iteration order. +type Map struct { + keys []string + values map[string]any +} + +func NewMap() *Map { return &Map{values: map[string]any{}} } + +// Set keeps a key's first position when it is set again, as assigning to a JavaScript +// object property does. +func (m *Map) Set(key string, value any) { + if _, exists := m.values[key]; !exists { + m.keys = append(m.keys, key) + } + m.values[key] = value +} + +func (m *Map) Get(key string) (any, bool) { + v, ok := m.values[key] + return v, ok +} + +func (m *Map) Delete(key string) { + if _, ok := m.values[key]; !ok { + return + } + delete(m.values, key) + for i, k := range m.keys { + if k == key { + m.keys = append(m.keys[:i], m.keys[i+1:]...) + return + } + } +} + +// SetFirst puts a key at the start, as the spread `{ key, ...experiment }` did. +func (m *Map) SetFirst(key string, value any) { + m.Delete(key) + m.keys = append([]string{key}, m.keys...) + m.values[key] = value +} + +func (m *Map) Len() int { return len(m.keys) } + +// Keys in JavaScript's order: keys that are array indices first, ascending, then the +// rest in insertion order. JSON.parse and js-yaml both see objects this way. +func (m *Map) Keys() []string { + var indices, others []string + for _, k := range m.keys { + if isArrayIndex(k) { + indices = append(indices, k) + } else { + others = append(others, k) + } + } + sort.SliceStable(indices, func(i, j int) bool { + a, _ := strconv.ParseUint(indices[i], 10, 64) + b, _ := strconv.ParseUint(indices[j], 10, 64) + return a < b + }) + return append(indices, others...) +} + +func isArrayIndex(k string) bool { + if k == "" || len(k) > 10 || (len(k) > 1 && k[0] == '0') { + return false + } + n, err := strconv.ParseUint(k, 10, 64) + return err == nil && n < math.MaxUint32 +} + +// Timestamp is a YAML timestamp read from a file, a JavaScript Date in the TypeScript CLI. +type Timestamp time.Time + +func (t Timestamp) ISO() string { + return time.Time(t).UTC().Format("2006-01-02T15:04:05.000Z") +} + +// NumberString formats a float64 as JavaScript's Number.prototype.toString does. +func NumberString(f float64) string { + switch { + case math.IsNaN(f): + return "NaN" + case math.IsInf(f, 1): + return "Infinity" + case math.IsInf(f, -1): + return "-Infinity" + case f == 0: + return "0" + } + sign := "" + if f < 0 { + sign, f = "-", -f + } + // Shortest round-trip digits, the same ones JavaScript picks. + e := strconv.FormatFloat(f, 'e', -1, 64) + mantissa, exponent, _ := strings.Cut(e, "e") + digits := strings.Replace(mantissa, ".", "", 1) + exp, _ := strconv.Atoi(exponent) + k, n := len(digits), exp+1 + switch { + case k <= n && n <= 21: + return sign + digits + strings.Repeat("0", n-k) + case 0 < n && n <= 21: + return sign + digits[:n] + "." + digits[n:] + case -6 < n && n <= 0: + return sign + "0." + strings.Repeat("0", -n) + digits + } + expSign := "+" + if n-1 < 0 { + expSign = "-" + } + abs := n - 1 + if abs < 0 { + abs = -abs + } + if k == 1 { + return sign + digits + "e" + expSign + strconv.Itoa(abs) + } + return sign + digits[:1] + "." + digits[1:] + "e" + expSign + strconv.Itoa(abs) +} diff --git a/internal/output/document.go b/internal/output/document.go index 462cf8e..05e045c 100644 --- a/internal/output/document.go +++ b/internal/output/document.go @@ -6,218 +6,236 @@ package output import ( "bytes" "encoding/json" + "errors" "fmt" + "io" + "math" "strconv" "strings" + "time" + "github.com/steadybit/cli/internal/jsyaml" "go.yaml.in/yaml/v3" ) -// Document is a JSON or YAML document that keeps its field order. Files written by -// `get` are kept in Git, so the order the platform returns has to survive: a Go map -// would sort the keys and turn every upgrade of the CLI into a diff of every file. +// Document is a JSON or YAML object as the TypeScript CLI held it: a JavaScript value +// with JavaScript's key order and number semantics. Files written from it are kept in +// Git, so they have to come out exactly as they did before. type Document struct { - node *yaml.Node + value *jsyaml.Map } // ParseDocument reads JSON or YAML; JSON is valid YAML, so one parser handles both. -// Anchors and merge keys (`<<:`) are resolved when the document is turned into JSON. +// Anchors, aliases and merge keys (`<<:`) are resolved, as js-yaml's load did. func ParseDocument(content []byte) (*Document, error) { + // JSON is read with a JSON decoder: a YAML parser rejects characters JSON allows + // unescaped, such as DEL, and would fail on a platform response containing one. + if json.Valid(content) { + value, err := decodeJSON(json.NewDecoder(bytes.NewReader(content))) + if err != nil { + return nil, err + } + m, ok := value.(*jsyaml.Map) + if !ok { + return nil, fmt.Errorf("expected an object") + } + return &Document{value: m}, nil + } var node yaml.Node if err := yaml.Unmarshal(content, &node); err != nil { return nil, err } - if node.Kind != yaml.DocumentNode || len(node.Content) != 1 || node.Content[0].Kind != yaml.MappingNode { + if node.Kind != yaml.DocumentNode || len(node.Content) != 1 { return nil, fmt.Errorf("expected an object") } - return &Document{node: node.Content[0]}, nil -} - -func (d *Document) Get(key string) (string, bool) { - for i := 0; i+1 < len(d.node.Content); i += 2 { - if d.node.Content[i].Value == key { - return d.node.Content[i+1].Value, true - } + value, err := toValue(node.Content[0]) + if err != nil { + return nil, err } - return "", false -} - -func (d *Document) Delete(key string) { - for i := 0; i+1 < len(d.node.Content); i += 2 { - if d.node.Content[i].Value == key { - d.node.Content = append(d.node.Content[:i], d.node.Content[i+2:]...) - return - } + m, ok := value.(*jsyaml.Map) + if !ok { + return nil, fmt.Errorf("expected an object") } + return &Document{value: m}, nil } -// SetFirst sets a string field, moving it to the top, where `key` and `id` belong. -func (d *Document) SetFirst(key, value string) { - d.Delete(key) - d.node.Content = append([]*yaml.Node{ - {Kind: yaml.ScalarNode, Tag: "!!str", Value: key}, - {Kind: yaml.ScalarNode, Tag: "!!str", Value: value}, - }, d.node.Content...) +func NewDocument(m *jsyaml.Map) *Document { return &Document{value: m} } + +func (d *Document) Value() *jsyaml.Map { return d.value } + +func (d *Document) Get(key string) (string, bool) { + v, ok := d.value.Get(key) + s, isString := v.(string) + return s, ok && isString } +func (d *Document) Delete(key string) { d.value.Delete(key) } +func (d *Document) SetFirst(key, value string) { d.value.SetFirst(key, value) } + +// Render as `get` printed it: JSON.stringify(value, undefined, 2), or js-yaml's dump. func (d *Document) Render(datatype Datatype) ([]byte, error) { if datatype == JSON { - var buf bytes.Buffer - if err := writeJSON(&buf, d.node, ""); err != nil { - return nil, err - } - buf.WriteByte('\n') - return buf.Bytes(), nil - } - restyle(d.node) - var buf bytes.Buffer - encoder := yaml.NewEncoder(&buf) - encoder.SetIndent(2) - if err := encoder.Encode(d.node); err != nil { - return nil, err + return []byte(jsyaml.JSON(d.value) + "\n"), nil } - return buf.Bytes(), encoder.Close() -} - -// MarshalJSON lets a document be sent as a request body. -func (d *Document) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer - err := writeJSON(&buf, d.node, "") - return buf.Bytes(), err + return []byte(jsyaml.Dump(d.value)), nil } -// restyle drops the flow style and double quotes that parsing JSON leaves on every node, -// and quotes a string the way js-yaml did: single quotes when it would otherwise read as -// something else, double quotes only when it needs escapes. -func restyle(node *yaml.Node) { - node.Style = 0 - // JavaScript has one number type, so the platform's 1.0 was always written as 1. - if node.Kind == yaml.ScalarNode && node.ShortTag() == "!!float" { - if f, err := strconv.ParseFloat(node.Value, 64); err == nil { - node.Value = strconv.FormatFloat(f, 'f', -1, 64) - if f == float64(int64(f)) { - node.Tag = "!!int" - } - } - } - if node.Kind == yaml.ScalarNode && node.Tag == "!!str" && needsQuotes(node.Value) { - if strings.ContainsAny(node.Value, "\n\t\\") || !strconv.IsPrint(firstUnprintable(node.Value)) { - node.Style = yaml.DoubleQuotedStyle - } else { - node.Style = yaml.SingleQuotedStyle - } - } - if node.Kind == yaml.ScalarNode && node.Tag == "!!str" && strings.Contains(node.Value, "\n") { - node.Style = yaml.LiteralStyle - } - for _, child := range node.Content { - restyle(child) +// RenderFile as the TypeScript CLI wrote files: compact JSON without a newline, or YAML. +func (d *Document) RenderFile(datatype Datatype) []byte { + if datatype == JSON { + return []byte(jsyaml.CompactJSON(d.value)) } + return []byte(jsyaml.Dump(d.value)) } -func firstUnprintable(s string) rune { - for _, r := range s { - if !strconv.IsPrint(r) { - return r - } - } - return 'a' +// MarshalJSON sends a document as a request body, as JSON.stringify did. +func (d *Document) MarshalJSON() ([]byte, error) { + return []byte(jsyaml.CompactJSON(d.value)), nil } -// needsQuotes reports whether a plain scalar would not read back as this string. -func needsQuotes(value string) bool { - plain := &yaml.Node{Kind: yaml.ScalarNode, Value: value} - out, err := yaml.Marshal(plain) +// decodeJSON reads the next JSON value in order, with numbers as JavaScript sees them. +func decodeJSON(decoder *json.Decoder) (any, error) { + decoder.UseNumber() + token, err := decoder.Token() if err != nil { - return true + return nil, err } - rendered := strings.TrimSuffix(string(out), "\n") - if rendered != value { - return true + switch t := token.(type) { + case json.Delim: + switch t { + case '{': + m := jsyaml.NewMap() + for decoder.More() { + keyToken, err := decoder.Token() + if err != nil { + return nil, err + } + value, err := decodeJSON(decoder) + if err != nil { + return nil, err + } + m.Set(keyToken.(string), value) + } + _, err := decoder.Token() + return m, err + case '[': + items := []any{} + for decoder.More() { + item, err := decodeJSON(decoder) + if err != nil { + return nil, err + } + items = append(items, item) + } + _, err := decoder.Token() + return items, err + } + case json.Number: + return jsNumber(string(t)), nil + case string, bool, nil: + return t, nil } - var resolved yaml.Node - if err := yaml.Unmarshal(out, &resolved); err != nil || len(resolved.Content) != 1 { - return true + if errors.Is(err, io.EOF) { + return nil, fmt.Errorf("unexpected end of JSON") } - return resolved.Content[0].Tag != "!!str" || isTimestamp(value) + return nil, fmt.Errorf("unexpected JSON token %v", token) } -// js-yaml's default schema reads timestamps as dates, so it quoted them; so do we. -func isTimestamp(value string) bool { - return len(value) >= 10 && value[4] == '-' && value[7] == '-' && strings.IndexFunc(value[:4], func(r rune) bool { return r < '0' || r > '9' }) < 0 +// jsNumber is Number(text): the nearest double, with the sign of zero kept. +func jsNumber(text string) float64 { + f, _ := strconv.ParseFloat(text, 64) + if f == 0 && strings.HasPrefix(text, "-") { + return math.Copysign(0, -1) + } + return f } -// writeJSON renders a node as indented JSON in its own field order, resolving aliases -// and merge keys on the way. -func writeJSON(buf *bytes.Buffer, node *yaml.Node, indent string) error { +func toValue(node *yaml.Node) (any, error) { switch node.Kind { case yaml.AliasNode: - return writeJSON(buf, node.Alias, indent) + return toValue(node.Alias) case yaml.MappingNode: - pairs := mergedPairs(node) - if len(pairs) == 0 { - buf.WriteString("{}") - return nil - } - buf.WriteString("{\n") - for i, pair := range pairs { - key := jsonString(pair[0].Value) - buf.WriteString(indent + " ") - buf.Write(key) - buf.WriteString(": ") - if err := writeJSON(buf, pair[1], indent+" "); err != nil { - return err + m := jsyaml.NewMap() + for _, pair := range mergedPairs(node) { + key, err := toValue(pair[0]) + if err != nil { + return nil, err } - if i < len(pairs)-1 { - buf.WriteByte(',') + value, err := toValue(pair[1]) + if err != nil { + return nil, err } - buf.WriteByte('\n') + m.Set(keyString(key), value) } - buf.WriteString(indent + "}") + return m, nil case yaml.SequenceNode: - if len(node.Content) == 0 { - buf.WriteString("[]") - return nil - } - buf.WriteString("[\n") - for i, item := range node.Content { - buf.WriteString(indent + " ") - if err := writeJSON(buf, item, indent+" "); err != nil { - return err + items := make([]any, 0, len(node.Content)) + for _, child := range node.Content { + item, err := toValue(child) + if err != nil { + return nil, err } - if i < len(node.Content)-1 { - buf.WriteByte(',') - } - buf.WriteByte('\n') + items = append(items, item) } - buf.WriteString(indent + "]") + return items, nil case yaml.ScalarNode: - switch node.ShortTag() { - case "!!null": - buf.WriteString("null") - case "!!bool", "!!int", "!!float": - var v any - if err := node.Decode(&v); err != nil { - return err - } - b, _ := json.Marshal(v) - buf.Write(b) - default: - buf.Write(jsonString(node.Value)) - } - default: - return fmt.Errorf("unsupported YAML node") + return scalarValue(node) + } + return nil, fmt.Errorf("unsupported YAML node at line %d", node.Line) +} + +// keyString is the property name JavaScript would use for a key of any type. +func keyString(key any) string { + switch k := key.(type) { + case string: + return k + case nil: + return "null" + case bool: + return strconv.FormatBool(k) + case float64: + return jsyaml.NumberString(k) + case jsyaml.Timestamp: + return time.Time(k).UTC().Format("Mon Jan 02 2006 15:04:05 GMT+0000 (Coordinated Universal Time)") } - return nil + return fmt.Sprint(key) } -// jsonString encodes like JSON.stringify: `&`, `<` and `>` stay as they are. -func jsonString(value string) []byte { - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - encoder.SetEscapeHTML(false) - _ = encoder.Encode(value) - return bytes.TrimSuffix(buf.Bytes(), []byte("\n")) +func scalarValue(node *yaml.Node) (any, error) { + switch node.ShortTag() { + case "!!null": + return nil, nil + case "!!bool": + var b bool + err := node.Decode(&b) + return b, err + case "!!int": + // JavaScript has one number type; big integers lose precision just as they did. + var i int64 + if err := node.Decode(&i); err == nil { + return float64(i), nil + } + var u uint64 + if err := node.Decode(&u); err == nil { + return float64(u), nil + } + f, err := strconv.ParseFloat(strings.ReplaceAll(node.Value, "_", ""), 64) + return f, err + case "!!float": + var f float64 + if err := node.Decode(&f); err != nil { + return math.NaN(), err + } + return f, nil + case "!!timestamp": + var t time.Time + if err := node.Decode(&t); err != nil { + return node.Value, nil + } + return jsyaml.Timestamp(t), nil + default: + return node.Value, nil + } } // mergedPairs returns a mapping's key/value pairs with `<<` merge keys expanded; keys @@ -227,7 +245,7 @@ func mergedPairs(node *yaml.Node) [][2]*yaml.Node { seen := map[string]bool{} for i := 0; i+1 < len(node.Content); i += 2 { key, value := node.Content[i], node.Content[i+1] - if key.Value == "<<" && key.Tag == "!!merge" { + if key.Value == "<<" && key.ShortTag() == "!!merge" { sources := []*yaml.Node{value} if resolve(value).Kind == yaml.SequenceNode { sources = resolve(value).Content diff --git a/internal/output/document_test.go b/internal/output/document_test.go index 5576694..d3ea829 100644 --- a/internal/output/document_test.go +++ b/internal/output/document_test.go @@ -19,41 +19,43 @@ func render(t *testing.T, input string, datatype Datatype) string { return string(out) } -func TestKeepsThePlatformsFieldOrder(t *testing.T) { - assert.Equal(t, "name: x\nteam: ADM\nactive: true\n", render(t, `{"name":"x","team":"ADM","active":true}`, YAML)) +// JSON.parse keeps the sign of -0, and js-yaml wrote it as a float. +func TestKeepsNegativeZero(t *testing.T) { + assert.Equal(t, "n: -0.0\n", render(t, `{"n":-0}`, YAML)) + assert.Equal(t, "{\n \"n\": 0\n}\n", render(t, `{"n":-0}`, JSON)) } -func TestQuotesLikeJsYaml(t *testing.T) { - out := render(t, `{"a":"*","b":"2026-02-25T06:35:52Z","c":"true","d":"plain","e":"line1\nline2"}`, YAML) - assert.Equal(t, "a: '*'\nb: '2026-02-25T06:35:52Z'\nc: 'true'\nd: plain\ne: |-\n line1\n line2\n", out) +// JSON.parse orders integer-like keys first; the fixture cannot show it, since the +// generator's own JSON.stringify already reordered them. +func TestOrdersKeysAsJavaScriptObjectsDo(t *testing.T) { + assert.Equal(t, "'2': two\n'10': ten\nb: b\n'01': zero-one\n", render(t, `{"b":"b","10":"ten","01":"zero-one","2":"two"}`, YAML)) } -func TestWritesWholeFloatsAsJavaScriptDid(t *testing.T) { - assert.Equal(t, "a: 1\nb: 1.5\n", render(t, `{"a":1.0,"b":1.5}`, YAML)) - assert.Equal(t, "{\n \"a\": 1,\n \"b\": 1.5\n}\n", render(t, `{"a":1.0,"b":1.5}`, JSON)) +// A YAML parser rejects DEL, which JSON allows unescaped. +func TestReadsJSONThatYAMLWouldReject(t *testing.T) { + assert.Equal(t, "s: \"a\\x7Fb\"\n", render(t, "{\"s\":\"a\x7fb\"}", YAML)) } -func TestIndentsSequencesUnderTheirKey(t *testing.T) { - assert.Equal(t, "tags:\n - a\n - b\n", render(t, `{"tags":["a","b"]}`, YAML)) -} - -func TestResolvesMergeKeysWhenSending(t *testing.T) { +func TestResolvesMergeKeysInYAMLFiles(t *testing.T) { doc, err := ParseDocument([]byte("base: &b\n x: 1\n y: 2\nderived:\n <<: *b\n y: 3\n")) require.NoError(t, err) out, err := doc.MarshalJSON() require.NoError(t, err) - assert.JSONEq(t, `{"base":{"x":1,"y":2},"derived":{"y":3,"x":1}}`, string(out)) + assert.Equal(t, `{"base":{"x":1,"y":2},"derived":{"y":3,"x":1}}`, string(out)) } -func TestDoesNotEscapeHTMLInJSON(t *testing.T) { - assert.Equal(t, "{\n \"u\": \"a?b=1&c=\"\n}\n", render(t, `{"u":"a?b=1&c="}`, JSON)) +// js-yaml's load turned timestamps into Dates, which JSON.stringify writes as ISO strings. +func TestSendsYAMLTimestampsAsJavaScriptDates(t *testing.T) { + doc, err := ParseDocument([]byte("startAt: 2030-06-01T09:00:00Z\n")) + require.NoError(t, err) + out, _ := doc.MarshalJSON() + assert.Equal(t, `{"startAt":"2030-06-01T09:00:00.000Z"}`, string(out)) } func TestSetFirstMovesTheKeyToTheTop(t *testing.T) { doc, err := ParseDocument([]byte(`{"name":"x","key":"old"}`)) require.NoError(t, err) doc.SetFirst("key", "ADM-1") - out, err := doc.Render(YAML) - require.NoError(t, err) + out, _ := doc.Render(YAML) assert.Equal(t, "key: ADM-1\nname: x\n", string(out)) } diff --git a/internal/output/parity_test.go b/internal/output/parity_test.go new file mode 100644 index 0000000..04cd652 --- /dev/null +++ b/internal/output/parity_test.go @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package output + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Each case is a value and what the TypeScript CLI rendered for it, recorded from js-yaml +// and JSON.stringify by internal/jsyaml/testdata/generate.mjs. +func TestRendersExactlyLikeTheTypeScriptCLI(t *testing.T) { + content, err := os.ReadFile("../jsyaml/testdata/cases.json") + require.NoError(t, err) + var cases []struct { + Value json.RawMessage `json:"value"` + YAML string `json:"yaml"` + JSON string `json:"json"` + Compact string `json:"compact"` + } + require.NoError(t, json.Unmarshal(content, &cases)) + + for _, c := range cases { + t.Run(string(c.Value), func(t *testing.T) { + doc, err := ParseDocument(c.Value) + require.NoError(t, err) + yamlOut, _ := doc.Render(YAML) + assert.Equal(t, c.YAML, string(yamlOut), "yaml") + jsonOut, _ := doc.Render(JSON) + assert.Equal(t, c.JSON+"\n", string(jsonOut), "json") + assert.Equal(t, c.Compact, string(doc.RenderFile(JSON)), "compact json") + }) + } +} + +// Set JSYAML_CORPUS to a directory recorded by a corpus script: .raw.json with the +// platform's response next to .ts.yaml, .ts.json and .ts.compact.json. +func TestRendersARecordedCorpusExactly(t *testing.T) { + dir := os.Getenv("JSYAML_CORPUS") + if dir == "" { + t.Skip("JSYAML_CORPUS not set") + } + raws, err := filepath.Glob(filepath.Join(dir, "*.raw.json")) + require.NoError(t, err) + require.NotEmpty(t, raws) + failures := 0 + for _, raw := range raws { + base := strings.TrimSuffix(raw, ".raw.json") + content, err := os.ReadFile(raw) + require.NoError(t, err) + doc, err := ParseDocument(content) + require.NoError(t, err) + doc.Delete("version") + for suffix, render := range map[string]func() string{ + ".ts.yaml": func() string { out, _ := doc.Render(YAML); return string(out) }, + ".ts.json": func() string { out, _ := doc.Render(JSON); return strings.TrimSuffix(string(out), "\n") }, + ".ts.compact.json": func() string { return string(doc.RenderFile(JSON)) }, + } { + expected, err := os.ReadFile(base + suffix) + require.NoError(t, err) + if actual := render(); actual != string(expected) { + failures++ + if failures <= 5 { + assert.Equal(t, string(expected), actual, filepath.Base(base)+suffix) + } + } + } + } + t.Logf("%d documents, %d renderings differ", len(raws), failures) + assert.Zero(t, failures) +} diff --git a/internal/platform/client.go b/internal/platform/client.go index d0e8152..692fa28 100644 --- a/internal/platform/client.go +++ b/internal/platform/client.go @@ -99,8 +99,10 @@ func New() (*Client, error) { return nil, fmt.Errorf("invalid base URL '%s': %w", cfg.BaseURL, err) } httpClient := &http.Client{ + // No client-wide timeout: it would also count the time a request waits for the + // rate limiter, which under a dump is far longer than any request takes. The + // transport bounds each attempt instead. Transport: &transport{next: http.DefaultTransport, base: base}, - Timeout: 30 * time.Second, // Requests carry the access token; following a redirect could hand it to another host. CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }, } @@ -127,6 +129,28 @@ func Check(resp *http.Response, body []byte) error { const maxRateLimitWait = 2 * time.Minute +const defaultTimeout = 30 * time.Second + +type timeoutKey struct{} + +// WithTimeout gives the requests made with ctx a longer deadline than the default 30 +// seconds, as artifact downloads need. +func WithTimeout(ctx context.Context, d time.Duration) context.Context { + return context.WithValue(ctx, timeoutKey{}, d) +} + +// cancelOnClose ends an attempt's deadline once its body has been read, so the deadline +// bounds the download of the body and not just the wait for the status line. +type cancelOnClose struct { + io.ReadCloser + cancel context.CancelFunc +} + +func (c cancelOnClose) Close() error { + defer c.cancel() + return c.ReadCloser.Close() +} + var idempotent = map[string]bool{"GET": true, "HEAD": true, "OPTIONS": true, "PUT": true, "DELETE": true} // transport retries what is safe to retry: a 429 for any method, since the request was @@ -152,9 +176,17 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { if body != nil { req.Body = io.NopCloser(bytes.NewReader(body)) } - logRequest(req, body) - resp, err := t.next.RoundTrip(req) + Limiter().Acquire() + timeout := defaultTimeout + if d, ok := req.Context().Value(timeoutKey{}).(time.Duration); ok { + timeout = d + } + ctx, cancel := context.WithTimeout(req.Context(), timeout) + sent := req.WithContext(ctx) + logRequest(sent, body) + resp, err := t.next.RoundTrip(sent) if err != nil { + cancel() if !idempotent[req.Method] || attempt >= 4 { return nil, fmt.Errorf("failed to call Steadybit API at %s %s: %w", req.Method, req.URL, err) } @@ -163,6 +195,7 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { } logResponse(resp) if resp.StatusCode != http.StatusTooManyRequests { + resp.Body = cancelOnClose{ReadCloser: resp.Body, cancel: cancel} return resp, nil } wait := time.Second @@ -173,9 +206,11 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { } } if waited+wait > maxRateLimitWait { + resp.Body = cancelOnClose{ReadCloser: resp.Body, cancel: cancel} return resp, nil } _ = resp.Body.Close() + cancel() time.Sleep(wait) waited += wait } diff --git a/internal/platform/ratelimit.go b/internal/platform/ratelimit.go new file mode 100644 index 0000000..8498b46 --- /dev/null +++ b/internal/platform/ratelimit.go @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package platform + +import ( + "fmt" + "math" + "os" + "regexp" + "strconv" + "strings" + "sync" + "time" +) + +// The platform limits requests with a token bucket: a burst of 100, refilled by 25 every +// 15 seconds. A fan-out like `experiment dump` issues far more than that, and every +// rejected request would retry into the window it just exhausted, so requests are paced +// to the documented allowance from the first one. The ratelimit-* headers cannot be used +// instead: they appear only on the 429 itself, and report the burst but not the refill. +type Bucket struct { + Burst int + RefillTokens int + RefillInterval time.Duration +} + +var DefaultBucket = Bucket{Burst: 100, RefillTokens: 25, RefillInterval: 15 * time.Second} + +// Plain decimal digits only: '1e3' or '0x10' are not how anyone writes a request count. +var positiveInteger = regexp.MustCompile(`^\d+$`) + +// BucketFromEnvironment reads the STEADYBIT_RATE_LIMIT_* overrides. An invalid value is +// warned about rather than ignored quietly, since it would change how hard the CLI hits +// the platform. +func BucketFromEnvironment() Bucket { + read := func(name string, fallback int) int { + value, ok := os.LookupEnv(name) + trimmed := strings.TrimSpace(value) + if !ok || trimmed == "" { + return fallback + } + n, err := strconv.Atoi(trimmed) + if !positiveInteger.MatchString(trimmed) || err != nil || n <= 0 { + fmt.Fprintf(os.Stderr, "Ignoring %s: '%s' is not a positive whole number. Using %d.\n", name, value, fallback) + return fallback + } + return n + } + return Bucket{ + Burst: read("STEADYBIT_RATE_LIMIT_BURST", DefaultBucket.Burst), + RefillTokens: read("STEADYBIT_RATE_LIMIT_REFILL", DefaultBucket.RefillTokens), + RefillInterval: time.Duration(read("STEADYBIT_RATE_LIMIT_INTERVAL", int(DefaultBucket.RefillInterval/time.Second))) * time.Second, + } +} + +// Clock is injected so tests can drive the bucket deterministically. +type Clock interface { + Now() time.Time + Sleep(time.Duration) +} + +type systemClock struct{} + +func (systemClock) Now() time.Time { return time.Now() } +func (systemClock) Sleep(d time.Duration) { time.Sleep(d) } + +type RateLimiter struct { + mu sync.Mutex + bucket Bucket + clock Clock + tokens float64 + lastRefill time.Time +} + +func NewRateLimiter(bucket Bucket, clock Clock) *RateLimiter { + if clock == nil { + clock = systemClock{} + } + return &RateLimiter{bucket: bucket, clock: clock, tokens: float64(bucket.Burst), lastRefill: clock.Now()} +} + +// Acquire blocks until a request may be sent. Callers are served one at a time, so +// concurrent ones cannot all spend the same token. +func (r *RateLimiter) Acquire() { + r.mu.Lock() + defer r.mu.Unlock() + for { + r.refill() + if r.tokens >= 1 { + r.tokens-- + return + } + r.clock.Sleep(r.untilNextToken()) + } +} + +// DurationFor is how long `count` requests take once the burst is spent, which makes +// the scale of a large dump visible before it starts rather than an hour into it. +func (r *RateLimiter) DurationFor(count int) time.Duration { + beyond := max(0, count-r.bucket.Burst) + return time.Duration(float64(beyond) / float64(r.bucket.RefillTokens) * float64(r.bucket.RefillInterval)) +} + +func (r *RateLimiter) perNanosecond() float64 { + return float64(r.bucket.RefillTokens) / float64(r.bucket.RefillInterval) +} + +func (r *RateLimiter) refill() { + now := r.clock.Now() + r.tokens = math.Min(float64(r.bucket.Burst), r.tokens+float64(now.Sub(r.lastRefill))*r.perNanosecond()) + r.lastRefill = now +} + +func (r *RateLimiter) untilNextToken() time.Duration { + return max(time.Millisecond, time.Duration(math.Ceil((1-r.tokens)/r.perNanosecond()))) +} + +var ( + sharedLimiter *RateLimiter + sharedLimiterOnce sync.Once +) + +// Limiter is built on first use, so that a command that sends nothing never reads, or +// complains about, the environment. +func Limiter() *RateLimiter { + sharedLimiterOnce.Do(func() { sharedLimiter = NewRateLimiter(BucketFromEnvironment(), nil) }) + return sharedLimiter +} diff --git a/internal/platform/ratelimit_test.go b/internal/platform/ratelimit_test.go new file mode 100644 index 0000000..c77b14d --- /dev/null +++ b/internal/platform/ratelimit_test.go @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package platform + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +type fakeClock struct{ now time.Time } + +func (c *fakeClock) Now() time.Time { return c.now } +func (c *fakeClock) Sleep(d time.Duration) { c.now = c.now.Add(d) } + +func TestSpendsTheBurstWithoutWaiting(t *testing.T) { + clock := &fakeClock{now: time.Unix(0, 0)} + limiter := NewRateLimiter(Bucket{Burst: 3, RefillTokens: 1, RefillInterval: time.Second}, clock) + + for range 3 { + limiter.Acquire() + } + + assert.Equal(t, time.Unix(0, 0), clock.now) +} + +func TestPacesToTheRefillOnceTheBurstIsSpent(t *testing.T) { + clock := &fakeClock{now: time.Unix(0, 0)} + limiter := NewRateLimiter(Bucket{Burst: 2, RefillTokens: 25, RefillInterval: 15 * time.Second}, clock) + + for range 2 + 25 { + limiter.Acquire() + } + + assert.InDelta(t, 15*time.Second, clock.now.Sub(time.Unix(0, 0)), float64(10*time.Millisecond)) +} + +func TestEstimatesTheDurationOfALargeWalk(t *testing.T) { + limiter := NewRateLimiter(DefaultBucket, &fakeClock{}) + + assert.Equal(t, time.Duration(0), limiter.DurationFor(100)) + assert.Equal(t, 60*time.Second, limiter.DurationFor(200)) +} + +func TestReadsOverridesAndIgnoresInvalidOnes(t *testing.T) { + t.Setenv("STEADYBIT_RATE_LIMIT_BURST", " 50 ") + t.Setenv("STEADYBIT_RATE_LIMIT_REFILL", "1e3") + t.Setenv("STEADYBIT_RATE_LIMIT_INTERVAL", "0x10") + + assert.Equal(t, Bucket{Burst: 50, RefillTokens: 25, RefillInterval: 15 * time.Second}, BucketFromEnvironment()) +} From 12ddeadba1d2073c8341d3f7e88ffb9d7be56a0f Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 13:50:05 +0200 Subject: [PATCH 3/9] feat(go): port advice, template, execution, schedule, service and service-profile Every command of the TypeScript CLI now exists in the Go CLI with the same flags, messages and output: console-table-printer's table layout, the problem body appended to errors, ids written back into applied files, read-only fields left out of files written by get. Shell completion is new, from cobra, with examples like every other command. The unchanged container e2e suite passes against the Go image. --- go.mod | 2 + go.sum | 4 + internal/advice/advice.go | 95 +++++ internal/cli/advice.go | 35 ++ internal/cli/execution.go | 122 ++++++ internal/cli/experiment.go | 69 +++- internal/cli/root.go | 22 +- internal/cli/schedule.go | 135 +++++++ internal/cli/service.go | 258 +++++++++++++ internal/cli/template.go | 55 +++ internal/execution/execution.go | 282 ++++++++++++++ internal/experiment/dump.go | 4 +- internal/experiment/experiment.go | 82 ++-- internal/experiment/template.go | 219 +++++++++++ internal/jsyaml/value.go | 22 ++ internal/output/document.go | 18 + internal/platform/client.go | 2 +- internal/platform/response.go | 124 ++++++ internal/resource/resource.go | 121 ++++++ internal/schedule/schedule.go | 245 ++++++++++++ internal/service/service.go | 441 ++++++++++++++++++++++ internal/serviceprofile/serviceprofile.go | 172 +++++++++ internal/table/table.go | 135 +++++++ internal/table/table_test.go | 27 ++ internal/template/template.go | 104 +++++ 25 files changed, 2716 insertions(+), 79 deletions(-) create mode 100644 internal/advice/advice.go create mode 100644 internal/cli/advice.go create mode 100644 internal/cli/execution.go create mode 100644 internal/cli/schedule.go create mode 100644 internal/cli/service.go create mode 100644 internal/cli/template.go create mode 100644 internal/execution/execution.go create mode 100644 internal/experiment/template.go create mode 100644 internal/platform/response.go create mode 100644 internal/resource/resource.go create mode 100644 internal/schedule/schedule.go create mode 100644 internal/service/service.go create mode 100644 internal/serviceprofile/serviceprofile.go create mode 100644 internal/table/table.go create mode 100644 internal/table/table_test.go create mode 100644 internal/template/template.go diff --git a/go.mod b/go.mod index 429f53d..e4fba85 100644 --- a/go.mod +++ b/go.mod @@ -14,12 +14,14 @@ require ( require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect github.com/getkin/kin-openapi v0.142.0 // indirect github.com/go-openapi/jsonpointer v0.23.1 // indirect github.com/go-openapi/swag/jsonname v0.26.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mattn/go-runewidth v0.0.30 // indirect github.com/oapi-codegen/oapi-codegen/v2 v2.8.0 // indirect github.com/oasdiff/yaml v0.1.1 // indirect github.com/oasdiff/yaml3 v0.0.14 // indirect diff --git a/go.sum b/go.sum index f167d36..4d09c8e 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= +github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -55,6 +57,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-runewidth v0.0.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk= +github.com/mattn/go-runewidth v0.0.30/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= diff --git a/internal/advice/advice.go b/internal/advice/advice.go new file mode 100644 index 0000000..0900bc3 --- /dev/null +++ b/internal/advice/advice.go @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package advice implements `advice validate-status`. +package advice + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/table" +) + +type Options struct { + Environment string + Query string + Status string +} + +type item struct { + Target struct { + Reference string `json:"reference"` + } `json:"target"` + Advice struct { + Label string `json:"label"` + Status string `json:"status"` + } `json:"advice"` +} + +type page struct { + TotalItems int `json:"totalItems"` + NextOffset *int64 `json:"nextOffset"` + Items []item `json:"items"` +} + +var separators = regexp.MustCompile(`[\s_-]+`) + +// The platform reports IMPLEMENTED while --status defaults to Implemented; case and the +// separator are ignored, so `action needed` matches ACTION_NEEDED too. +func sameStatus(reported, expected string) bool { + normalise := func(s string) string { return separators.ReplaceAllString(strings.ToLower(strings.TrimSpace(s)), "_") } + return normalise(reported) == normalise(expected) +} + +func fetchAll(ctx context.Context, c *platform.Client, o Options) ([]item, error) { + var all []item + offset := int64(0) + for { + request := api.GetAdviceApiRequestAO{EnvironmentName: o.Environment, Offset: &offset} + if o.Query != "" { + request.Query = &o.Query + } + resp, err := c.GetTargetAdviceSummary(ctx, request) + var p page + if _, err := platform.Decode(resp, err, &p); err != nil { + return nil, platform.Failed(err, "Failed to fetch advice status. HTTP request failed.") + } + if len(p.Items) > 0 { + all = append(all, p.Items...) + fmt.Printf("Fetched %d of %d matching advice.\n", len(all), p.TotalItems) + } else { + fmt.Println("No matching advice.") + } + if p.NextOffset == nil || *p.NextOffset <= 0 { + return all, nil + } + offset = *p.NextOffset + } +} + +func ValidateStatus(ctx context.Context, c *platform.Client, o Options) error { + all, err := fetchAll(ctx, c, o) + if err != nil || len(all) == 0 { + return err + } + errors := 0 + t := table.New() + for _, a := range all { + color := table.Green + if !sameStatus(a.Advice.Status, o.Status) { + errors++ + color = table.Red + } + t.AddRow(color, table.Cell("target", a.Target.Reference), table.Cell("advice", a.Advice.Label), table.Cell("status", a.Advice.Status)) + } + t.Print() + if errors > 0 { + return fmt.Errorf("%d of %d advice did not match the expected status.", errors, len(all)) + } + return nil +} diff --git a/internal/cli/advice.go b/internal/cli/advice.go new file mode 100644 index 0000000..6167c5b --- /dev/null +++ b/internal/cli/advice.go @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "context" + + "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/advice" + "github.com/steadybit/cli/internal/platform" +) + +func newAdvice() *cobra.Command { + cmd := &cobra.Command{Use: "advice", Short: "Show/verify advice status."} + var o advice.Options + validate := &cobra.Command{ + Use: "validate-status", + Short: "Validates the status of one or multiple advice for a given environment and an optional query.", + Args: cobra.NoArgs, + Example: examples( + "steadybit advice validate-status -e Global", + `steadybit advice validate-status -e Global -q "k8s.cluster-name=dev-demo and k8s.namespace=steadybit-demo"`, + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return advice.ValidateStatus(ctx, c, o) + }), + } + validate.Flags().StringVarP(&o.Environment, "environment", "e", "", "The environment name.") + validate.Flags().StringVarP(&o.Status, "status", "s", "Implemented", "The expected status of the advice.") + validate.Flags().StringVarP(&o.Query, "query", "q", "", "(optional) A target query to filter advice by targets.") + _ = validate.MarkFlagRequired("environment") + cmd.AddCommand(validate) + return cmd +} diff --git a/internal/cli/execution.go b/internal/cli/execution.go new file mode 100644 index 0000000..841a985 --- /dev/null +++ b/internal/cli/execution.go @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "context" + + "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/execution" + "github.com/steadybit/cli/internal/platform" +) + +func runID(cmd *cobra.Command, id *int64) { + cmd.Flags().Int64VarP(id, "id", "i", 0, "The experiment run id.") + _ = cmd.MarkFlagRequired("id") +} + +func newExecution() *cobra.Command { + cmd := &cobra.Command{Use: "execution", Short: "Inspect, cancel and annotate experiment runs, and download their artifacts."} + + var g execution.GetOptions + get := &cobra.Command{ + Use: "get", + Short: "Get an experiment run, including its steps and target executions. Output is written to file or stdout.", + Args: cobra.NoArgs, + Example: examples("steadybit execution get -i 1234", "steadybit execution get -i 1234 -t json | jq .state"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return execution.Get(ctx, c, g) }), + } + runID(get, &g.ID) + get.Flags().StringVarP(&g.File, "file", "f", "", "The path to write the experiment run to.") + get.Flags().StringVarP(&g.Type, "type", "t", "", typeHelp) + + var cancelID int64 + cancel := &cobra.Command{ + Use: "cancel", + Short: "Cancel a running experiment run. The run stops as soon as its agents have been told.", + Args: cobra.NoArgs, + Example: examples("steadybit execution cancel -i 1234"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return execution.Cancel(ctx, c, cancelID) + }), + } + runID(cancel, &cancelID) + + property := &cobra.Command{Use: "property", Short: "Change the properties of an experiment run."} + var s execution.PropertyOptions + set := &cobra.Command{ + Use: "set", + Short: "Set the value of a property of an experiment run. Only properties editable in a run can be changed. Several --value set a list property.", + Args: cobra.NoArgs, + Example: examples( + `steadybit execution property set -i 1234 -k approvedBy --value "Jane Doe"`, + "steadybit execution property set -i 1234 -k tickets --value SHOP-1 SHOP-2", + "steadybit execution property set -i 1234 -k score --value 7 --json", + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return execution.SetProperty(ctx, c, s) + }), + } + runID(set, &s.ID) + set.Flags().StringVarP(&s.Key, "key", "k", "", "The property key.") + set.Flags().StringArrayVar(&s.Values, "value", nil, "The value to set.") + set.Flags().BoolVar(&s.JSON, "json", false, "Parse each value as JSON, to send a number or an object.") + _ = set.MarkFlagRequired("key") + _ = set.MarkFlagRequired("value") + variadic(set, "value") + + var a execution.PropertyOptions + var addValue string + add := &cobra.Command{ + Use: "add", + Short: "Add a value to a list property of an experiment run.", + Args: cobra.NoArgs, + Example: examples("steadybit execution property add -i 1234 -k tickets --value SHOP-3"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + a.Values = []string{addValue} + return execution.AddProperty(ctx, c, a) + }), + } + runID(add, &a.ID) + add.Flags().StringVarP(&a.Key, "key", "k", "", "The property key.") + add.Flags().StringVar(&addValue, "value", "", "The value to add.") + add.Flags().BoolVar(&a.JSON, "json", false, "Parse the value as JSON, to send a number or an object.") + _ = add.MarkFlagRequired("key") + _ = add.MarkFlagRequired("value") + property.AddCommand(set, add) + + artifact := &cobra.Command{Use: "artifact", Short: "List and download the artifacts of an experiment run."} + var listID int64 + list := &cobra.Command{ + Use: "list", + Short: "List the artifacts that the actions of an experiment run attached.", + Args: cobra.NoArgs, + Example: examples("steadybit execution artifact list -i 1234"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return execution.ListArtifacts(ctx, c, listID) + }), + } + runID(list, &listID) + var d execution.DownloadOptions + download := &cobra.Command{ + Use: "download", + Short: "Download the artifacts of an experiment run into //. Without filters, all of them are downloaded.", + Args: cobra.NoArgs, + Example: examples( + "steadybit execution artifact download -i 1234 -d ./artifacts", + "steadybit execution artifact download -i 1234 -a jmeter-report.zip -o report.zip", + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return execution.Download(ctx, c, d) }), + } + runID(download, &d.ID) + download.Flags().StringVarP(&d.Artifact, "artifact", "a", "", "Only download artifacts with this id, usually the file name.") + download.Flags().StringVar(&d.TargetExecution, "target-execution", "", "Only download artifacts of this target execution.") + download.Flags().StringVarP(&d.Directory, "directory", "d", ".", "The directory to download into.") + download.Flags().StringVarP(&d.Output, "output", "o", "", "Write the artifact to this file instead. Requires exactly one match.") + download.MarkFlagsMutuallyExclusive("output", "directory") + artifact.AddCommand(list, download) + + cmd.AddCommand(get, cancel, property, artifact) + return cmd +} diff --git a/internal/cli/experiment.go b/internal/cli/experiment.go index f2da9f1..2e1820d 100644 --- a/internal/cli/experiment.go +++ b/internal/cli/experiment.go @@ -5,11 +5,13 @@ package cli import ( "context" + "errors" "fmt" "strings" "github.com/spf13/cobra" "github.com/steadybit/cli/internal/experiment" + "github.com/steadybit/cli/internal/jsyaml" "github.com/steadybit/cli/internal/platform" ) @@ -19,27 +21,47 @@ func newExperiment() *cobra.Command { return cmd } -// keyValues is a repeatable KEY=VALUE flag. Only the first `=` separates. -type keyValues map[string]string +// keyValues is a repeatable KEY=VALUE flag, kept in the order given. Only the first `=` +// separates, so a value may contain one, as a URL with a query string does. +type keyValues struct{ values *jsyaml.Map } + +func newKeyValues() *keyValues { return &keyValues{values: jsyaml.NewMap()} } func (k *keyValues) String() string { return "" } func (k *keyValues) Type() string { return "KEY=VALUE" } func (k *keyValues) Set(value string) error { i := strings.Index(value, "=") if i <= 0 { - return fmt.Errorf("'%s' is not in the form KEY=VALUE", value) - } - if *k == nil { - *k = keyValues{} + return fmt.Errorf("'%s' is not in the form KEY=VALUE.", value) } - (*k)[value[:i]] = value[i+1:] + k.values.Set(value[:i], value[i+1:]) return nil } +// addTemplateFlags adds what `run` and `apply` share for creating from a template. +func addTemplateFlags(cmd *cobra.Command, o *experiment.TemplateOptions) { + placeholders, vars := newKeyValues(), newKeyValues() + o.Placeholder, o.Variable = placeholders.values, vars.values + f := cmd.Flags() + f.StringVar(&o.Template, "template", "", "Create the experiment from the experiment template with this id.") + f.StringVar(&o.Team, "team", "", "With --template: the key of the team owning the experiment.") + f.StringVar(&o.Environment, "environment", "", "With --template: the environment the experiment runs in.") + f.StringVar(&o.ExternalID, "external-id", "", "With --template: an identifier of your own. Using the same one again updates the experiment it created before.") + f.VarP(placeholders, "placeholder", "p", "With --template: a placeholder value. Repeat for more.") + f.StringVar(&o.PlaceholdersFile, "placeholders", "", "With --template: a YAML/JSON file mapping placeholder keys to values. -p overrides entries.") + f.Var(vars, "variable", "With --template: an experiment variable to add to the experiment. Repeat for more.") + var noReset bool + f.BoolVar(&noReset, "no-reset-properties", false, "With --template: keep the properties of an existing experiment instead of resetting them to the template.") + // Read once the flags are parsed; the negated flag keeps the default of resetting. + cmd.PreRun = func(*cobra.Command, []string) { o.ResetProperties = !noReset } + cmd.MarkFlagsMutuallyExclusive("template", "file") +} + func newExperimentRun() *cobra.Command { var o experiment.RunOptions var noWait bool - placeholders := keyValues{} + executionVariables := newKeyValues() + o.ExecutionVariable = executionVariables.values cmd := &cobra.Command{ Use: "run", Aliases: []string{"exec"}, @@ -52,7 +74,6 @@ func newExperimentRun() *cobra.Command { ), RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { o.Wait = !noWait - o.Placeholder = placeholders return experiment.Run(ctx, c, o) }), } @@ -65,13 +86,9 @@ func newExperimentRun() *cobra.Command { f.BoolVar(&o.AllowParallel, "allowParallel", false, "Skip the prompt warning about another experiment running and allow always parallel execution.") f.IntVar(&o.Retries, "retries", 0, "Number of retries when the experiment fails validation (e.g., missing targets). 0 means no retry.") f.IntVar(&o.RetryInterval, "retryInterval", 10, "Interval in seconds between retries.") - f.StringVar(&o.Template, "template", "", "Create the experiment from the experiment template with this id.") - f.StringVar(&o.Team, "team", "", "With --template: the key of the team owning the experiment.") - f.StringVar(&o.Environment, "environment", "", "With --template: the environment the experiment runs in.") - f.StringVar(&o.ExternalID, "external-id", "", "With --template: an identifier of your own; reusing it updates the experiment.") - f.VarP(&placeholders, "placeholder", "p", "With --template: a placeholder value. Repeat for more.") + f.Var(executionVariables, "execution-variable", "With --template: a variable for this run only, overriding experiment and environment variables. Repeat for more.") + addTemplateFlags(cmd, &o.TemplateOptions) cmd.MarkFlagsMutuallyExclusive("key", "file") - cmd.MarkFlagsMutuallyExclusive("template", "file") variadic(cmd, "file") return cmd } @@ -96,19 +113,31 @@ func newExperimentGet() *cobra.Command { func newExperimentApply() *cobra.Command { var o experiment.ApplyOptions + var t experiment.TemplateOptions cmd := &cobra.Command{ - Use: "apply", - Short: "Upload an experiment to Steadybit. If a key is provided, an update is performed. Otherwise, the externalId from the file is used to create or update the experiment.", - Args: cobra.NoArgs, - Example: examples("steadybit experiment apply -f experiment.yml", "steadybit experiment apply -f ./experiments -R"), + Use: "apply", + Short: "Upload an experiment to Steadybit. If a key is provided, an update is performed. Otherwise, the externalId from the file is used to create or update the experiment. With --template, the experiment is created from an experiment template instead of a file.", + Args: cobra.NoArgs, + Example: examples( + "steadybit experiment apply -f experiment.yml", + "steadybit experiment apply -f ./experiments -R", + "steadybit experiment apply --template d7e65100-1d20-4980-be87-c351704910b8 --team ADM --external-id shop-latency -p CLUSTER=prod", + "steadybit experiment apply --template d7e65100-1d20-4980-be87-c351704910b8 -k ADM-12 --placeholders values.yml", + ), RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + if t.Template != "" { + return experiment.ApplyTemplate(ctx, c, o.Key, t) + } + if len(o.Files) == 0 { + return errors.New("Either --file or --template must be specified.") + } return experiment.Apply(ctx, c, o) }), } cmd.Flags().StringVarP(&o.Key, "key", "k", "", "The experiment key.") cmd.Flags().StringArrayVarP(&o.Files, "file", "f", nil, "The path to the experiment file or a directory containing multiple files.") cmd.Flags().BoolVarP(&o.Recursive, "recursive", "R", false, "Process the directory used in -f, --file recursively.") - _ = cmd.MarkFlagRequired("file") + addTemplateFlags(cmd, &t) variadic(cmd, "file") return cmd } diff --git a/internal/cli/root.go b/internal/cli/root.go index 914d4cd..0812837 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -59,6 +59,7 @@ func newRoot() *cobra.Command { SilenceErrors: true, Example: examples( "steadybit experiment run -f experiment.yml", + "steadybit schedule list --team ADM", "steadybit experiment --help", ), PersistentPreRun: func(cmd *cobra.Command, _ []string) { @@ -68,7 +69,26 @@ func newRoot() *cobra.Command { root.PersistentFlags().BoolP("verbose", "v", false, "Enable verbose logging") root.Flags().BoolP("version", "V", false, "output the version number") root.SetVersionTemplate("{{.Version}}\n") - root.AddCommand(newConfig(), newExperiment()) + root.AddCommand(newAdvice(), newConfig(), newExecution(), newExperiment(), newSchedule(), newService(), newServiceProfile(), newTemplate()) + // Shell completion is new with the Go CLI; it gets examples like every other command. + root.InitDefaultCompletionCmd() + for _, cmd := range root.Commands() { + if cmd.Name() != "completion" { + continue + } + cmd.Example = examples("steadybit completion zsh > \"${fpath[1]}/_steadybit\"") + shells := map[string]string{ + "bash": "source <(steadybit completion bash)", + "zsh": `steadybit completion zsh > "${fpath[1]}/_steadybit"`, + "fish": "steadybit completion fish > ~/.config/fish/completions/steadybit.fish", + "powershell": "steadybit completion powershell | Out-String | Invoke-Expression", + } + for _, shell := range cmd.Commands() { + if example, ok := shells[shell.Name()]; ok { + shell.Example = examples(example) + } + } + } for _, cmd := range append(root.Commands(), root) { setUsage(cmd) } diff --git a/internal/cli/schedule.go b/internal/cli/schedule.go new file mode 100644 index 0000000..8682bbb --- /dev/null +++ b/internal/cli/schedule.go @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "context" + + "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/schedule" +) + +const scheduleID = "01951394-727f-76a0-8675-c7519ebd0ff5" + +func scheduleIDFlag(cmd *cobra.Command, id *string) { + cmd.Flags().StringVarP(id, "id", "i", "", "The experiment schedule id.") + _ = cmd.MarkFlagRequired("id") +} + +// scheduleFields adds the flags `create` and `update` share. --allow-parallel and +// --no-allow-parallel stay unset unless one is given, so an update leaves it alone. +func scheduleFields(cmd *cobra.Command, f *schedule.Fields) { + vars := newKeyValues() + f.Variables = vars.values + var allow, noAllow bool + flags := cmd.Flags() + flags.StringVar(&f.Cron, "cron", "", `Run repeatedly on this Quartz cron expression (seconds first), e.g. "0 0 9 ? * MON-FRI".`) + flags.StringVar(&f.StartAt, "start-at", "", "Run once at this ISO 8601 time, e.g. 2026-10-01T09:00:00Z.") + flags.StringVar(&f.Timezone, "timezone", "", "The timezone of the cron expression, e.g. Europe/Berlin.") + flags.BoolVar(&allow, "allow-parallel", false, "Run even when another experiment is running.") + flags.BoolVar(&noAllow, "no-allow-parallel", false, "Skip the run when another experiment is running.") + flags.Var(vars, "variable", "A variable for the scheduled runs, overriding experiment and environment variables. Repeat for more.") + cmd.MarkFlagsMutuallyExclusive("allow-parallel", "no-allow-parallel") + cmd.PreRun = func(*cobra.Command, []string) { + switch { + case allow: + f.AllowParallel = &allow + case noAllow: + no := false + f.AllowParallel = &no + } + } +} + +func newSchedule() *cobra.Command { + cmd := &cobra.Command{Use: "schedule", Short: "Schedule experiments."} + + var l schedule.ListOptions + list := &cobra.Command{ + Use: "list", + Short: "List experiment schedules.", + Args: cobra.NoArgs, + Example: examples("steadybit schedule list", "steadybit schedule list --team ADM --experiment ADM-1 ADM-2"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return schedule.List(ctx, c, l) }), + } + list.Flags().StringArrayVar(&l.Teams, "team", nil, "Only list schedules of these teams, by team key.") + list.Flags().StringArrayVar(&l.Experiments, "experiment", nil, "Only list schedules of these experiments, by experiment key.") + variadic(list, "team", "experiment") + + var g schedule.GetOptions + get := &cobra.Command{ + Use: "get", + Short: "Get an experiment schedule. Output is written to file or stdout.", + Args: cobra.NoArgs, + Example: examples("steadybit schedule get -i " + scheduleID + " -f schedule.yml"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return schedule.Get(ctx, c, g) }), + } + scheduleIDFlag(get, &g.ID) + get.Flags().StringVarP(&g.File, "file", "f", "", "The path to write the schedule to.") + get.Flags().StringVarP(&g.Type, "type", "t", "", typeHelp) + + var a schedule.ApplyOptions + apply := &cobra.Command{ + Use: "apply", + Short: "Create or update experiment schedules from files. A file without an id creates a schedule, and the new id is written back to it.", + Args: cobra.NoArgs, + Example: examples("steadybit schedule apply -f schedule.yml", "steadybit schedule apply -f ./schedules -R"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return schedule.Apply(ctx, c, a) }), + } + apply.Flags().StringArrayVarP(&a.Files, "file", "f", nil, "The path to the schedule file or a directory containing multiple files.") + apply.Flags().BoolVarP(&a.Recursive, "recursive", "R", false, "Process the directory used in -f, --file recursively.") + _ = apply.MarkFlagRequired("file") + variadic(apply, "file") + + var cr schedule.CreateOptions + create := &cobra.Command{ + Use: "create", + Short: "Schedule an experiment, either repeatedly with --cron or once with --start-at.", + Args: cobra.NoArgs, + Example: examples( + `steadybit schedule create -k ADM-1 --cron "0 0 9 ? * MON-FRI" --timezone Europe/Berlin`, + "steadybit schedule create -k ADM-1 --start-at 2026-10-01T09:00:00Z --no-allow-parallel", + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return schedule.Create(ctx, c, cr) }), + } + create.Flags().StringVarP(&cr.Experiment, "experiment", "k", "", "The key of the experiment to schedule.") + create.Flags().BoolVar(&cr.Disabled, "disabled", false, "Create the schedule disabled.") + _ = create.MarkFlagRequired("experiment") + scheduleFields(create, &cr.Fields) + + var u schedule.UpdateOptions + update := &cobra.Command{ + Use: "update", + Short: "Change an experiment schedule. Only the given fields are changed.", + Args: cobra.NoArgs, + Example: examples(`steadybit schedule update -i ` + scheduleID + ` --cron "0 30 8 ? * *"`), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return schedule.Update(ctx, c, u) }), + } + scheduleIDFlag(update, &u.ID) + scheduleFields(update, &u.Fields) + + idCommand := func(use, short string, run func(ctx context.Context, c *platform.Client, id string) error) *cobra.Command { + var id string + c := &cobra.Command{ + Use: use, + Short: short, + Args: cobra.NoArgs, + Example: examples("steadybit schedule " + use + " -i " + scheduleID), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return run(ctx, c, id) }), + } + scheduleIDFlag(c, &id) + return c + } + cmd.AddCommand(list, get, apply, create, update, + idCommand("enable", "Enable an experiment schedule.", func(ctx context.Context, c *platform.Client, id string) error { + return schedule.SetEnabled(ctx, c, id, true) + }), + idCommand("disable", "Disable an experiment schedule without deleting it.", func(ctx context.Context, c *platform.Client, id string) error { + return schedule.SetEnabled(ctx, c, id, false) + }), + idCommand("delete", "Delete an experiment schedule.", schedule.Delete), + ) + return cmd +} diff --git a/internal/cli/service.go b/internal/cli/service.go new file mode 100644 index 0000000..9990d77 --- /dev/null +++ b/internal/cli/service.go @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "context" + + "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/service" + "github.com/steadybit/cli/internal/serviceprofile" +) + +const ( + serviceID = "019cd80d-a4c9-775b-bdf8-2672a280ce7c" + profileID = "019eacd7-fb2c-733a-bed5-99a935323db5" +) + +func idFlag(cmd *cobra.Command, id *string, help string) { + cmd.Flags().StringVarP(id, "id", "i", "", help) + _ = cmd.MarkFlagRequired("id") +} + +func newService() *cobra.Command { + cmd := &cobra.Command{Use: "service", Short: "Manage services, their experiments, variables and risk."} + + var l service.ListOptions + list := &cobra.Command{ + Use: "list", + Short: "List services. Filters of the same kind match any of the given values.", + Args: cobra.NoArgs, + Example: examples("steadybit service list", "steadybit service list --team ADM --environment Global"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return service.List(ctx, c, l) }), + } + list.Flags().StringArrayVar(&l.Teams, "team", nil, "Only list services of these teams, by team key.") + list.Flags().StringArrayVar(&l.Environments, "environment", nil, "Only list services in these environments.") + list.Flags().StringArrayVar(&l.Experiments, "experiment", nil, "Only list services these experiments are linked to.") + variadic(list, "team", "environment", "experiment") + + var g service.GetOptions + get := &cobra.Command{ + Use: "get", + Short: "Get a service. Output is written to file or stdout.", + Args: cobra.NoArgs, + Example: examples("steadybit service get -i " + serviceID + " -f service.yml"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return service.Get(ctx, c, g) }), + } + idFlag(get, &g.ID, "The service id.") + get.Flags().StringVarP(&g.File, "file", "f", "", "The path to write the service to.") + get.Flags().StringVarP(&g.Type, "type", "t", "", typeHelp) + + var a service.ApplyOptions + apply := &cobra.Command{ + Use: "apply", + Short: "Create or update services from files. A file without an id creates a service, and the new id is written back to it.", + Args: cobra.NoArgs, + Example: examples("steadybit service apply -f service.yml", "steadybit service apply -f ./services -R"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return service.Apply(ctx, c, a) }), + } + apply.Flags().StringArrayVarP(&a.Files, "file", "f", nil, "The path to the service file or a directory containing multiple files.") + apply.Flags().BoolVarP(&a.Recursive, "recursive", "R", false, "Process the directory used in -f, --file recursively.") + apply.Flags().BoolVar(&a.DeleteExperiments, "delete-experiments", false, "When the service profile changes, delete provided experiments whose templates the new profile does not contain. Without it, such a change is refused.") + _ = apply.MarkFlagRequired("file") + variadic(apply, "file") + + var deleteID string + del := &cobra.Command{ + Use: "delete", + Short: "Delete a service.", + Args: cobra.NoArgs, + Example: examples("steadybit service delete -i " + serviceID), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return service.Delete(ctx, c, deleteID) + }), + } + idFlag(del, &deleteID, "The service id.") + + var r service.RiskOptions + var failAbove int + risk := &cobra.Command{ + Use: "risk", + Short: "Show the risk score of a service, overall, per category and per experiment.", + Args: cobra.NoArgs, + Example: examples("steadybit service risk -i "+serviceID, "steadybit service risk -i "+serviceID+" --fail-above 50"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return service.Risk(ctx, c, r) + }), + } + idFlag(risk, &r.ID, "The service id.") + risk.Flags().StringVarP(&r.Type, "type", "t", "", `Print the raw risk as "json" or "yaml" instead of tables.`) + risk.Flags().IntVar(&failAbove, "fail-above", 0, "Exit with a non-zero status when the overall risk is above this score.") + risk.PreRun = func(cmd *cobra.Command, _ []string) { + if cmd.Flags().Changed("fail-above") { + r.FailAbove = &failAbove + } + } + + experiments := &cobra.Command{Use: "experiment", Short: "Manage the experiments of a service."} + var el service.ExperimentListOptions + elist := &cobra.Command{ + Use: "list", + Short: "List the experiments of a service: those provided by its service profile, created or not, and custom ones linked to it.", + Args: cobra.NoArgs, + Example: examples("steadybit service experiment list -i "+serviceID, "steadybit service experiment list -i "+serviceID+" --type custom"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return service.ListExperiments(ctx, c, el) + }), + } + idFlag(elist, &el.ID, "The service id.") + elist.Flags().StringArrayVar(&el.Categories, "category", nil, "Only list experiments in these categories.") + elist.Flags().StringArrayVar(&el.Types, "type", nil, `Only list "provided" or "custom" experiments.`) + variadic(elist, "category", "type") + + var p service.ProvideOptions + placeholders := newKeyValues() + p.Placeholder = placeholders.values + var noReset bool + provide := &cobra.Command{ + Use: "provide", + Short: "Create or update a provided experiment of a service from one of its service profile's templates.", + Args: cobra.NoArgs, + Example: examples("steadybit service experiment provide -i " + serviceID + " --template d7e65100-1d20-4980-be87-c351704910b8 -p REPLICAS=3"), + PreRun: func(*cobra.Command, []string) { p.ResetProperties = !noReset }, + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return service.Provide(ctx, c, p) }), + } + idFlag(provide, &p.ID, "The service id.") + provide.Flags().StringVar(&p.Template, "template", "", "The template, which must be part of the service profile.") + provide.Flags().StringVarP(&p.Experiment, "experiment", "k", "", "Update this existing provided experiment instead of creating one.") + provide.Flags().VarP(placeholders, "placeholder", "p", "A placeholder value. Repeat for more.") + provide.Flags().StringVar(&p.PlaceholdersFile, "placeholders", "", "A YAML/JSON file mapping placeholder keys to values. -p overrides entries.") + provide.Flags().BoolVar(&noReset, "no-reset-properties", false, "Keep the properties of an existing experiment instead of resetting them to the template.") + _ = provide.MarkFlagRequired("template") + + var lk struct{ id, experiment, category string } + link := &cobra.Command{ + Use: "link", + Short: "Link an existing experiment to a service as a custom experiment.", + Args: cobra.NoArgs, + Example: examples("steadybit service experiment link -i " + serviceID + " -k ADM-1 --category Redundancy"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return service.Link(ctx, c, lk.id, lk.experiment, lk.category) + }), + } + idFlag(link, &lk.id, "The service id.") + link.Flags().StringVarP(&lk.experiment, "experiment", "k", "", "The experiment to link.") + link.Flags().StringVar(&lk.category, "category", "", "The category to link it in.") + _ = link.MarkFlagRequired("experiment") + _ = link.MarkFlagRequired("category") + + var ul struct{ id, experiment string } + unlink := &cobra.Command{ + Use: "unlink", + Short: "Remove a custom experiment from a service. The experiment itself is kept.", + Args: cobra.NoArgs, + Example: examples("steadybit service experiment unlink -i " + serviceID + " -k ADM-1"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return service.Unlink(ctx, c, ul.id, ul.experiment) + }), + } + idFlag(unlink, &ul.id, "The service id.") + unlink.Flags().StringVarP(&ul.experiment, "experiment", "k", "", "The experiment to unlink.") + _ = unlink.MarkFlagRequired("experiment") + experiments.AddCommand(elist, provide, link, unlink) + + variable := &cobra.Command{Use: "variable", Short: "Manage the variables of a service."} + var vg service.VariableGetOptions + vget := &cobra.Command{ + Use: "get", + Short: "Print the variables of a service.", + Args: cobra.NoArgs, + Example: examples("steadybit service variable get -i " + serviceID), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return service.GetVariables(ctx, c, vg) + }), + } + idFlag(vget, &vg.ID, "The service id.") + vget.Flags().StringVarP(&vg.Type, "type", "t", "yaml", `The output format ("json" or "yaml").`) + var vs service.VariableSetOptions + vset := &cobra.Command{ + Use: "set [KEY=VALUE...]", + Short: "Set variables of a service, keeping the others. With --replace, the given variables become the only ones.", + Example: examples( + "steadybit service variable set -i "+serviceID+" endpoint=http://shop.internal region=eu", + "steadybit service variable set -i "+serviceID+" -f variables.yml --replace", + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, args []string) error { + return service.SetVariables(ctx, c, args, vs) + }), + } + idFlag(vset, &vs.ID, "The service id.") + vset.Flags().StringVarP(&vs.File, "file", "f", "", "A YAML/JSON file mapping variable names to values, which may be lists or select expressions.") + vset.Flags().BoolVar(&vs.Replace, "replace", false, "Remove every variable not given.") + variable.AddCommand(vget, vset) + + cmd.AddCommand(list, get, apply, del, risk, experiments, variable) + return cmd +} + +func newServiceProfile() *cobra.Command { + cmd := &cobra.Command{Use: "service-profile", Short: "Manage the service profiles that provide experiments to services."} + + var l serviceprofile.ListOptions + list := &cobra.Command{ + Use: "list", + Short: "List service profiles.", + Args: cobra.NoArgs, + Example: examples("steadybit service-profile list", "steadybit service-profile list --origin custom"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return serviceprofile.List(ctx, c, l) }), + } + list.Flags().StringVar(&l.Name, "name", "", "Only list profiles whose name contains this.") + list.Flags().StringArrayVar(&l.Origins, "origin", nil, `Only list "provided" or "custom" profiles.`) + list.Flags().BoolVar(&l.Default, "default", false, "Only list the default profile.") + variadic(list, "origin") + + var g serviceprofile.GetOptions + get := &cobra.Command{ + Use: "get", + Short: "Get a service profile. Output is written to file or stdout.", + Args: cobra.NoArgs, + Example: examples("steadybit service-profile get -i " + profileID + " -f profile.yml"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return serviceprofile.Get(ctx, c, g) }), + } + idFlag(get, &g.ID, "The service profile id.") + get.Flags().StringVarP(&g.File, "file", "f", "", "The path to write the service profile to.") + get.Flags().StringVarP(&g.Type, "type", "t", "", typeHelp) + + var a serviceprofile.ApplyOptions + apply := &cobra.Command{ + Use: "apply", + Short: "Create or update service profiles from files. A file without an id creates a profile, and the new id is written back to it.", + Args: cobra.NoArgs, + Example: examples("steadybit service-profile apply -f profile.yml"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return serviceprofile.Apply(ctx, c, a) + }), + } + apply.Flags().StringArrayVarP(&a.Files, "file", "f", nil, "The path to the service profile file or a directory containing multiple files.") + apply.Flags().BoolVarP(&a.Recursive, "recursive", "R", false, "Process the directory used in -f, --file recursively.") + apply.Flags().BoolVar(&a.DeleteExperiments, "delete-experiments", false, "Delete the provided experiments of services that use templates removed from the profile.") + _ = apply.MarkFlagRequired("file") + variadic(apply, "file") + + var deleteID string + del := &cobra.Command{ + Use: "delete", + Short: "Delete a custom service profile.", + Args: cobra.NoArgs, + Example: examples("steadybit service-profile delete -i " + profileID), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return serviceprofile.Delete(ctx, c, deleteID) + }), + } + idFlag(del, &deleteID, "The service profile id.") + + cmd.AddCommand(list, get, apply, del) + return cmd +} diff --git a/internal/cli/template.go b/internal/cli/template.go new file mode 100644 index 0000000..fd90864 --- /dev/null +++ b/internal/cli/template.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "context" + + "github.com/spf13/cobra" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/template" +) + +const typeHelp = `The output format ("json" or "yaml"). (default: "json" if the file ends in ".json", "yaml" otherwise.)` + +func newTemplate() *cobra.Command { + cmd := &cobra.Command{Use: "template", Short: "Find experiment templates to create experiments from."} + + var l template.ListOptions + list := &cobra.Command{ + Use: "list", + Short: "List experiment templates. Filters of the same kind match any of the given values.", + Args: cobra.NoArgs, + Example: examples( + "steadybit template list", + "steadybit template list --search kubernetes --action com.steadybit.extension_host.stress-cpu", + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return template.List(ctx, c, l) }), + } + list.Flags().StringArrayVar(&l.Tags, "tag", nil, "Only list templates with one of these tags.") + list.Flags().StringArrayVar(&l.TargetTypes, "target-type", nil, "Only list templates targeting one of these target types.") + list.Flags().StringArrayVar(&l.Actions, "action", nil, "Only list templates using one of these actions.") + list.Flags().StringArrayVar(&l.Search, "search", nil, "Only list templates whose title or description match.") + variadic(list, "tag", "target-type", "action", "search") + + var g template.GetOptions + get := &cobra.Command{ + Use: "get", + Short: "Get an experiment template. Output is written to file or stdout.", + Args: cobra.NoArgs, + Example: examples( + "steadybit template get -i d7e65100-1d20-4980-be87-c351704910b8", + "steadybit template get -i d7e65100-1d20-4980-be87-c351704910b8 --placeholders -f values.yml", + ), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { return template.Get(ctx, c, g) }), + } + get.Flags().StringVarP(&g.ID, "id", "i", "", "The experiment template id.") + get.Flags().StringVarP(&g.File, "file", "f", "", "The path to write the template to.") + get.Flags().StringVarP(&g.Type, "type", "t", "", typeHelp) + get.Flags().BoolVar(&g.Placeholders, "placeholders", false, "Only output the template placeholders, as a file to fill in and pass to --placeholders.") + _ = get.MarkFlagRequired("id") + + cmd.AddCommand(list, get) + return cmd +} diff --git a/internal/execution/execution.go b/internal/execution/execution.go new file mode 100644 index 0000000..682c30d --- /dev/null +++ b/internal/execution/execution.go @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package execution implements the `execution` commands on experiment runs. +package execution + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/resource" + "github.com/steadybit/cli/internal/table" +) + +func notFoundOr(err error, id int64, format string, args ...any) error { + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Experiment run %d not found.", id) + } + return platform.Failed(err, format, append(args, id)...) +} + +// Fetch gets a run with its steps. The platform leaves the steps out unless asked, and +// with them every target execution and artifact. +func Fetch(ctx context.Context, c *platform.Client, id int64) (*output.Document, error) { + fields := "steps" + doc, _, err := platform.ReadDocument(c.GetExperimentExecution(ctx, id, &api.GetExperimentExecutionParams{Fields: &fields})) + if err != nil { + return nil, notFoundOr(err, id, "Failed to get experiment run %d") + } + return doc, nil +} + +type GetOptions struct { + ID int64 + File, Type string +} + +func Get(ctx context.Context, c *platform.Client, o GetOptions) error { + doc, err := Fetch(ctx, c, o.ID) + if err != nil { + return err + } + if err := resource.Output(doc, o.File, o.Type); err != nil { + return err + } + if o.File != "" { + fmt.Printf("Experiment run %d written to %s.\n", o.ID, o.File) + } + return nil +} + +// Cancel asks the platform to stop a run. A 202 means it is being stopped, a 200 that +// there was nothing left to cancel. +func Cancel(ctx context.Context, c *platform.Client, id int64) error { + _, resp, err := platform.Read(c.CancelExperimentExecution(ctx, id)) + if err != nil { + return notFoundOr(err, id, "Failed to cancel experiment run %d") + } + if resp.StatusCode == http.StatusAccepted { + fmt.Printf("Experiment run %d is being canceled.\n", id) + } else { + fmt.Printf("Experiment run %d has already ended.\n", id) + } + return nil +} + +type PropertyOptions struct { + ID int64 + Key string + Values []string + JSON bool +} + +// Values are sent as strings unless --json asks otherwise. Guessing from the text would +// turn a ticket number such as "0042" into the number 42. +func parseValue(value string, asJSON bool) (json.RawMessage, error) { + if !asJSON { + return json.RawMessage(jsyaml.CompactJSON(value)), nil + } + parsed, err := output.ParseValue([]byte(value)) + if err != nil || !json.Valid([]byte(value)) { + return nil, fmt.Errorf("'%s' is not valid JSON: %s", value, jsonError(value)) + } + return json.RawMessage(jsyaml.CompactJSON(parsed)), nil +} + +func jsonError(value string) string { + var v any + if err := json.Unmarshal([]byte(value), &v); err != nil { + return err.Error() + } + return "invalid JSON" +} + +func SetProperty(ctx context.Context, c *platform.Client, o PropertyOptions) error { + values := make([]json.RawMessage, len(o.Values)) + for i, v := range o.Values { + parsed, err := parseValue(v, o.JSON) + if err != nil { + return err + } + values[i] = parsed + } + // Several values set a list property; a single one stays a scalar. + var body json.RawMessage + if len(values) == 1 { + body = values[0] + } else { + body, _ = json.Marshal(values) + } + _, _, err := platform.Read(c.SetExecutionPropertyValueWithBody(ctx, o.ID, o.Key, "application/json", bytes.NewReader(body))) + return reportProperty(err, "set", o) +} + +func AddProperty(ctx context.Context, c *platform.Client, o PropertyOptions) error { + if len(o.Values) != 1 { + return fmt.Errorf("Adding to a list property takes exactly one --value.") + } + body, err := parseValue(o.Values[0], o.JSON) + if err != nil { + return err + } + _, _, err = platform.Read(c.AddExecutionPropertyValueWithBody(ctx, o.ID, o.Key, "application/json", bytes.NewReader(body))) + return reportProperty(err, "add", o) +} + +func reportProperty(err error, operation string, o PropertyOptions) error { + if err != nil { + return notFoundOr(err, o.ID, fmt.Sprintf("Failed to %s property %s of experiment run ", operation, o.Key)+"%d") + } + fmt.Printf("Property %s of experiment run %d updated.\n", o.Key, o.ID) + return nil +} + +type Artifact struct { + Step, Target, TargetExecutionID, ArtifactID string +} + +func str(m *jsyaml.Map, key string) string { + if m == nil { + return "" + } + v, _ := m.Get(key) + s, _ := v.(string) + return s +} + +func list(m *jsyaml.Map, key string) []any { + if m == nil { + return nil + } + v, _ := m.Get(key) + l, _ := v.([]any) + return l +} + +// Collect gathers artifacts from the target executions of action steps, and of the +// actions a service validation step runs; the platform offers no listing of its own. +func Collect(run *jsyaml.Map) []Artifact { + var artifacts []Artifact + addFrom := func(step string, targets []any) { + for _, t := range targets { + target, _ := t.(*jsyaml.Map) + for _, a := range list(target, "artifacts") { + if id, ok := a.(string); ok { + artifacts = append(artifacts, Artifact{Step: step, Target: str(target, "name"), TargetExecutionID: str(target, "id"), ArtifactID: id}) + } + } + } + } + firstOf := func(values ...string) string { + for _, v := range values { + if v != "" { + return v + } + } + return "" + } + for _, s := range list(run, "steps") { + step, _ := s.(*jsyaml.Map) + label := firstOf(str(step, "customLabel"), str(step, "actionId"), str(step, "stepType")) + addFrom(label, list(step, "targetExecutions")) + for _, v := range list(step, "validations") { + validation, _ := v.(*jsyaml.Map) + addFrom(firstOf(str(validation, "customLabel"), str(validation, "actionId"), label), list(validation, "targetExecutions")) + } + } + return artifacts +} + +func ListArtifacts(ctx context.Context, c *platform.Client, id int64) error { + doc, err := Fetch(ctx, c, id) + if err != nil { + return err + } + artifacts := Collect(doc.Value()) + if len(artifacts) == 0 { + fmt.Printf("Experiment run %d has no artifacts.\n", id) + return nil + } + t := table.New( + table.Column{Name: "artifactId", Title: "Artifact", Alignment: table.Left}, + table.Column{Name: "target", Title: "Target", Alignment: table.Left}, + table.Column{Name: "step", Title: "Step", Alignment: table.Left}, + table.Column{Name: "targetExecutionId", Title: "Target execution", Alignment: table.Left}, + ) + for _, a := range artifacts { + t.AddRow(table.Default, table.Cell("artifactId", a.ArtifactID), table.Cell("target", a.Target), table.Cell("step", a.Step), table.Cell("targetExecutionId", a.TargetExecutionID)) + } + t.Print() + return nil +} + +type DownloadOptions struct { + ID int64 + Artifact, TargetExecution, Directory string + Output string +} + +// PathSegment reduces an id from the platform to one path segment, so that one +// containing "../" cannot write outside the chosen directory; Base alone leaves "..". +func PathSegment(id string) string { + segment := filepath.Base(id) + if segment == "" || segment == "." || segment == ".." || segment == string(filepath.Separator) { + return "_" + } + return segment +} + +// Download writes every selected artifact to //: +// two targets of one step usually produce files of the same name. +func Download(ctx context.Context, c *platform.Client, o DownloadOptions) error { + doc, err := Fetch(ctx, c, o.ID) + if err != nil { + return err + } + var selected []Artifact + for _, a := range Collect(doc.Value()) { + if (o.Artifact == "" || a.ArtifactID == o.Artifact) && (o.TargetExecution == "" || a.TargetExecutionID == o.TargetExecution) { + selected = append(selected, a) + } + } + if len(selected) == 0 { + return fmt.Errorf("No matching artifacts found in experiment run %d.", o.ID) + } + if o.Output != "" && len(selected) > 1 { + return fmt.Errorf("%d artifacts match, but --output takes exactly one. Narrow it down with --artifact and --target-execution.", len(selected)) + } + // Artifacts are reports and log archives, which take far longer than an API response. + ctx = platform.WithTimeout(ctx, 5*time.Minute) + for _, a := range selected { + file := o.Output + if file == "" { + file = filepath.Join(o.Directory, PathSegment(a.TargetExecutionID), PathSegment(a.ArtifactID)) + } + content, _, err := platform.Read(c.GetArtifact(ctx, o.ID, a.TargetExecutionID, a.ArtifactID)) + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Artifact %s of experiment run %d not found.", a.ArtifactID, o.ID) + } + if err != nil { + return platform.Failed(err, "Failed to download artifact %s of experiment run %d", a.ArtifactID, o.ID) + } + if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil { + return err + } + if err := os.WriteFile(file, content, 0o644); err != nil { + return err + } + fmt.Printf("Artifact %s written to %s.\n", a.ArtifactID, file) + } + return nil +} diff --git a/internal/experiment/dump.go b/internal/experiment/dump.go index 0162115..10cd211 100644 --- a/internal/experiment/dump.go +++ b/internal/experiment/dump.go @@ -128,7 +128,7 @@ func Dump(ctx context.Context, c *platform.Client, o DumpOptions) error { // getJSON reads a response into target once the request has been checked. func getJSON(resp *http.Response, err error) func(target any) error { return func(target any) error { - body, _, err := read(resp, err) + body, _, err := platform.Read(resp, err) if err != nil { return err } @@ -246,7 +246,7 @@ func dumpExperiment(ctx context.Context, c *platform.Client, key, dir string, da ok := make([]bool, len(executions.Executions)) forEach(len(ok), executionConcurrency, func(i int) { id := executions.Executions[i].ID - body, _, err := read(c.GetExperimentExecution(ctx, id, nil)) + body, _, err := platform.Read(c.GetExperimentExecution(ctx, id, nil)) if err != nil { return } diff --git a/internal/experiment/experiment.go b/internal/experiment/experiment.go index ce18b31..1f77567 100644 --- a/internal/experiment/experiment.go +++ b/internal/experiment/experiment.go @@ -23,7 +23,6 @@ import ( "strings" "time" - openapi_types "github.com/oapi-codegen/runtime/types" "github.com/steadybit/cli/api" "github.com/steadybit/cli/internal/output" "github.com/steadybit/cli/internal/platform" @@ -34,35 +33,18 @@ type Document = *output.Document const anotherExperimentRunning = "https://steadybit.com/problems/another-experiment-running-exception" -func read(resp *http.Response, err error) ([]byte, *http.Response, error) { - if err != nil { - return nil, nil, err - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, resp, err - } - return body, resp, platform.Check(resp, body) -} - func jsonBody(document any) (io.Reader, error) { b, err := json.Marshal(document) return bytes.NewReader(b), err } -func isStatus(err error, status int) bool { - var apiErr *platform.APIError - return errors.As(err, &apiErr) && apiErr.Status == status -} - func Fetch(ctx context.Context, c *platform.Client, key string) (Document, error) { - body, _, err := read(c.GetExperiment(ctx, key)) - if isStatus(err, http.StatusNotFound) { + body, _, err := platform.Read(c.GetExperiment(ctx, key)) + if platform.IsStatus(err, http.StatusNotFound) { return nil, fmt.Errorf("Experiment %s not found.", key) } if err != nil { - return nil, fmt.Errorf("Failed to get the experiment. HTTP request failed: %w", err) + return nil, platform.Failed(err, "Failed to get the experiment. HTTP request failed.") } document, err := output.ParseDocument(body) if err != nil { @@ -194,12 +176,12 @@ func update(ctx context.Context, c *platform.Client, key string, document Docume if err != nil { return err } - _, _, err = read(c.UpdateExperimentWithBody(ctx, key, "application/json", body)) - if isStatus(err, http.StatusNotFound) { + _, _, err = platform.Read(c.UpdateExperimentWithBody(ctx, key, "application/json", body)) + if platform.IsStatus(err, http.StatusNotFound) { return fmt.Errorf("Experiment %s not found.", key) } if err != nil { - return fmt.Errorf("Failed to save the experiment. HTTP request failed: %w", err) + return platform.Failed(err, "Failed to save the experiment. HTTP request failed.") } return nil } @@ -238,9 +220,9 @@ func Apply(ctx context.Context, c *platform.Client, o ApplyOptions) error { if err != nil { return err } - _, resp, err := read(c.CreateOrUpdateExperimentWithBody(ctx, "application/json", body)) + _, resp, err := platform.Read(c.CreateOrUpdateExperimentWithBody(ctx, "application/json", body)) if err != nil { - return fmt.Errorf("Failed to save the experiment. HTTP request failed: %w", err) + return platform.Failed(err, "Failed to save the experiment. HTTP request failed.") } key = keyFromLocation(resp) if resp.StatusCode == http.StatusCreated { @@ -264,11 +246,7 @@ type RunOptions struct { Retries int RetryInterval int - Template string - Team string - Environment string - ExternalID string - Placeholder map[string]string + TemplateOptions } type started struct { @@ -358,7 +336,7 @@ func withRetries(o RunOptions, run func(parallel bool) (started, error)) (starte continue } } - return result, fmt.Errorf("Failed to execute experiment: %w", err) + return result, platform.Failed(err, "Failed to execute experiment") } } @@ -380,7 +358,7 @@ func decodeStarted(body []byte, resp *http.Response, fallbackKey string) (starte } func runKey(ctx context.Context, c *platform.Client, key string, parallel, persist bool) (started, error) { - body, resp, err := read(c.ExecuteExperimentWithBody(ctx, key, + body, resp, err := platform.Read(c.ExecuteExperimentWithBody(ctx, key, &api.ExecuteExperimentParams{AllowParallel: ¶llel, ForcePersist: &persist}, "application/json", nil)) if err != nil { return started{}, err @@ -407,7 +385,7 @@ func runFile(ctx context.Context, c *platform.Client, o RunOptions, file string, if err != nil { return started{}, err } - body, resp, err := read(c.SaveAndRunWithBody(ctx, + body, resp, err := platform.Read(c.SaveAndRunWithBody(ctx, &api.SaveAndRunParams{AllowParallel: ¶llel, ForcePersist: &persist}, "application/json", reqBody)) if err != nil { return started{}, err @@ -420,28 +398,22 @@ func runFile(ctx context.Context, c *platform.Client, o RunOptions, file string, } func runTemplate(ctx context.Context, c *platform.Client, o RunOptions, parallel, persist bool) (started, error) { - if o.Team == "" { - return started{}, errors.New("--team is required to create an experiment from a template.") - } - var id openapi_types.UUID - if err := id.UnmarshalText([]byte(o.Template)); err != nil { - return started{}, fmt.Errorf("'%s' is not a template id: %w", o.Template, err) - } - placeholders := make([]api.ExperimentTemplatePlaceholderValueAO, 0, len(o.Placeholder)) - for key, value := range o.Placeholder { - placeholders = append(placeholders, api.ExperimentTemplatePlaceholderValueAO{Key: key, Value: value}) + id, err := templateID(o.Template) + if err != nil { + return started{}, err } - request := api.CreateAndRunExperimentFromTemplateAO{Team: o.Team, Placeholders: &placeholders} - if o.Environment != "" { - request.Environment = &o.Environment + create, err := createRequest(o.TemplateOptions) + if err != nil { + return started{}, err } - if o.ExternalID != "" { - request.ExternalId = &o.ExternalID + request := api.CreateAndRunExperimentFromTemplateAO{ + Team: create.Team, Environment: create.Environment, ExternalId: create.ExternalId, + Placeholders: create.Placeholders, ExperimentVariables: create.ExperimentVariables, + ExecutionVariables: variables(o.ExecutionVariable), } - reset := true - body, resp, err := read(c.SaveAndRunFromTemplate(ctx, id, - &api.SaveAndRunFromTemplateParams{ResetProperties: &reset, AllowParallel: ¶llel, ForcePersist: &persist}, request)) - if isStatus(err, http.StatusNotFound) { + body, resp, err := platform.Read(c.SaveAndRunFromTemplate(ctx, id, + &api.SaveAndRunFromTemplateParams{ResetProperties: &o.ResetProperties, AllowParallel: ¶llel, ForcePersist: &persist}, request)) + if platform.IsStatus(err, http.StatusNotFound) { return started{}, fmt.Errorf("Experiment template %s not found.", o.Template) } if err != nil { @@ -461,9 +433,9 @@ func wait(ctx context.Context, c *platform.Client, location string) error { } for { time.Sleep(5 * time.Second) - body, _, err := read(c.Get(ctx, path)) + body, _, err := platform.Read(c.Get(ctx, path)) if err != nil { - return fmt.Errorf("Failed to get experiment run: %w", err) + return platform.Failed(err, "Failed to get experiment run ") } var run struct { ID int64 `json:"id"` diff --git a/internal/experiment/template.go b/internal/experiment/template.go new file mode 100644 index 0000000..08c1c39 --- /dev/null +++ b/internal/experiment/template.go @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package experiment + +import ( + "context" + "errors" + "fmt" + "io/fs" + "net/http" + "os" + "strings" + + openapi_types "github.com/oapi-codegen/runtime/types" + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" +) + +type TemplateOptions struct { + Template string + Team string + Environment string + ExternalID string + Placeholder *jsyaml.Map + PlaceholdersFile string + Variable *jsyaml.Map + ResetProperties bool + ExecutionVariable *jsyaml.Map +} + +// ResolvePlaceholders reads the placeholders file, a map of key to value or the +// platform's list of {key, value}, and applies -p values on top, so that a pipeline can +// keep shared values in a file and override one per stage. +func ResolvePlaceholders(o TemplateOptions) ([]api.ExperimentTemplatePlaceholderValueAO, error) { + values := jsyaml.NewMap() + if o.PlaceholdersFile != "" { + content, err := readAny(o.PlaceholdersFile) + if err != nil { + return nil, err + } + invalid := fmt.Errorf("Placeholders file '%s' must be a map of key to value or a list of {key, value} entries.", o.PlaceholdersFile) + switch v := content.(type) { + case []any: + for _, entry := range v { + m, ok := entry.(*jsyaml.Map) + key, hasKey := m.Get("key") + value, hasValue := m.Get("value") + if !ok || !hasKey || !hasValue { + return nil, invalid + } + keyString, ok := key.(string) + if !ok { + return nil, invalid + } + values.Set(keyString, value) + } + case *jsyaml.Map: + for _, k := range v.Keys() { + value, _ := v.Get(k) + values.Set(k, value) + } + default: + return nil, invalid + } + } + if o.Placeholder != nil { + for _, k := range o.Placeholder.Keys() { + value, _ := o.Placeholder.Get(k) + values.Set(k, value) + } + } + result := make([]api.ExperimentTemplatePlaceholderValueAO, 0, values.Len()) + for _, k := range values.Keys() { + value, _ := values.Get(k) + result = append(result, api.ExperimentTemplatePlaceholderValueAO{Key: k, Value: plain(value)}) + } + return result, nil +} + +// plain turns a document value into something encoding/json writes the same way. +func plain(value any) any { + switch v := value.(type) { + case *jsyaml.Map: + return rawJSON(jsyaml.CompactJSON(v)) + case []any: + return rawJSON(jsyaml.CompactJSON(v)) + case float64: + return rawJSON(jsyaml.CompactJSON(v)) + case jsyaml.Timestamp: + return v.ISO() + } + return value +} + +type rawJSON string + +func (r rawJSON) MarshalJSON() ([]byte, error) { return []byte(r), nil } + +func readAny(file string) (any, error) { + content, err := os.ReadFile(file) + if err != nil { + return nil, fmt.Errorf("Failed to read placeholders file at path '%s': %s", file, pathCause(err)) + } + value, err := output.ParseValue(content) + if err != nil { + return nil, fmt.Errorf("Failed to parse placeholders file at path '%s' as YAML/JSON: %s", file, err) + } + return value, nil +} + +func pathCause(err error) string { + var pathErr *fs.PathError + if errors.As(err, &pathErr) { + return pathErr.Err.Error() + } + return err.Error() +} + +// variables turns KEY=VALUE flags into the constant-string form of a variable. +func variables(values *jsyaml.Map) *map[string]api.VariableExpressionAO { + if values == nil || values.Len() == 0 { + return nil + } + result := map[string]api.VariableExpressionAO{} + for _, k := range values.Keys() { + value, _ := values.Get(k) + var v api.VariableExpressionAO + _ = v.FromVariableExpressionAO0(fmt.Sprint(value)) + result[k] = v + } + return &result +} + +func templateID(id string) (openapi_types.UUID, error) { + var uuid openapi_types.UUID + if err := uuid.UnmarshalText([]byte(id)); err != nil { + return uuid, fmt.Errorf("Experiment template %s not found.", id) + } + return uuid, nil +} + +func createRequest(o TemplateOptions) (api.CreateExperimentFromTemplateAO, error) { + if o.Team == "" { + return api.CreateExperimentFromTemplateAO{}, errors.New("--team is required to create an experiment from a template.") + } + placeholders, err := ResolvePlaceholders(o) + if err != nil { + return api.CreateExperimentFromTemplateAO{}, err + } + request := api.CreateExperimentFromTemplateAO{Team: o.Team, Placeholders: &placeholders, ExperimentVariables: variables(o.Variable)} + if o.Environment != "" { + request.Environment = &o.Environment + } + if o.ExternalID != "" { + request.ExternalId = &o.ExternalID + } + return request, nil +} + +// ApplyTemplate creates an experiment from a template, or updates the one with key. +func ApplyTemplate(ctx context.Context, c *platform.Client, key string, o TemplateOptions) error { + id, err := templateID(o.Template) + if err != nil { + return err + } + if key != "" { + // An update only re-renders the experiment; it keeps its team and environment, + // so accepting those here would silently do nothing. + var ignored []string + for _, f := range []struct { + flag string + set bool + }{{"--team", o.Team != ""}, {"--environment", o.Environment != ""}, {"--external-id", o.ExternalID != ""}, {"--variable", o.Variable != nil && o.Variable.Len() > 0}} { + if f.set { + ignored = append(ignored, f.flag) + } + } + if len(ignored) > 0 { + return fmt.Errorf("Updating experiment %s from a template only takes placeholders; remove %s.", key, strings.Join(ignored, ", ")) + } + placeholders, err := ResolvePlaceholders(o) + if err != nil { + return err + } + _, _, err = platform.Read(c.UpdateExperimentByTemplate(ctx, id, key, + &api.UpdateExperimentByTemplateParams{ResetProperties: &o.ResetProperties}, + api.UpdateExperimentFromTemplateAO{Placeholders: &placeholders})) + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Experiment template %s or experiment %s not found.", o.Template, key) + } + if err != nil { + return platform.Failed(err, "Failed to update experiment %s from template %s", key, o.Template) + } + fmt.Printf("Experiment %s updated from template %s.\n", key, o.Template) + return nil + } + + request, err := createRequest(o) + if err != nil { + return err + } + _, resp, err := platform.Read(c.CreateExperimentByTemplate(ctx, id, + &api.CreateExperimentByTemplateParams{ResetProperties: &o.ResetProperties}, request)) + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Experiment template %s not found.", o.Template) + } + if err != nil { + return platform.Failed(err, "Failed to create the experiment from template %s", o.Template) + } + verb := "updated" + if resp.StatusCode == http.StatusCreated { + verb = "created" + } + fmt.Printf("Experiment %s %s from template %s.\n", keyFromLocation(resp), verb, o.Template) + return nil +} diff --git a/internal/jsyaml/value.go b/internal/jsyaml/value.go index 541295a..ec69616 100644 --- a/internal/jsyaml/value.go +++ b/internal/jsyaml/value.go @@ -135,3 +135,25 @@ func NumberString(f float64) string { } return sign + digits[:1] + "." + digits[1:] + "e" + expSign + strconv.Itoa(abs) } + +// Clone copies a value deeply, so that one copy can be trimmed for a request while the +// other is written back to its file intact. +func Clone(value any) any { + switch v := value.(type) { + case *Map: + c := NewMap() + for _, k := range v.keys { + c.keys = append(c.keys, k) + c.values[k] = Clone(v.values[k]) + } + return c + case []any: + c := make([]any, len(v)) + for i, item := range v { + c[i] = Clone(item) + } + return c + default: + return v + } +} diff --git a/internal/output/document.go b/internal/output/document.go index 05e045c..6972231 100644 --- a/internal/output/document.go +++ b/internal/output/document.go @@ -25,6 +25,21 @@ type Document struct { value *jsyaml.Map } +// ParseValue reads any JSON or YAML value, such as a placeholders file that is a list. +func ParseValue(content []byte) (any, error) { + if json.Valid(content) { + return decodeJSON(json.NewDecoder(bytes.NewReader(content))) + } + var node yaml.Node + if err := yaml.Unmarshal(content, &node); err != nil { + return nil, err + } + if node.Kind != yaml.DocumentNode || len(node.Content) != 1 { + return nil, nil + } + return toValue(node.Content[0]) +} + // ParseDocument reads JSON or YAML; JSON is valid YAML, so one parser handles both. // Anchors, aliases and merge keys (`<<:`) are resolved, as js-yaml's load did. func ParseDocument(content []byte) (*Document, error) { @@ -273,3 +288,6 @@ func resolve(node *yaml.Node) *yaml.Node { } return node } + +// IsJSON reports whether content is a JSON document, as JSON.parse would accept it. +func IsJSON(content []byte) bool { return json.Valid(content) } diff --git a/internal/platform/client.go b/internal/platform/client.go index 692fa28..e6bd1c0 100644 --- a/internal/platform/client.go +++ b/internal/platform/client.go @@ -188,7 +188,7 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { if err != nil { cancel() if !idempotent[req.Method] || attempt >= 4 { - return nil, fmt.Errorf("failed to call Steadybit API at %s %s: %w", req.Method, req.URL, err) + return nil, fmt.Errorf("Failed to call Steadybit API at %s %s: %w", req.Method, req.URL, err) } time.Sleep(jitter(time.Duration(attempt) * time.Second)) continue diff --git a/internal/platform/response.go b/internal/platform/response.go new file mode 100644 index 0000000..ba06d38 --- /dev/null +++ b/internal/platform/response.go @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package platform + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + + "github.com/steadybit/cli/internal/output" +) + +// Read takes a generated client call's result and returns the body, failing on any +// status outside 2xx. +func Read(resp *http.Response, err error) ([]byte, *http.Response, error) { + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, resp, err + } + return body, resp, Check(resp, body) +} + +// Decode reads a response into target. +func Decode(resp *http.Response, err error, target any) (*http.Response, error) { + body, resp, err := Read(resp, err) + if err != nil { + return resp, err + } + return resp, json.Unmarshal(body, target) +} + +// ReadDocument reads a response as an order-preserving document. +func ReadDocument(resp *http.Response, err error) (*output.Document, *http.Response, error) { + body, resp, err := Read(resp, err) + if err != nil { + return nil, resp, err + } + doc, err := output.ParseDocument(body) + return doc, resp, err +} + +func IsStatus(err error, status int) bool { + var apiErr *APIError + return errors.As(err, &apiErr) && apiErr.Status == status +} + +// Failed reports a failed request as the TypeScript CLI did: the message, the request +// error, and the platform's problem body pretty-printed, which is what names the +// violated constraint. +func Failed(err error, format string, args ...any) error { + message := err.Error() + var apiErr *APIError + if errors.As(err, &apiErr) { + if problem := apiErr.problemJSON(); problem != "" { + message += ": " + problem + } + } + return fmt.Errorf("%s: %s", fmt.Sprintf(format, args...), message) +} + +func (e *APIError) problemJSON() string { + body := bytes.TrimSpace(e.Body) + if len(body) == 0 || !json.Valid(body) { + return "" + } + switch body[0] { + case '{': + doc, err := output.ParseDocument(body) + if err != nil { + return "" + } + rendered, _ := doc.Render(output.JSON) + return string(bytes.TrimSuffix(rendered, []byte("\n"))) + case '[': + if string(body) == "[]" { + return "[]" + } + // Arrays are rare in problem bodies; reindenting them as JavaScript would is + // left to json.Indent, which agrees for plain values. + var buf bytes.Buffer + if json.Indent(&buf, body, "", " ") == nil { + return buf.String() + } + case '"': + var s string + if json.Unmarshal(body, &s) == nil && s != "" { + return string(body) + } + } + return "" +} + +// PageSize is the most items a paged endpoint returns at once. +const PageSize int32 = 100 + +// AllPages follows nextPage until the last page: a listing cut at the first response +// would silently leave the rest out. +func AllPages[T any](fetch func(page, size int32) (*http.Response, error)) ([]T, error) { + var items []T + page := int32(0) + for { + var body struct { + Items []T `json:"items"` + NextPage *int32 `json:"nextPage"` + } + resp, err := fetch(page, PageSize) + if _, err := Decode(resp, err, &body); err != nil { + return nil, err + } + items = append(items, body.Items...) + if body.NextPage == nil { + return items, nil + } + page = *body.NextPage + } +} diff --git a/internal/resource/resource.go b/internal/resource/resource.go new file mode 100644 index 0000000..f7183b8 --- /dev/null +++ b/internal/resource/resource.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package resource holds what the commands managing schedules, services, profiles, +// templates and runs share: writing a document to a file or stdout, reading one back, +// and applying files with the new id written into them. +package resource + +import ( + "fmt" + "os" + "strings" + + "github.com/steadybit/cli/internal/experiment" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/output" +) + +// Output writes to the file when one is given and to stdout otherwise, as JSON +// indented by two, or YAML. +func Output(doc *output.Document, file, explicitType string) error { + datatype, err := output.ResolveDatatype(explicitType, file) + if err != nil { + return err + } + rendered := format(doc, datatype) + if file == "" { + fmt.Println(rendered) + return nil + } + return os.WriteFile(file, []byte(rendered), 0o644) +} + +// OutputValue is Output for a value that is not an object, such as a map of variables. +func OutputValue(value *jsyaml.Map, file, explicitType string) error { + return Output(output.NewDocument(value), file, explicitType) +} + +func format(doc *output.Document, datatype output.Datatype) string { + if datatype == output.JSON { + return jsyaml.JSON(doc.Value()) + } + return jsyaml.Dump(doc.Value()) +} + +// Read loads a JSON or YAML file; JSON is tried first, as it was. +func Read(file, what string) (*output.Document, output.Datatype, error) { + content, err := os.ReadFile(file) + if err != nil { + return nil, "", fmt.Errorf("Failed to read %s file at path '%s': %s", what, file, cause(err)) + } + doc, err := output.ParseDocument(content) + if err != nil { + return nil, "", fmt.Errorf("Failed to parse %s file at path '%s' as YAML/JSON: %s", what, file, err) + } + datatype := output.YAML + if isJSON(content) { + datatype = output.JSON + } + return doc, datatype, nil +} + +func isJSON(content []byte) bool { + trimmed := strings.TrimSpace(string(content)) + return strings.HasPrefix(trimmed, "{") && output.IsJSON(content) +} + +func cause(err error) string { + if pathErr, ok := err.(*os.PathError); ok { + return pathErr.Err.Error() + } + return err.Error() +} + +// Strip removes fields the platform reports but does not accept back, so that a file +// written by `get` can be applied again unchanged. +func Strip(doc *output.Document, fields ...string) *output.Document { + for _, f := range fields { + doc.Delete(f) + } + return doc +} + +type Applied struct { + ID string + Created bool +} + +// ApplyFiles upserts every file. A file without an id gets the new one written into it, +// first, so the next apply updates what this one created instead of creating another. +func ApplyFiles(paths []string, recursive bool, what string, upsert func(file string, doc *output.Document) (Applied, error)) error { + files, err := experiment.ResolveFiles(paths, recursive) + if err != nil { + return err + } + for _, file := range files { + doc, datatype, err := Read(file, what) + if err != nil { + return err + } + existingID, _ := doc.Value().Get("id") + result, err := upsert(file, output.NewDocument(jsyaml.Clone(doc.Value()).(*jsyaml.Map))) + if err != nil { + return err + } + if existingID == nil || existingID == "" { + doc.Value().SetFirst("id", result.ID) + if err := os.WriteFile(file, []byte(format(doc, datatype)), 0o644); err != nil { + return err + } + } + } + return nil +} + +func CreatedOrUpdated(created bool) string { + if created { + return "created" + } + return "updated" +} diff --git a/internal/schedule/schedule.go b/internal/schedule/schedule.go new file mode 100644 index 0000000..02089e1 --- /dev/null +++ b/internal/schedule/schedule.go @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package schedule implements the `schedule` commands. +package schedule + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/http" + + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/resource" + "github.com/steadybit/cli/internal/table" +) + +// What the platform reports about the last edit and the next run cannot be sent back, +// so it is left out of files, keeping `get` followed by `apply` a round trip. +var readOnly = []string{"editedBy", "lastUpdated", "nextExecution"} + +func notFoundOr(err error, id, format string) error { + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Experiment schedule %s not found.", id) + } + return platform.Failed(err, format, id) +} + +func body(m *jsyaml.Map) io.Reader { return bytes.NewReader([]byte(jsyaml.CompactJSON(m))) } + +func optional(values []string) *[]string { + if len(values) == 0 { + return nil + } + return &values +} + +type ListOptions struct { + Teams, Experiments []string +} + +func List(ctx context.Context, c *platform.Client, o ListOptions) error { + var schedules []struct { + ID string `json:"id"` + ExperimentKey string `json:"experimentKey"` + Cron *string `json:"cron"` + StartAt *string `json:"startAt"` + Timezone *string `json:"timezone"` + Enabled *bool `json:"enabled"` + AllowParallel *bool `json:"allowParallel"` + } + resp, err := c.GetAllSchedulesV2(ctx, &api.GetAllSchedulesV2Params{Team: optional(o.Teams), Experiment: optional(o.Experiments)}) + if _, err := platform.Decode(resp, err, &schedules); err != nil { + return platform.Failed(err, "Failed to get the experiment schedules") + } + if len(schedules) == 0 { + fmt.Println("No experiment schedules found.") + return nil + } + t := table.New( + table.Column{Name: "id", Title: "Id", Alignment: table.Left}, + table.Column{Name: "experiment", Title: "Experiment", Alignment: table.Left}, + table.Column{Name: "when", Title: "When", Alignment: table.Left}, + table.Column{Name: "enabled", Title: "Enabled", Alignment: table.Left}, + table.Column{Name: "allowParallel", Title: "Parallel", Alignment: table.Left}, + ) + boolOr := func(b *bool) string { + if b == nil { + return "true" + } + return fmt.Sprint(*b) + } + for _, s := range schedules { + when := "" + switch { + case s.Cron != nil && *s.Cron != "": + when = *s.Cron + if s.Timezone != nil && *s.Timezone != "" { + when += " (" + *s.Timezone + ")" + } + case s.StartAt != nil: + when = *s.StartAt + } + t.AddRow(table.Default, table.Cell("id", s.ID), table.Cell("experiment", s.ExperimentKey), table.Cell("when", when), + table.Cell("enabled", boolOr(s.Enabled)), table.Cell("allowParallel", boolOr(s.AllowParallel))) + } + t.Print() + return nil +} + +type GetOptions struct { + ID, File, Type string +} + +func Get(ctx context.Context, c *platform.Client, o GetOptions) error { + doc, _, err := platform.ReadDocument(c.GetSchedules(ctx, o.ID)) + if err != nil { + return notFoundOr(err, o.ID, "Failed to get experiment schedule %s") + } + if err := resource.Output(resource.Strip(doc, readOnly...), o.File, o.Type); err != nil { + return err + } + if o.File != "" { + fmt.Printf("Experiment schedule %s written to %s.\n", o.ID, o.File) + } + return nil +} + +type upserted struct { + ID string `json:"id"` + ExperimentKey string `json:"experimentKey"` +} + +type ApplyOptions struct { + Files []string + Recursive bool +} + +func Apply(ctx context.Context, c *platform.Client, o ApplyOptions) error { + return resource.ApplyFiles(o.Files, o.Recursive, "schedule", func(file string, doc *output.Document) (resource.Applied, error) { + if key, _ := doc.Get("experimentKey"); key == "" { + return resource.Applied{}, fmt.Errorf("Schedule file '%s' does not name an experimentKey.", file) + } + schedule, created, err := save(ctx, c, resource.Strip(doc, readOnly...).Value()) + if err != nil { + return resource.Applied{}, err + } + fmt.Printf("Experiment schedule %s for %s %s.\n", schedule.ID, schedule.ExperimentKey, resource.CreatedOrUpdated(created)) + return resource.Applied{ID: schedule.ID, Created: created}, nil + }) +} + +func save(ctx context.Context, c *platform.Client, schedule *jsyaml.Map) (upserted, bool, error) { + resp, err := c.UpsertScheduleWithBody(ctx, "application/json", body(schedule)) + var result upserted + resp, err = platform.Decode(resp, err, &result) + if err != nil { + key, _ := schedule.Get("experimentKey") + return result, false, platform.Failed(err, "Failed to save the experiment schedule for %v", key) + } + return result, resp.StatusCode == http.StatusCreated, nil +} + +type Fields struct { + Cron, StartAt, Timezone string + AllowParallel *bool + Variables *jsyaml.Map +} + +func (f Fields) check(requireOne bool) error { + if f.Cron != "" && f.StartAt != "" { + return errors.New("--cron and --start-at cannot be combined.") + } + if requireOne && f.Cron == "" && f.StartAt == "" { + return errors.New("Either --cron or --start-at must be specified.") + } + return nil +} + +// into sets the fields that were given, leaving the others out of the request. +func (f Fields) into(m *jsyaml.Map) { + for _, field := range [][2]string{{"cron", f.Cron}, {"startAt", f.StartAt}, {"timezone", f.Timezone}} { + if field[1] != "" { + m.Set(field[0], field[1]) + } + } + if f.AllowParallel != nil { + m.Set("allowParallel", *f.AllowParallel) + } + if f.Variables != nil && f.Variables.Len() > 0 { + m.Set("variables", f.Variables) + } +} + +type CreateOptions struct { + Fields + Experiment string + Disabled bool +} + +func Create(ctx context.Context, c *platform.Client, o CreateOptions) error { + if err := o.check(true); err != nil { + return err + } + m := jsyaml.NewMap() + m.Set("experimentKey", o.Experiment) + o.into(m) + m.Set("enabled", !o.Disabled) + schedule, _, err := save(ctx, c, m) + if err != nil { + return err + } + fmt.Printf("Experiment schedule %s for %s created.\n", schedule.ID, schedule.ExperimentKey) + return nil +} + +type UpdateOptions struct { + Fields + ID string +} + +func Update(ctx context.Context, c *platform.Client, o UpdateOptions) error { + if err := o.check(false); err != nil { + return err + } + m := jsyaml.NewMap() + o.into(m) + if m.Len() == 0 { + return errors.New("Nothing to update. Pass at least one of the options, see --help.") + } + return patchAndReport(ctx, c, o.ID, m, "updated") +} + +func SetEnabled(ctx context.Context, c *platform.Client, id string, enabled bool) error { + m := jsyaml.NewMap() + m.Set("enabled", enabled) + outcome := "disabled" + if enabled { + outcome = "enabled" + } + return patchAndReport(ctx, c, id, m, outcome) +} + +func patchAndReport(ctx context.Context, c *platform.Client, id string, m *jsyaml.Map, outcome string) error { + resp, err := c.PatchScheduleWithBody(ctx, id, "application/json", body(m)) + var result upserted + if _, err := platform.Decode(resp, err, &result); err != nil { + return notFoundOr(err, id, "Failed to update experiment schedule %s") + } + fmt.Printf("Experiment schedule %s for %s %s.\n", id, result.ExperimentKey, outcome) + return nil +} + +func Delete(ctx context.Context, c *platform.Client, id string) error { + if _, _, err := platform.Read(c.RemoveExperimentScheduleById(ctx, id)); err != nil { + return notFoundOr(err, id, "Failed to delete experiment schedule %s") + } + fmt.Printf("Experiment schedule %s deleted.\n", id) + return nil +} diff --git a/internal/service/service.go b/internal/service/service.go new file mode 100644 index 0000000..5f14964 --- /dev/null +++ b/internal/service/service.go @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package service implements the `service` commands. +package service + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "strings" + + openapi_types "github.com/oapi-codegen/runtime/types" + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/experiment" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/resource" + "github.com/steadybit/cli/internal/table" +) + +// Who created and edited a service cannot be sent back. The version is dropped as +// `experiment get` drops it: kept in a file, it turns every apply after an edit in the UI +// into a conflict. +var readOnly = []string{"created", "createdBy", "edited", "editedBy", "version"} + +var ErrNotFound = errors.New("not found") + +func uuid(id string) (openapi_types.UUID, error) { + var u openapi_types.UUID + if err := u.UnmarshalText([]byte(id)); err != nil { + return u, fmt.Errorf("Service %s not found.", id) + } + return u, nil +} + +func notFoundOr(err error, id, format string, args ...any) error { + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Service %s not found.", id) + } + return platform.Failed(err, format, append(args, id)...) +} + +func optional(values []string) *[]string { + if len(values) == 0 { + return nil + } + return &values +} + +type ListOptions struct { + Teams, Environments, Experiments []string +} + +func List(ctx context.Context, c *platform.Client, o ListOptions) error { + type summary struct { + ID, Name, Team, Environment string + } + services, err := platform.AllPages[summary](func(page, size int32) (*http.Response, error) { + return c.GetServiceList(ctx, &api.GetServiceListParams{ + TeamKey: optional(o.Teams), EnvironmentName: optional(o.Environments), ExperimentKey: optional(o.Experiments), + Page: api.PageRequestAO{Page: &page, Size: &size}, + }) + }) + if err != nil { + return platform.Failed(err, "Failed to get the services") + } + if len(services) == 0 { + fmt.Println("No services found.") + return nil + } + t := table.New( + table.Column{Name: "id", Title: "Id", Alignment: table.Left}, + table.Column{Name: "name", Title: "Name", Alignment: table.Left}, + table.Column{Name: "team", Title: "Team", Alignment: table.Left}, + table.Column{Name: "environment", Title: "Environment", Alignment: table.Left}, + ) + for _, s := range services { + t.AddRow(table.Default, table.Cell("id", s.ID), table.Cell("name", s.Name), table.Cell("team", s.Team), table.Cell("environment", s.Environment)) + } + t.Print() + return nil +} + +type GetOptions struct { + ID, File, Type string +} + +func Get(ctx context.Context, c *platform.Client, o GetOptions) error { + id, err := uuid(o.ID) + if err != nil { + return err + } + doc, _, err := platform.ReadDocument(c.GetService(ctx, id)) + if err != nil { + return notFoundOr(err, o.ID, "Failed to get service %s") + } + if err := resource.Output(resource.Strip(doc, readOnly...), o.File, o.Type); err != nil { + return err + } + if o.File != "" { + fmt.Printf("Service %s written to %s.\n", o.ID, o.File) + } + return nil +} + +type ApplyOptions struct { + Files []string + Recursive bool + DeleteExperiments bool +} + +// RefusedForProvidedExperiments recognises the platform refusing a change that would +// orphan provided experiments. It names its query parameter, so the user is pointed at +// the CLI flag instead. +func RefusedForProvidedExperiments(err error) bool { + var apiErr *platform.APIError + if !errors.As(err, &apiErr) || apiErr.Status != http.StatusUnprocessableEntity { + return false + } + var problem struct { + Violations []struct { + Message string `json:"message"` + } `json:"violations"` + } + _ = json.Unmarshal(apiErr.Body, &problem) + for _, v := range problem.Violations { + if strings.Contains(v.Message, "deleteExperiments") { + return true + } + } + return false +} + +func Apply(ctx context.Context, c *platform.Client, o ApplyOptions) error { + return resource.ApplyFiles(o.Files, o.Recursive, "service", func(file string, doc *output.Document) (resource.Applied, error) { + name, _ := doc.Get("name") + if name == "" { + return resource.Applied{}, fmt.Errorf("Service file '%s' does not name the service.", file) + } + var saved struct{ ID, Name string } + resp, err := c.UpsertServiceWithBody(ctx, &api.UpsertServiceParams{DeleteExperiments: &o.DeleteExperiments}, "application/json", + bytes.NewReader([]byte(jsyaml.CompactJSON(resource.Strip(doc, readOnly...).Value())))) + resp, err = platform.Decode(resp, err, &saved) + if err != nil { + if !o.DeleteExperiments && RefusedForProvidedExperiments(err) { + return resource.Applied{}, fmt.Errorf("Service %s was not saved: the change would remove provided experiments. Pass --delete-experiments to delete them.", name) + } + return resource.Applied{}, platform.Failed(err, "Failed to save service %s", name) + } + created := resp.StatusCode == http.StatusCreated + fmt.Printf("Service %s (%s) %s.\n", saved.Name, saved.ID, resource.CreatedOrUpdated(created)) + return resource.Applied{ID: saved.ID, Created: created}, nil + }) +} + +func Delete(ctx context.Context, c *platform.Client, idText string) error { + id, err := uuid(idText) + if err != nil { + return err + } + if _, _, err := platform.Read(c.DeleteService(ctx, id)); err != nil { + return notFoundOr(err, idText, "Failed to delete service %s") + } + fmt.Printf("Service %s deleted.\n", idText) + return nil +} + +type RiskOptions struct { + ID string + Type string + FailAbove *int +} + +func Risk(ctx context.Context, c *platform.Client, o RiskOptions) error { + id, err := uuid(o.ID) + if err != nil { + return err + } + doc, _, err := platform.ReadDocument(c.GetRisk(ctx, id)) + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Service %s not found, or its risk has not been calculated yet.", o.ID) + } + if err != nil { + return platform.Failed(err, "Failed to get the risk of service %s", o.ID) + } + value := doc.Value() + riskValue, hasRisk := value.Get("risk") + risk, _ := riskValue.(float64) + riskText := "unknown" + if n, ok := riskValue.(float64); hasRisk && ok { + riskText = jsyaml.NumberString(n) + } + + if o.Type != "" { + if err := resource.Output(doc, "", o.Type); err != nil { + return err + } + } else { + calculated, _ := doc.Get("lastCalculated") + if calculated == "" { + calculated = "never" + } + fmt.Printf("Risk of service %s: %s (calculated %s)\n", o.ID, riskText, calculated) + if categories, ok := value.Get("categoryRisks"); ok { + if m, ok := categories.(*jsyaml.Map); ok && m.Len() > 0 { + t := table.New(table.Column{Name: "category", Title: "Category", Alignment: table.Left}, + table.Column{Name: "total", Title: "Total"}, table.Column{Name: "experiment", Title: "Experiments"}, table.Column{Name: "advice", Title: "Advice"}) + for _, category := range m.Keys() { + r, _ := m.Get(category) + rm, _ := r.(*jsyaml.Map) + t.AddRow(table.Default, table.Cell("category", category), table.Cell("total", number(rm, "total")), + table.Cell("experiment", number(rm, "experiment")), table.Cell("advice", number(rm, "advice"))) + } + t.Print() + } + } + if experiments, ok := value.Get("experimentRisks"); ok { + if list, ok := experiments.([]any); ok && len(list) > 0 { + t := table.New(table.Column{Name: "experimentKey", Title: "Experiment", Alignment: table.Left}, table.Column{Name: "risk", Title: "Risk"}) + for _, e := range list { + em, _ := e.(*jsyaml.Map) + key, _ := em.Get("experimentKey") + t.AddRow(table.Default, table.Cell("experimentKey", key), table.Cell("risk", number(em, "risk"))) + } + t.Print() + } + } + } + + // Lets a pipeline stop the rollout of a service whose risk is too high. + if o.FailAbove != nil && (!hasRisk || riskText == "unknown" || risk > float64(*o.FailAbove)) { + return fmt.Errorf("Risk of service %s is %s, above the accepted %d.", o.ID, riskText, *o.FailAbove) + } + return nil +} + +func number(m *jsyaml.Map, key string) any { + if m == nil { + return nil + } + v, _ := m.Get(key) + if n, ok := v.(float64); ok { + return jsyaml.NumberString(n) + } + return v +} + +type ExperimentListOptions struct { + ID string + Categories, Types []string +} + +func ListExperiments(ctx context.Context, c *platform.Client, o ExperimentListOptions) error { + id, err := uuid(o.ID) + if err != nil { + return err + } + var types *[]api.GetServiceExperimentsParamsType + if len(o.Types) > 0 { + list := make([]api.GetServiceExperimentsParamsType, len(o.Types)) + for i, t := range o.Types { + list[i] = api.GetServiceExperimentsParamsType(strings.ToUpper(t)) + } + types = &list + } + type entry struct { + ExperimentKey *string `json:"experimentKey"` + TemplateID *string `json:"templateId"` + Category string `json:"category"` + AssociationType string `json:"associationType"` + } + experiments, err := platform.AllPages[entry](func(page, size int32) (*http.Response, error) { + return c.GetServiceExperiments(ctx, id, &api.GetServiceExperimentsParams{Category: optional(o.Categories), Type: types, Page: api.PageRequestAO{Page: &page, Size: &size}}) + }) + if err != nil { + return notFoundOr(err, o.ID, "Failed to get the experiments of service %s") + } + if len(experiments) == 0 { + if len(o.Categories) > 0 || len(o.Types) > 0 { + fmt.Printf("Service %s has no matching experiments.\n", o.ID) + } else { + fmt.Printf("Service %s has no experiments.\n", o.ID) + } + return nil + } + t := table.New( + table.Column{Name: "category", Title: "Category", Alignment: table.Left}, + table.Column{Name: "associationType", Title: "Type", Alignment: table.Left}, + // A provided experiment not created yet has no key, only its template. + table.Column{Name: "experimentKey", Title: "Experiment", Alignment: table.Left}, + table.Column{Name: "templateId", Title: "Template", Alignment: table.Left}, + ) + for _, e := range experiments { + key, template := "(not created)", "" + if e.ExperimentKey != nil { + key = *e.ExperimentKey + } + if e.TemplateID != nil { + template = *e.TemplateID + } + t.AddRow(table.Default, table.Cell("category", e.Category), table.Cell("associationType", e.AssociationType), table.Cell("experimentKey", key), table.Cell("templateId", template)) + } + t.Print() + return nil +} + +func Link(ctx context.Context, c *platform.Client, idText, experimentKey, category string) error { + id, err := uuid(idText) + if err != nil { + return err + } + if _, _, err := platform.Read(c.LinkCustomExperiment(ctx, id, api.LinkCustomExperimentRequestAO{ExperimentKey: experimentKey, Category: category})); err != nil { + return notFoundOr(err, idText, "Failed to link experiment %s to service %s", experimentKey) + } + fmt.Printf("Experiment %s linked to service %s in category %s.\n", experimentKey, idText, category) + return nil +} + +func Unlink(ctx context.Context, c *platform.Client, idText, experimentKey string) error { + id, err := uuid(idText) + if err != nil { + return err + } + if _, _, err := platform.Read(c.UnlinkCustomExperiment(ctx, id, &api.UnlinkCustomExperimentParams{ExperimentKey: experimentKey})); err != nil { + return notFoundOr(err, idText, "Failed to unlink experiment %s from service %s", experimentKey) + } + fmt.Printf("Experiment %s unlinked from service %s.\n", experimentKey, idText) + return nil +} + +type ProvideOptions struct { + ID string + Experiment string + experiment.TemplateOptions +} + +func Provide(ctx context.Context, c *platform.Client, o ProvideOptions) error { + id, err := uuid(o.ID) + if err != nil { + return err + } + placeholders, err := experiment.ResolvePlaceholders(o.TemplateOptions) + if err != nil { + return err + } + var templateID openapi_types.UUID + if err := templateID.UnmarshalText([]byte(o.Template)); err != nil { + return fmt.Errorf("Service %s or experiment template %s not found.", o.ID, o.Template) + } + request := api.UpsertProvidedExperimentRequestAO{TemplateId: templateID, Placeholders: &placeholders} + if o.Experiment != "" { + request.ExperimentKey = &o.Experiment + } + _, resp, err := platform.Read(c.UpsertProvidedExperiment(ctx, id, &api.UpsertProvidedExperimentParams{ResetProperties: &o.ResetProperties}, request)) + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Service %s or experiment template %s not found.", o.ID, o.Template) + } + if err != nil { + return platform.Failed(err, "Failed to save the provided experiment of service %s", o.ID) + } + key := o.Experiment + if location := resp.Header.Get("Location"); location != "" { + key = location[strings.LastIndex(location, "/")+1:] + } + fmt.Printf("Provided experiment %s of service %s %s from template %s.\n", key, o.ID, resource.CreatedOrUpdated(resp.StatusCode == http.StatusCreated), o.Template) + return nil +} + +type VariableGetOptions struct { + ID, Type string +} + +func GetVariables(ctx context.Context, c *platform.Client, o VariableGetOptions) error { + id, err := uuid(o.ID) + if err != nil { + return err + } + doc, _, err := platform.ReadDocument(c.GetServiceVariables(ctx, id)) + if err != nil { + return notFoundOr(err, o.ID, "Failed to get the variables of service %s") + } + return resource.Output(doc, "", o.Type) +} + +type VariableSetOptions struct { + ID string + File string + Replace bool +} + +// SetVariables merges KEY=VALUE arguments, always strings, over a file's variables, +// which may be lists or select expressions. With --replace, the result is all there is. +func SetVariables(ctx context.Context, c *platform.Client, pairs []string, o VariableSetOptions) error { + given := jsyaml.NewMap() + for _, pair := range pairs { + i := strings.Index(pair, "=") + if i <= 0 { + return fmt.Errorf("'%s' is not in the form KEY=VALUE.", pair) + } + given.Set(pair[:i], pair[i+1:]) + } + variables := jsyaml.NewMap() + if o.File != "" { + doc, _, err := resource.Read(o.File, "variables") + if err != nil { + return fmt.Errorf("Variables file '%s' must be a map of variable names to values.", o.File) + } + variables = doc.Value() + } + for _, k := range given.Keys() { + v, _ := given.Get(k) + variables.Set(k, v) + } + if variables.Len() == 0 && !o.Replace { + return errors.New("No variables given. Pass KEY=VALUE arguments or --file.") + } + id, err := uuid(o.ID) + if err != nil { + return err + } + body := bytes.NewReader([]byte(jsyaml.CompactJSON(variables))) + if o.Replace { + _, _, err = platform.Read(c.SetServiceVariablesWithBody(ctx, id, "application/json", body)) + } else { + _, _, err = platform.Read(c.MergeServiceVariablesWithBody(ctx, id, "application/json", body)) + } + if err != nil { + return notFoundOr(err, o.ID, "Failed to update the variables of service %s") + } + outcome := "set" + if o.Replace { + outcome = "set, all others removed" + } + fmt.Printf("%d variable(s) of service %s %s.\n", variables.Len(), o.ID, outcome) + return nil +} diff --git a/internal/serviceprofile/serviceprofile.go b/internal/serviceprofile/serviceprofile.go new file mode 100644 index 0000000..ffdf1a1 --- /dev/null +++ b/internal/serviceprofile/serviceprofile.go @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package serviceprofile implements the `service-profile` commands. +package serviceprofile + +import ( + "bytes" + "context" + "fmt" + "net/http" + "strings" + + openapi_types "github.com/oapi-codegen/runtime/types" + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/resource" + "github.com/steadybit/cli/internal/service" + "github.com/steadybit/cli/internal/table" +) + +// Only what can be sent back is kept. Whether a profile is the default is changed in the +// platform, not through the file. +var readOnly = []string{"created", "createdBy", "edited", "editedBy", "version", "defaultProfile"} + +func uuid(id string) (openapi_types.UUID, error) { + var u openapi_types.UUID + if err := u.UnmarshalText([]byte(id)); err != nil { + return u, fmt.Errorf("Service profile %s not found.", id) + } + return u, nil +} + +func notFoundOr(err error, id, format string) error { + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Service profile %s not found.", id) + } + return platform.Failed(err, format, id) +} + +type ListOptions struct { + Name string + Origins []string + Default bool +} + +func List(ctx context.Context, c *platform.Client, o ListOptions) error { + type profile struct { + ID string `json:"id"` + Name string `json:"name"` + Origin string `json:"origin"` + DefaultProfile bool `json:"defaultProfile"` + Templates []struct { + TemplateIDs []string `json:"templateIds"` + } `json:"templates"` + } + params := api.GetProfilesParams{} + if o.Name != "" { + params.Name = &o.Name + } + if len(o.Origins) > 0 { + origins := make([]string, len(o.Origins)) + for i, origin := range o.Origins { + origins[i] = strings.ToUpper(origin) + } + params.Origin = &origins + } + if o.Default { + params.DefaultProfile = &o.Default + } + profiles, err := platform.AllPages[profile](func(page, size int32) (*http.Response, error) { + p := params + p.Page = api.PageRequestAO{Page: &page, Size: &size} + return c.GetProfiles(ctx, &p) + }) + if err != nil { + return platform.Failed(err, "Failed to get the service profiles") + } + if len(profiles) == 0 { + fmt.Println("No service profiles found.") + return nil + } + t := table.New( + table.Column{Name: "id", Title: "Id", Alignment: table.Left}, + table.Column{Name: "name", Title: "Name", Alignment: table.Left}, + table.Column{Name: "origin", Title: "Origin", Alignment: table.Left}, + table.Column{Name: "defaultProfile", Title: "Default", Alignment: table.Left}, + table.Column{Name: "templates", Title: "Templates"}, + ) + for _, p := range profiles { + count := 0 + for _, category := range p.Templates { + count += len(category.TemplateIDs) + } + t.AddRow(table.Default, table.Cell("id", p.ID), table.Cell("name", p.Name), table.Cell("origin", p.Origin), + table.Cell("defaultProfile", p.DefaultProfile), table.Cell("templates", count)) + } + t.Print() + return nil +} + +type GetOptions struct { + ID, File, Type string +} + +func Get(ctx context.Context, c *platform.Client, o GetOptions) error { + id, err := uuid(o.ID) + if err != nil { + return err + } + doc, _, err := platform.ReadDocument(c.GetProfile(ctx, id)) + if err != nil { + return notFoundOr(err, o.ID, "Failed to get service profile %s") + } + if err := resource.Output(resource.Strip(doc, readOnly...), o.File, o.Type); err != nil { + return err + } + if o.File != "" { + fmt.Printf("Service profile %s written to %s.\n", o.ID, o.File) + } + return nil +} + +type ApplyOptions struct { + Files []string + Recursive bool + DeleteExperiments bool +} + +func Apply(ctx context.Context, c *platform.Client, o ApplyOptions) error { + return resource.ApplyFiles(o.Files, o.Recursive, "service profile", func(file string, doc *output.Document) (resource.Applied, error) { + name, _ := doc.Get("name") + if name == "" { + return resource.Applied{}, fmt.Errorf("Service profile file '%s' does not name the profile.", file) + } + profile := resource.Strip(doc, readOnly...).Value() + // Profiles written by hand are the team's own; PROVIDED ones come from Steadybit. + if origin, ok := profile.Get("origin"); !ok || origin == nil { + profile.Set("origin", "CUSTOM") + } + var saved struct{ ID, Name string } + resp, err := c.UpsertProfileWithBody(ctx, &api.UpsertProfileParams{DeleteExperiments: &o.DeleteExperiments}, "application/json", + bytes.NewReader([]byte(jsyaml.CompactJSON(profile)))) + resp, err = platform.Decode(resp, err, &saved) + if err != nil { + if !o.DeleteExperiments && service.RefusedForProvidedExperiments(err) { + return resource.Applied{}, fmt.Errorf("Service profile %s was not saved: the change would remove provided experiments. Pass --delete-experiments to delete them.", name) + } + return resource.Applied{}, platform.Failed(err, "Failed to save service profile %s", name) + } + created := resp.StatusCode == http.StatusCreated + fmt.Printf("Service profile %s (%s) %s.\n", saved.Name, saved.ID, resource.CreatedOrUpdated(created)) + return resource.Applied{ID: saved.ID, Created: created}, nil + }) +} + +func Delete(ctx context.Context, c *platform.Client, idText string) error { + id, err := uuid(idText) + if err != nil { + return err + } + if _, _, err := platform.Read(c.DeleteProfile(ctx, id)); err != nil { + if platform.IsStatus(err, http.StatusUnprocessableEntity) { + return fmt.Errorf("Service profile %s is provided by Steadybit and cannot be deleted.", idText) + } + return notFoundOr(err, idText, "Failed to delete service profile %s") + } + fmt.Printf("Service profile %s deleted.\n", idText) + return nil +} diff --git a/internal/table/table.go b/internal/table/table.go new file mode 100644 index 0000000..8d78d1e --- /dev/null +++ b/internal/table/table.go @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package table prints tables laid out as console-table-printer did for the TypeScript +// CLI: box drawing, one space of padding, right-aligned unless a column says otherwise. +package table + +import ( + "fmt" + "strings" + + "github.com/mattn/go-runewidth" + "github.com/steadybit/cli/internal/output" +) + +type Alignment int + +const ( + Right Alignment = iota + Left +) + +type Column struct { + Name string + Title string + Alignment Alignment +} + +type Color int + +const ( + Default Color = iota + Red + Green +) + +type Table struct { + columns []Column + rows []row +} + +type row struct { + cells map[string]string + color Color +} + +func New(columns ...Column) *Table { return &Table{columns: columns} } + +// AddRow adds a row. Without declared columns, they are taken from the row's keys in +// order, as console-table-printer did. +func (t *Table) AddRow(color Color, cells ...[2]string) { + values := map[string]string{} + for _, cell := range cells { + values[cell[0]] = cell[1] + if !t.hasColumn(cell[0]) { + t.columns = append(t.columns, Column{Name: cell[0]}) + } + } + t.rows = append(t.rows, row{cells: values, color: color}) +} + +func (t *Table) hasColumn(name string) bool { + for _, c := range t.columns { + if c.Name == name { + return true + } + } + return false +} + +func Cell(name string, value any) [2]string { + if value == nil { + return [2]string{name, ""} + } + return [2]string{name, fmt.Sprint(value)} +} + +func (c Column) title() string { + if c.Title != "" { + return c.Title + } + return c.Name +} + +func pad(s string, width int, a Alignment) string { + gap := strings.Repeat(" ", width-runewidth.StringWidth(s)) + if a == Left { + return s + gap + } + return gap + s +} + +func colored(s string, c Color) string { + switch c { + case Red: + return output.Red(s) + case Green: + return output.Green(s) + } + return s +} + +func (t *Table) Render() string { + widths := make([]int, len(t.columns)) + for i, c := range t.columns { + widths[i] = runewidth.StringWidth(c.title()) + for _, r := range t.rows { + widths[i] = max(widths[i], runewidth.StringWidth(r.cells[c.Name])) + } + } + line := func(left, middle, right string) string { + parts := make([]string, len(widths)) + for i, w := range widths { + parts[i] = strings.Repeat("─", w+2) + } + return left + strings.Join(parts, middle) + right + } + var b strings.Builder + b.WriteString(line("┌", "┬", "┐") + "\n│") + for i, c := range t.columns { + b.WriteString(" " + output.Bold(pad(c.title(), widths[i], c.Alignment)) + " │") + } + b.WriteString("\n" + line("├", "┼", "┤") + "\n") + for _, r := range t.rows { + b.WriteString("│") + for i, c := range t.columns { + b.WriteString(" " + colored(pad(r.cells[c.Name], widths[i], c.Alignment), r.color) + " │") + } + b.WriteString("\n") + } + b.WriteString(line("└", "┴", "┘")) + return b.String() +} + +func (t *Table) Print() { fmt.Println(t.Render()) } diff --git a/internal/table/table_test.go b/internal/table/table_test.go new file mode 100644 index 0000000..eaaa32c --- /dev/null +++ b/internal/table/table_test.go @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package table + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// Recorded from console-table-printer with colours off. +func TestRendersLikeConsoleTablePrinter(t *testing.T) { + tbl := New(Column{Name: "a", Title: "A", Alignment: Left}, Column{Name: "n", Title: "Num"}) + tbl.AddRow(Default, Cell("a", "x"), Cell("n", 5)) + tbl.AddRow(Default, Cell("a", "longer ünï 😀"), Cell("n", 12345)) + tbl.AddRow(Default, Cell("a", ""), Cell("n", nil)) + + assert.Equal(t, "┌───────────────┬───────┐\n│ A │ Num │\n├───────────────┼───────┤\n│ x │ 5 │\n│ longer ünï 😀 │ 12345 │\n│ │ │\n└───────────────┴───────┘", tbl.Render()) +} + +func TestTakesColumnsFromTheRowsWhenNoneAreDeclared(t *testing.T) { + tbl := New() + tbl.AddRow(Red, Cell("target", "a"), Cell("advice", "b")) + + assert.Equal(t, "┌────────┬────────┐\n│ target │ advice │\n├────────┼────────┤\n│ a │ b │\n└────────┴────────┘", tbl.Render()) +} diff --git a/internal/template/template.go b/internal/template/template.go new file mode 100644 index 0000000..4e1367a --- /dev/null +++ b/internal/template/template.go @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package template implements `template list` and `template get`. +package template + +import ( + "context" + "fmt" + "net/http" + + openapi_types "github.com/oapi-codegen/runtime/types" + "github.com/steadybit/cli/api" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/resource" + "github.com/steadybit/cli/internal/table" +) + +type ListOptions struct { + Tags, TargetTypes, Actions, Search []string +} + +func optional(values []string) *[]string { + if len(values) == 0 { + return nil + } + return &values +} + +func List(ctx context.Context, c *platform.Client, o ListOptions) error { + var summaries struct { + Templates []struct { + ID string `json:"id"` + TemplateTitle string `json:"templateTitle"` + } `json:"templates"` + } + resp, err := c.GetExperimentTemplates(ctx, &api.GetExperimentTemplatesParams{ + Tag: optional(o.Tags), TargetType: optional(o.TargetTypes), Action: optional(o.Actions), FreeTextPhrases: optional(o.Search), + }) + if _, err := platform.Decode(resp, err, &summaries); err != nil { + return platform.Failed(err, "Failed to get the experiment templates") + } + if len(summaries.Templates) == 0 { + fmt.Println("No experiment templates found.") + return nil + } + t := table.New(table.Column{Name: "id", Title: "Id", Alignment: table.Left}, table.Column{Name: "templateTitle", Title: "Title", Alignment: table.Left}) + for _, s := range summaries.Templates { + t.AddRow(table.Default, table.Cell("id", s.ID), table.Cell("templateTitle", s.TemplateTitle)) + } + t.Print() + return nil +} + +// Fetch gets a template, reporting one that does not exist by name. +func Fetch(ctx context.Context, c *platform.Client, id string) (*output.Document, error) { + var uuid openapi_types.UUID + if err := uuid.UnmarshalText([]byte(id)); err != nil { + return nil, fmt.Errorf("Experiment template %s not found.", id) + } + doc, _, err := platform.ReadDocument(c.GetExperimentTemplate(ctx, uuid)) + if platform.IsStatus(err, http.StatusNotFound) { + return nil, fmt.Errorf("Experiment template %s not found.", id) + } + if err != nil { + return nil, platform.Failed(err, "Failed to get experiment template %s", id) + } + return doc, nil +} + +type GetOptions struct { + ID, File, Type string + Placeholders bool +} + +func Get(ctx context.Context, c *platform.Client, o GetOptions) error { + doc, err := Fetch(ctx, c, o.ID) + if err != nil { + return err + } + if o.Placeholders { + // A starting point for --placeholders: every key the template asks for, empty. + values := jsyaml.NewMap() + placeholders, _ := doc.Value().Get("placeholders") + list, _ := placeholders.([]any) + for _, p := range list { + if m, ok := p.(*jsyaml.Map); ok { + if key, ok := m.Get("key"); ok { + values.Set(fmt.Sprint(key), "") + } + } + } + doc = output.NewDocument(values) + } + if err := resource.Output(doc, o.File, o.Type); err != nil { + return err + } + if o.File != "" { + fmt.Printf("Experiment template %s written to %s.\n", o.ID, o.File) + } + return nil +} From e45c36de3ed60ad800e6ea79244bcf2dbb9c6410 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 14:03:39 +0200 Subject: [PATCH 4/9] test(go): command tests against a fake platform internal/platformtest runs commands against an httptest server that records every request. Covers the transport (429s for any method, transport retries only for idempotent ones, the problem body in errors), every command group, the help examples and the SPDX headers. Fixes a crash on a placeholders file listing plain strings. --- internal/advice/advice_test.go | 50 +++ internal/cli/help_test.go | 29 ++ internal/execution/execution_test.go | 152 +++++++++ internal/experiment/experiment.go | 5 +- internal/experiment/experiment_test.go | 308 ++++++++++++++++++ internal/experiment/template.go | 5 +- internal/platform/client.go | 16 +- internal/platform/client_test.go | 115 +++++++ internal/platformtest/platformtest.go | 170 ++++++++++ internal/schedule/schedule_test.go | 132 ++++++++ internal/service/service_test.go | 154 +++++++++ .../serviceprofile/serviceprofile_test.go | 73 +++++ internal/template/template_test.go | 51 +++ internal/tools/headers_test.go | 38 +++ 14 files changed, 1291 insertions(+), 7 deletions(-) create mode 100644 internal/advice/advice_test.go create mode 100644 internal/cli/help_test.go create mode 100644 internal/execution/execution_test.go create mode 100644 internal/experiment/experiment_test.go create mode 100644 internal/platform/client_test.go create mode 100644 internal/platformtest/platformtest.go create mode 100644 internal/schedule/schedule_test.go create mode 100644 internal/service/service_test.go create mode 100644 internal/serviceprofile/serviceprofile_test.go create mode 100644 internal/template/template_test.go create mode 100644 internal/tools/headers_test.go diff --git a/internal/advice/advice_test.go b/internal/advice/advice_test.go new file mode 100644 index 0000000..945fcd3 --- /dev/null +++ b/internal/advice/advice_test.go @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package advice_test + +import ( + "context" + "testing" + + "github.com/steadybit/cli/internal/advice" + "github.com/steadybit/cli/internal/platformtest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func item(ref, label, status string) map[string]any { + return map[string]any{"target": map[string]any{"reference": ref}, "advice": map[string]any{"label": label, "status": status}} +} + +func TestPagesThroughAdviceAndReportsMismatches(t *testing.T) { + p := platformtest.New(t) + p.Handle("POST /api/advice", func(r platformtest.Request) platformtest.Reply { + if r.JSON(t).(map[string]any)["offset"] == float64(0) { + return platformtest.Reply{JSON: map[string]any{"totalItems": 3, "nextOffset": 2, "items": []any{item("t-1", "a-1", "IMPLEMENTED"), item("t-2", "a-2", "ACTION_NEEDED")}}} + } + return platformtest.Reply{JSON: map[string]any{"totalItems": 3, "items": []any{item("t-3", "a-3", "IMPLEMENTED")}}} + }) + + out, err := platformtest.Stdout(t, func() error { + return advice.ValidateStatus(context.Background(), p.Client, advice.Options{Environment: "Global", Query: "a=b", Status: "Implemented"}) + }) + + assert.EqualError(t, err, "1 of 3 advice did not match the expected status.") + assert.Contains(t, out, "Fetched 2 of 3 matching advice.\nFetched 3 of 3 matching advice.\n") + first := p.Requests("POST /api/advice")[0].JSON(t) + assert.Equal(t, map[string]any{"environmentName": "Global", "offset": float64(0), "query": "a=b"}, first) +} + +// The platform reports IMPLEMENTED while the flag defaults to Implemented; case and the +// separator are ignored, so `action needed` matches too. +func TestMatchesStatusesRegardlessOfCaseAndSeparator(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/advice", platformtest.Reply{JSON: map[string]any{"totalItems": 1, "items": []any{item("t", "a", "ACTION_NEEDED")}}}) + + _, err := platformtest.Stdout(t, func() error { + return advice.ValidateStatus(context.Background(), p.Client, advice.Options{Environment: "Global", Status: "action needed"}) + }) + + require.NoError(t, err) +} diff --git a/internal/cli/help_test.go b/internal/cli/help_test.go new file mode 100644 index 0000000..b4ae286 --- /dev/null +++ b/internal/cli/help_test.go @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +// Every command shows an example in its help: an option list says what can be passed, +// not which combination does what a pipeline author came for. +func TestEveryCommandHasAnExample(t *testing.T) { + var walk func(*cobra.Command) + walk = func(cmd *cobra.Command) { + if cmd.Name() == "help" { + return + } + if !cmd.HasSubCommands() { + assert.NotEmpty(t, cmd.Example, cmd.CommandPath()) + } + for _, sub := range cmd.Commands() { + walk(sub) + } + } + walk(newRoot()) +} diff --git a/internal/execution/execution_test.go b/internal/execution/execution_test.go new file mode 100644 index 0000000..7a8d505 --- /dev/null +++ b/internal/execution/execution_test.go @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package execution_test + +import ( + "context" + "net/http" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/steadybit/cli/internal/execution" + "github.com/steadybit/cli/internal/output" + "github.com/steadybit/cli/internal/platformtest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ctx = context.Background() + +const run = `{"id":42,"key":"ADM-1","state":"COMPLETED","steps":[ + {"stepType":"wait"}, + {"stepType":"action","actionId":"com.steadybit.extension_jmeter.run","targetExecutions":[ + {"id":"te-1","name":"host-a","artifacts":["report.zip","log.txt"]}, + {"id":"te-2","name":"host-b","artifacts":["report.zip"]}]}, + {"stepType":"service-validation","customLabel":"shop is healthy","validations":[ + {"stepType":"action","targetExecutions":[{"id":"te-3","name":"check","artifacts":["result.json"]}]}]}]}` + +func TestCollectsArtifactsOfActionsAndServiceValidations(t *testing.T) { + doc, err := output.ParseDocument([]byte(run)) + require.NoError(t, err) + + assert.Equal(t, []execution.Artifact{ + {Step: "com.steadybit.extension_jmeter.run", Target: "host-a", TargetExecutionID: "te-1", ArtifactID: "report.zip"}, + {Step: "com.steadybit.extension_jmeter.run", Target: "host-a", TargetExecutionID: "te-1", ArtifactID: "log.txt"}, + {Step: "com.steadybit.extension_jmeter.run", Target: "host-b", TargetExecutionID: "te-2", ArtifactID: "report.zip"}, + {Step: "shop is healthy", Target: "check", TargetExecutionID: "te-3", ArtifactID: "result.json"}, + }, execution.Collect(doc.Value())) +} + +// The platform leaves the steps out unless asked, and with them every artifact. +func TestAsksForTheSteps(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/executions/42", platformtest.Reply{JSON: map[string]any{"id": 42, "state": "RUNNING"}}) + + out, err := platformtest.Stdout(t, func() error { return execution.Get(ctx, p.Client, execution.GetOptions{ID: 42}) }) + + require.NoError(t, err) + assert.Equal(t, "id: 42\nstate: RUNNING\n\n", out) + assert.Equal(t, []string{"steps"}, p.Requests("GET /api/experiments/executions/42")[0].Query["fields"]) +} + +func TestCancel(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/executions/42/cancel", platformtest.Reply{Status: http.StatusAccepted}) + p.Reply("POST /api/experiments/executions/43/cancel", platformtest.Reply{Status: http.StatusOK}) + p.Reply("POST /api/experiments/executions/44/cancel", platformtest.Reply{Status: http.StatusNotFound}) + + out, err := platformtest.Stdout(t, func() error { + if err := execution.Cancel(ctx, p.Client, 42); err != nil { + return err + } + return execution.Cancel(ctx, p.Client, 43) + }) + require.NoError(t, err) + assert.Equal(t, "Experiment run 42 is being canceled.\nExperiment run 43 has already ended.\n", out) + assert.EqualError(t, execution.Cancel(ctx, p.Client, 44), "Experiment run 44 not found.") +} + +func TestPropertyValues(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/executions/42/properties/*/set", platformtest.Reply{}) + p.Reply("POST /api/experiments/executions/42/properties/*/add", platformtest.Reply{}) + set := func(values []string, asJSON bool) string { + _, err := platformtest.Stdout(t, func() error { + return execution.SetProperty(ctx, p.Client, execution.PropertyOptions{ID: 42, Key: "k", Values: values, JSON: asJSON}) + }) + require.NoError(t, err) + requests := p.Requests("POST /api/experiments/executions/42/properties/k/set") + return string(requests[len(requests)-1].Body) + } + + assert.Equal(t, `"0042"`, set([]string{"0042"}, false), "a number-like value stays a string") + assert.Equal(t, `["a","b"]`, set([]string{"a", "b"}, false)) + assert.Equal(t, `7`, set([]string{"7"}, true)) + assert.Equal(t, `0`, set([]string{"0"}, true), "falsy values are sent") + assert.Equal(t, `false`, set([]string{"false"}, true)) + assert.Equal(t, `""`, set([]string{""}, false)) + + err := execution.SetProperty(ctx, p.Client, execution.PropertyOptions{ID: 42, Key: "k", Values: []string{"seven"}, JSON: true}) + assert.ErrorContains(t, err, "'seven' is not valid JSON") + assert.EqualError(t, execution.AddProperty(ctx, p.Client, execution.PropertyOptions{ID: 42, Key: "k", Values: []string{"a", "b"}}), + "Adding to a list property takes exactly one --value.") +} + +func TestPropertyErrorsNameTheProperty(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/executions/42/properties/locked/set", platformtest.Reply{Status: 422, JSON: map[string]any{"title": "not editable"}}) + + err := execution.SetProperty(ctx, p.Client, execution.PropertyOptions{ID: 42, Key: "locked", Values: []string{"x"}}) + + assert.ErrorContains(t, err, "Failed to set property locked of experiment run 42: ") + assert.ErrorContains(t, err, "not editable") +} + +func TestDownloadsEveryArtifactIntoADirectoryPerTarget(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/executions/42", platformtest.Reply{Body: run}) + p.Handle("GET /api/experiments/executions/42/artifacts/*/*", func(r platformtest.Request) platformtest.Reply { + parts := strings.Split(r.Path, "/") + return platformtest.Reply{Body: "content of " + parts[6] + "/" + parts[7]} + }) + dir := t.TempDir() + + _, err := platformtest.Stdout(t, func() error { return execution.Download(ctx, p.Client, execution.DownloadOptions{ID: 42, Directory: dir}) }) + + require.NoError(t, err) + for _, f := range []string{"te-1/report.zip", "te-1/log.txt", "te-2/report.zip", "te-3/result.json"} { + content, err := os.ReadFile(filepath.Join(dir, f)) + require.NoError(t, err) + assert.Equal(t, "content of "+f, string(content)) + } +} + +func TestDownloadToASingleFile(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/executions/42", platformtest.Reply{Body: run}) + p.Reply("GET /api/experiments/executions/42/artifacts/te-1/log.txt", platformtest.Reply{Body: "the log"}) + file := filepath.Join(t.TempDir(), "my.log") + + _, err := platformtest.Stdout(t, func() error { + return execution.Download(ctx, p.Client, execution.DownloadOptions{ID: 42, Artifact: "log.txt", Output: file}) + }) + + require.NoError(t, err) + content, _ := os.ReadFile(file) + assert.Equal(t, "the log", string(content)) + err = execution.Download(ctx, p.Client, execution.DownloadOptions{ID: 42, Artifact: "report.zip", Output: file}) + assert.EqualError(t, err, "2 artifacts match, but --output takes exactly one. Narrow it down with --artifact and --target-execution.") + err = execution.Download(ctx, p.Client, execution.DownloadOptions{ID: 42, Artifact: "nope"}) + assert.EqualError(t, err, "No matching artifacts found in experiment run 42.") +} + +func TestNeverLetsAnIdStepOutOfADirectory(t *testing.T) { + for _, id := range []string{"..", ".", "", "../..", "a/../..", "/"} { + assert.Equal(t, "_", execution.PathSegment(id), id) + } + assert.Equal(t, "evil", execution.PathSegment("../../evil")) + assert.Equal(t, "report.zip", execution.PathSegment("report.zip")) +} diff --git a/internal/experiment/experiment.go b/internal/experiment/experiment.go index 1f77567..00d15d9 100644 --- a/internal/experiment/experiment.go +++ b/internal/experiment/experiment.go @@ -422,6 +422,9 @@ func runTemplate(ctx context.Context, c *platform.Client, o RunOptions, parallel return decodeStarted(body, resp, "") } +// PollInterval is how often --wait asks for the state of a run. Tests shorten it. +var PollInterval = 5 * time.Second + var terminal = map[string]bool{"FAILED": true, "ERRORED": true, "CANCELED": true, "COMPLETED": true} // wait polls the run until it ends. A run that did not complete exits non-zero, which is @@ -432,7 +435,7 @@ func wait(ctx context.Context, c *platform.Client, location string) error { path = location[i:] } for { - time.Sleep(5 * time.Second) + time.Sleep(PollInterval) body, _, err := platform.Read(c.Get(ctx, path)) if err != nil { return platform.Failed(err, "Failed to get experiment run ") diff --git a/internal/experiment/experiment_test.go b/internal/experiment/experiment_test.go new file mode 100644 index 0000000..b3cd232 --- /dev/null +++ b/internal/experiment/experiment_test.go @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package experiment_test + +import ( + "context" + "net/http" + "os" + "path/filepath" + "sync/atomic" + "testing" + "time" + + "github.com/steadybit/cli/internal/experiment" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/platformtest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func init() { + platform.RetryUnit = time.Millisecond + experiment.PollInterval = time.Millisecond +} + +var ctx = context.Background() + +const design = `{"key":"TST-1","version":3,"name":"Verify TTR","team":"TST","environment":"Global","lanes":[{"steps":[{"type":"wait","parameters":{"duration":"10s"}}]}]}` + +func TestGetPrintsYAMLWithoutTheVersion(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/TST-1", platformtest.Reply{Body: design}) + + out, err := platformtest.Stdout(t, func() error { return experiment.Get(ctx, p.Client, experiment.GetOptions{Key: "TST-1"}) }) + + require.NoError(t, err) + assert.Equal(t, "key: TST-1\nname: Verify TTR\nteam: TST\nenvironment: Global\nlanes:\n - steps:\n - type: wait\n parameters:\n duration: 10s\n\n", out) +} + +func TestGetWritesCompactJSONFiles(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/TST-1", platformtest.Reply{Body: design}) + file := filepath.Join(t.TempDir(), "experiment.json") + + out, err := platformtest.Stdout(t, func() error { + return experiment.Get(ctx, p.Client, experiment.GetOptions{Key: "TST-1", File: file}) + }) + + require.NoError(t, err) + assert.Equal(t, "Experiment TST-1 written to "+file+".\n", out) + content, _ := os.ReadFile(file) + assert.Equal(t, `{"key":"TST-1","name":"Verify TTR","team":"TST","environment":"Global","lanes":[{"steps":[{"type":"wait","parameters":{"duration":"10s"}}]}]}`, string(content)) +} + +func TestGetReportsAMissingExperiment(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/TST-9", platformtest.Reply{Status: http.StatusNotFound}) + + err := experiment.Get(ctx, p.Client, experiment.GetOptions{Key: "TST-9"}) + + assert.EqualError(t, err, "Experiment TST-9 not found.") +} + +func TestApplyCreatesAndPrependsTheKeyKeepingTheFile(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments", platformtest.Reply{Status: http.StatusCreated, Headers: map[string]string{"Location": p.URL + "/api/experiments/NEW-1"}}) + file := filepath.Join(t.TempDir(), "experiment.yml") + original := "# kept\nname: new\nlanes:\n - steps:\n - &w\n type: wait\n - <<: *w\n" + require.NoError(t, os.WriteFile(file, []byte(original), 0o644)) + + out, err := platformtest.Stdout(t, func() error { return experiment.Apply(ctx, p.Client, experiment.ApplyOptions{Files: []string{file}}) }) + + require.NoError(t, err) + assert.Equal(t, "Experiment NEW-1 created.\n", out) + content, _ := os.ReadFile(file) + assert.Equal(t, "key: NEW-1\n"+original, string(content)) + // Anchors and merge keys are resolved in what is sent. + assert.Equal(t, map[string]any{"name": "new", "lanes": []any{map[string]any{"steps": []any{ + map[string]any{"type": "wait"}, map[string]any{"type": "wait"}, + }}}}, p.Requests("POST /api/experiments")[0].JSON(t)) +} + +func TestApplyUpdatesByTheKeyInTheFile(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/TST-1", platformtest.Reply{}) + file := filepath.Join(t.TempDir(), "experiment.json") + require.NoError(t, os.WriteFile(file, []byte(design), 0o644)) + + out, err := platformtest.Stdout(t, func() error { return experiment.Apply(ctx, p.Client, experiment.ApplyOptions{Files: []string{file}}) }) + + require.NoError(t, err) + assert.Equal(t, "Experiment TST-1 updated.\n", out) +} + +func TestApplyRefusesAKeyWithSeveralFiles(t *testing.T) { + dir := t.TempDir() + for _, f := range []string{"a.yml", "b.yml"} { + require.NoError(t, os.WriteFile(filepath.Join(dir, f), []byte("name: x\n"), 0o644)) + } + + err := experiment.Apply(ctx, nil, experiment.ApplyOptions{Key: "TST-1", Files: []string{dir}}) + + assert.EqualError(t, err, "If --key is specified, at most one --file can be specified.") +} + +func started(p *platformtest.Platform, key string, run int) platformtest.Reply { + return platformtest.Reply{Status: http.StatusCreated, JSON: map[string]any{ + "key": key, "executionId": run, + "apiLocation": p.URL + "/api/experiments/executions/1", + "uiLocation": "https://ui/" + key, + }, Headers: map[string]string{"Location": "https://elsewhere.example.com/api/experiments/executions/1"}} +} + +func TestRunByKeyAndWaitPollsTheConfiguredHost(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/TST-1/execute", started(p, "TST-1", 1)) + var polls atomic.Int32 + p.Handle("GET /api/experiments/executions/1", func(platformtest.Request) platformtest.Reply { + if polls.Add(1) < 2 { + return platformtest.Reply{JSON: map[string]any{"id": 1, "key": "TST-1", "state": "RUNNING"}} + } + return platformtest.Reply{JSON: map[string]any{"id": 1, "key": "TST-1", "state": "COMPLETED"}} + }) + + out, err := platformtest.Stdout(t, func() error { + return experiment.Run(ctx, p.Client, experiment.RunOptions{Key: "TST-1", Yes: true, Wait: true}) + }) + + require.NoError(t, err) + assert.Equal(t, "Executing experiment: TST-1\nExperiment run API: https://elsewhere.example.com/api/experiments/executions/1\nExperiment run UI: https://ui/TST-1\nCurrent run state: running\nCurrent run state: completed\n", out) + query := p.Requests("POST /api/experiments/TST-1/execute")[0].Query + assert.Equal(t, []string{"false"}, query["allowParallel"]) + assert.Equal(t, []string{"true"}, query["forcePersist"]) +} + +func TestAFailedRunFailsTheCommand(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/TST-1/execute", started(p, "TST-1", 1)) + p.Reply("GET /api/experiments/executions/1", platformtest.Reply{JSON: map[string]any{"id": 1, "key": "TST-1", "state": "FAILED", "reason": "hypothesis violated"}}) + + _, err := platformtest.Stdout(t, func() error { + return experiment.Run(ctx, p.Client, experiment.RunOptions{Key: "TST-1", Yes: true, Wait: true}) + }) + + assert.EqualError(t, err, "Experiment TST-1 (#1) failed, reason: hypothesis violated") +} + +func TestRunRetriesValidationErrorsWithoutPersistingThem(t *testing.T) { + p := platformtest.New(t) + var calls atomic.Int32 + p.Handle("POST /api/experiments/TST-1/execute", func(platformtest.Request) platformtest.Reply { + if calls.Add(1) <= 2 { + return platformtest.Reply{Status: http.StatusUnprocessableEntity, JSON: map[string]any{"title": "no targets"}} + } + return started(p, "TST-1", 1) + }) + + out, err := platformtest.Stdout(t, func() error { + return experiment.Run(ctx, p.Client, experiment.RunOptions{Key: "TST-1", Yes: true, Retries: 2}) + }) + + require.NoError(t, err) + assert.Contains(t, out, "Experiment has validation errors (attempt 1/3). Retrying in 0s...") + for _, r := range p.Requests("POST /api/experiments/TST-1/execute") { + assert.Equal(t, []string{"false"}, r.Query["forcePersist"]) + } +} + +func TestRunGivesUpAfterTheLastRetry(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/TST-1/execute", platformtest.Reply{Status: http.StatusUnprocessableEntity, JSON: map[string]any{"title": "no targets"}}) + + _, err := platformtest.Stdout(t, func() error { + return experiment.Run(ctx, p.Client, experiment.RunOptions{Key: "TST-1", Yes: true, Retries: 1}) + }) + + assert.ErrorContains(t, err, "Failed to execute experiment: Steadybit API at POST") + assert.Len(t, p.Requests("POST /api/experiments/TST-1/execute"), 2) +} + +func TestRunsInParallelWithYesWhenAnotherIsRunning(t *testing.T) { + p := platformtest.New(t) + p.Handle("POST /api/experiments/TST-1/execute", func(r platformtest.Request) platformtest.Reply { + if r.Query["allowParallel"][0] == "true" { + return started(p, "TST-1", 1) + } + return platformtest.Reply{Status: http.StatusConflict, JSON: map[string]any{"type": "https://steadybit.com/problems/another-experiment-running-exception"}} + }) + + _, err := platformtest.Stdout(t, func() error { + return experiment.Run(ctx, p.Client, experiment.RunOptions{Key: "TST-1", Yes: true}) + }) + + require.NoError(t, err) + assert.Len(t, p.Requests("POST /api/experiments/TST-1/execute"), 2) +} + +func TestRunByFileWithoutKeyUpsertsAndWritesTheKey(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/execute", started(p, "NEW-1", 1)) + file := filepath.Join(t.TempDir(), "experiment.yml") + require.NoError(t, os.WriteFile(file, []byte("name: new\n"), 0o644)) + + _, err := platformtest.Stdout(t, func() error { + return experiment.Run(ctx, p.Client, experiment.RunOptions{Files: []string{file}, Yes: true}) + }) + + require.NoError(t, err) + content, _ := os.ReadFile(file) + assert.Equal(t, "key: NEW-1\nname: new\n", string(content)) +} + +func TestRunNeedsSomethingToRun(t *testing.T) { + err := experiment.Run(ctx, nil, experiment.RunOptions{Yes: true}) + + assert.EqualError(t, err, "Either --key, --file or --template must be specified.") +} + +const templateID = "d7e65100-1d20-4980-be87-c351704910b8" + +func templateOptions() experiment.TemplateOptions { + return experiment.TemplateOptions{Template: templateID, Team: "ADM", Placeholder: jsyaml.NewMap(), Variable: jsyaml.NewMap(), ExecutionVariable: jsyaml.NewMap(), ResetProperties: true} +} + +func TestRunFromATemplate(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/templates/"+templateID+"/experiment-execute", started(p, "ADM-12", 7)) + o := experiment.RunOptions{Yes: true, TemplateOptions: templateOptions()} + o.Placeholder.Set("CLUSTER", "prod") + o.ExecutionVariable.Set("region", "eu") + + _, err := platformtest.Stdout(t, func() error { return experiment.Run(ctx, p.Client, o) }) + + require.NoError(t, err) + r := p.Requests("POST /api/experiments/templates/" + templateID + "/experiment-execute")[0] + assert.Equal(t, map[string]any{"team": "ADM", "placeholders": []any{map[string]any{"key": "CLUSTER", "value": "prod"}}, "executionVariables": map[string]any{"region": "eu"}}, r.JSON(t)) + assert.Equal(t, []string{"true"}, r.Query["resetProperties"]) +} + +func TestApplyFromATemplateMergesAPlaceholdersFile(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/templates/"+templateID+"/experiment-create", platformtest.Reply{Status: http.StatusCreated, Headers: map[string]string{"Location": p.URL + "/api/experiments/ADM-12"}}) + file := filepath.Join(t.TempDir(), "values.yml") + require.NoError(t, os.WriteFile(file, []byte("CLUSTER: dev\nREPLICAS: 3\n"), 0o644)) + o := templateOptions() + o.PlaceholdersFile = file + o.Placeholder.Set("CLUSTER", "prod") + o.ResetProperties = false + + out, err := platformtest.Stdout(t, func() error { return experiment.ApplyTemplate(ctx, p.Client, "", o) }) + + require.NoError(t, err) + assert.Equal(t, "Experiment ADM-12 created from template "+templateID+".\n", out) + r := p.Requests("POST /api/experiments/templates/" + templateID + "/experiment-create")[0] + assert.Equal(t, []any{map[string]any{"key": "CLUSTER", "value": "prod"}, map[string]any{"key": "REPLICAS", "value": float64(3)}}, r.JSON(t).(map[string]any)["placeholders"]) + assert.Equal(t, []string{"false"}, r.Query["resetProperties"]) +} + +func TestApplyFromATemplateRefusesWhatAnUpdateIgnores(t *testing.T) { + o := templateOptions() + o.Environment = "Global" + + err := experiment.ApplyTemplate(ctx, nil, "ADM-12", o) + + assert.EqualError(t, err, "Updating experiment ADM-12 from a template only takes placeholders; remove --team, --environment.") +} + +func TestApplyFromATemplateRejectsAnInvalidPlaceholdersFile(t *testing.T) { + file := filepath.Join(t.TempDir(), "values.yml") + require.NoError(t, os.WriteFile(file, []byte("- just\n- strings\n"), 0o644)) + o := templateOptions() + o.PlaceholdersFile = file + + err := experiment.ApplyTemplate(ctx, nil, "", o) + + assert.ErrorContains(t, err, "must be a map of key to value or a list of {key, value} entries.") +} + +func TestDumpWritesEveryTeamAndCountsWhatFailed(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/teams", platformtest.Reply{JSON: map[string]any{"teams": []any{map[string]any{"key": "TST", "name": "Test"}}}}) + p.Reply("GET /api/experiments", platformtest.Reply{JSON: map[string]any{"experiments": []any{map[string]any{"key": "TST-1"}}}}) + p.Reply("GET /api/experiments/TST-1", platformtest.Reply{Body: `{"key":"TST-1","lanes":[{"steps":[{"type":"action","radius":{"query":"x","list":[],"percentage":50}}]}]}`}) + p.Reply("GET /api/experiments/TST-1/executions", platformtest.Reply{JSON: map[string]any{"executions": []any{map[string]any{"id": 1}, map[string]any{"id": 2}}}}) + p.Reply("GET /api/experiments/executions/1", platformtest.Reply{JSON: map[string]any{"id": 1}}) + p.Reply("GET /api/experiments/executions/2", platformtest.Reply{Status: http.StatusInternalServerError}) + dir := t.TempDir() + + out, err := platformtest.Stdout(t, func() error { return experiment.Dump(ctx, p.Client, experiment.DumpOptions{Directory: dir}) }) + + assert.ErrorIs(t, err, experiment.ErrIncomplete) + assert.Equal(t, "Listing experiments for 1 team.\nFetching experiments for team Test (TST)... experiments: 1, executions: 1, failed: 1\nWritten 1 experiments with 1 executions\n", out) + design, _ := os.ReadFile(filepath.Join(dir, "TST-1", "experiment.yaml")) + assert.Equal(t, "key: TST-1\nlanes:\n - steps:\n - type: action\n radius:\n percentage: 50\n", string(design)) + _, err = os.Stat(filepath.Join(dir, "TST-1", "execution-1.yaml")) + assert.NoError(t, err) +} + +func TestDumpRefusesAnUnknownTeam(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/teams", platformtest.Reply{JSON: map[string]any{"teams": []any{map[string]any{"key": "B"}, map[string]any{"key": "A"}}}}) + + err := experiment.Dump(ctx, p.Client, experiment.DumpOptions{Directory: t.TempDir(), Teams: []string{"a", "nope"}}) + + assert.EqualError(t, err, "No accessible team with key NOPE. Available: A, B") +} diff --git a/internal/experiment/template.go b/internal/experiment/template.go index 08c1c39..e9378b2 100644 --- a/internal/experiment/template.go +++ b/internal/experiment/template.go @@ -46,9 +46,12 @@ func ResolvePlaceholders(o TemplateOptions) ([]api.ExperimentTemplatePlaceholder case []any: for _, entry := range v { m, ok := entry.(*jsyaml.Map) + if !ok { + return nil, invalid + } key, hasKey := m.Get("key") value, hasValue := m.Get("value") - if !ok || !hasKey || !hasValue { + if !hasKey || !hasValue { return nil, invalid } keyString, ok := key.(string) diff --git a/internal/platform/client.go b/internal/platform/client.go index e6bd1c0..e496529 100644 --- a/internal/platform/client.go +++ b/internal/platform/client.go @@ -127,7 +127,13 @@ func Check(resp *http.Response, body []byte) error { return &APIError{Method: resp.Request.Method, URL: resp.Request.URL.String(), Status: resp.StatusCode, Body: body} } -const maxRateLimitWait = 2 * time.Minute +var ( + // RetryUnit is the base of every wait between attempts: the backoff after a transport + // failure, and a 429 without a reset header. Tests shorten it. + RetryUnit = time.Second + // MaxRateLimitWait bounds the total time spent waiting out 429s for one request. + MaxRateLimitWait = 2 * time.Minute +) const defaultTimeout = 30 * time.Second @@ -190,7 +196,7 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { if !idempotent[req.Method] || attempt >= 4 { return nil, fmt.Errorf("Failed to call Steadybit API at %s %s: %w", req.Method, req.URL, err) } - time.Sleep(jitter(time.Duration(attempt) * time.Second)) + time.Sleep(jitter(time.Duration(attempt) * RetryUnit)) continue } logResponse(resp) @@ -198,14 +204,14 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { resp.Body = cancelOnClose{ReadCloser: resp.Body, cancel: cancel} return resp, nil } - wait := time.Second + wait := RetryUnit for _, h := range []string{"RateLimit-Reset", "Retry-After"} { if seconds, err := strconv.Atoi(resp.Header.Get(h)); err == nil && seconds > 0 { - wait = time.Duration(seconds) * time.Second + wait = time.Duration(seconds) * RetryUnit break } } - if waited+wait > maxRateLimitWait { + if waited+wait > MaxRateLimitWait { resp.Body = cancelOnClose{ReadCloser: resp.Body, cancel: cancel} return resp, nil } diff --git a/internal/platform/client_test.go b/internal/platform/client_test.go new file mode 100644 index 0000000..67ac779 --- /dev/null +++ b/internal/platform/client_test.go @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package platform_test + +import ( + "context" + "net" + "net/http" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/steadybit/cli/internal/platform" + "github.com/steadybit/cli/internal/platformtest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func init() { + platform.RetryUnit = time.Millisecond +} + +func TestSendsTheTokenAndUserAgent(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/teams", platformtest.Reply{JSON: map[string]any{"teams": []any{}}}) + + _, _, err := platform.Read(p.Client.GetTeams(context.Background(), nil)) + + require.NoError(t, err) + r := p.Requests("GET /api/teams")[0] + assert.Equal(t, "accessToken test-token", r.Header.Get("Authorization")) + assert.True(t, strings.HasPrefix(r.Header.Get("User-Agent"), "steadybit@")) +} + +func TestWaitsOutRateLimitsForAnyMethod(t *testing.T) { + p := platformtest.New(t) + var calls atomic.Int32 + p.Handle("POST /api/experiments", func(platformtest.Request) platformtest.Reply { + if calls.Add(1) <= 3 { + return platformtest.Reply{Status: http.StatusTooManyRequests, Headers: map[string]string{"RateLimit-Reset": "1"}} + } + return platformtest.Reply{Status: http.StatusCreated} + }) + + _, resp, err := platform.Read(p.Client.CreateOrUpdateExperimentWithBody(context.Background(), "application/json", strings.NewReader("{}"))) + + require.NoError(t, err) + assert.Equal(t, http.StatusCreated, resp.StatusCode) + assert.EqualValues(t, 4, calls.Load()) + // The body is sent again on every attempt, not only the first. + for _, r := range p.Requests("POST /api/experiments") { + assert.Equal(t, "{}", string(r.Body)) + } +} + +func TestGivesUpOnRateLimitsOnceTheBudgetIsSpent(t *testing.T) { + p := platformtest.New(t) + original := platform.MaxRateLimitWait + platform.MaxRateLimitWait = 5 * time.Millisecond + t.Cleanup(func() { platform.MaxRateLimitWait = original }) + p.Reply("GET /api/teams", platformtest.Reply{Status: http.StatusTooManyRequests, Body: "slow down"}) + + _, _, err := platform.Read(p.Client.GetTeams(context.Background(), nil)) + + assert.ErrorContains(t, err, "responded with unexpected status code: 429 - slow down") +} + +// A POST that failed in transit may still have started a run, so only methods defined to +// be idempotent are repeated. +func TestRetriesTransportFailuresOnlyForIdempotentMethods(t *testing.T) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + var accepted atomic.Int32 + go func() { + for { + conn, err := listener.Accept() + if err != nil { + return + } + accepted.Add(1) + _ = conn.Close() // every connection fails before a response + } + }() + t.Cleanup(func() { _ = listener.Close() }) + t.Setenv("HOME", t.TempDir()) + t.Setenv("STEADYBIT_URL", "http://"+listener.Addr().String()) + t.Setenv("STEADYBIT_TOKEN", "t") + client, err := platform.New() + require.NoError(t, err) + + _, _, err = platform.Read(client.GetTeams(context.Background(), nil)) + assert.ErrorContains(t, err, "Failed to call Steadybit API at GET") + gets := accepted.Load() + + _, _, err = platform.Read(client.CreateOrUpdateExperimentWithBody(context.Background(), "application/json", strings.NewReader("{}"))) + assert.Error(t, err) + + assert.EqualValues(t, 4, gets) + assert.EqualValues(t, 1, accepted.Load()-gets) +} + +func TestReportsTheProblemBodyAsTheTypeScriptCLIDid(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/teams", platformtest.Reply{Status: 422, Body: `{"title":"bad","violations":[]}`}) + + _, _, err := platform.Read(p.Client.GetTeams(context.Background(), nil)) + err = platform.Failed(err, "Failed to get %s", "teams") + + assert.Equal(t, "Failed to get teams: Steadybit API at GET "+p.URL+`/api/teams responded with unexpected status code: 422 - {"title":"bad","violations":[]}: { + "title": "bad", + "violations": [] +}`, err.Error()) +} diff --git a/internal/platformtest/platformtest.go b/internal/platformtest/platformtest.go new file mode 100644 index 0000000..d8ca345 --- /dev/null +++ b/internal/platformtest/platformtest.go @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package platformtest runs commands against a fake platform: an httptest server whose +// endpoints a test declares, which records every request it receives. +package platformtest + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "os" + "strings" + "sync" + "testing" + + "github.com/steadybit/cli/internal/platform" +) + +type Request struct { + Method string + Path string + Query map[string][]string + Header http.Header + Body []byte +} + +// JSON decodes the request body. +func (r Request) JSON(t *testing.T) any { + t.Helper() + var v any + if err := json.Unmarshal(r.Body, &v); err != nil { + t.Fatalf("request body is not JSON: %s", r.Body) + } + return v +} + +type Reply struct { + Status int + JSON any + Body string + Headers map[string]string +} + +type Platform struct { + t *testing.T + server *httptest.Server + mu sync.Mutex + routes map[string]func(Request) Reply + requests []Request + Client *platform.Client + URL string +} + +// New starts a fake platform and points the CLI configuration at it. +func New(t *testing.T) *Platform { + t.Helper() + p := &Platform{t: t, routes: map[string]func(Request) Reply{}} + p.server = httptest.NewServer(http.HandlerFunc(p.serve)) + t.Cleanup(p.server.Close) + p.URL = p.server.URL + t.Setenv("HOME", t.TempDir()) + t.Setenv("STEADYBIT_URL", p.server.URL) + t.Setenv("STEADYBIT_TOKEN", "test-token") + client, err := platform.New() + if err != nil { + t.Fatal(err) + } + p.Client = client + return p +} + +// Handle answers "METHOD /path"; a path segment written as * matches any value. +func (p *Platform) Handle(route string, reply func(Request) Reply) { + p.mu.Lock() + defer p.mu.Unlock() + p.routes[route] = reply +} + +// Reply answers a route with a fixed reply. +func (p *Platform) Reply(route string, reply Reply) { + p.Handle(route, func(Request) Reply { return reply }) +} + +func (p *Platform) Requests(route string) []Request { + p.mu.Lock() + defer p.mu.Unlock() + method, path, _ := strings.Cut(route, " ") + var matching []Request + for _, r := range p.requests { + if r.Method == method && matches(path, r.Path) { + matching = append(matching, r) + } + } + return matching +} + +func matches(pattern, path string) bool { + a, b := strings.Split(pattern, "/"), strings.Split(path, "/") + if len(a) != len(b) { + return false + } + for i := range a { + if a[i] != "*" && a[i] != b[i] { + return false + } + } + return true +} + +func (p *Platform) serve(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + request := Request{Method: r.Method, Path: r.URL.EscapedPath(), Query: r.URL.Query(), Header: r.Header, Body: body} + p.mu.Lock() + p.requests = append(p.requests, request) + var handler func(Request) Reply + for route, h := range p.routes { + method, path, _ := strings.Cut(route, " ") + if method == r.Method && matches(path, request.Path) { + handler = h + break + } + } + p.mu.Unlock() + if handler == nil { + p.t.Errorf("unexpected request %s %s", r.Method, request.Path) + w.WriteHeader(http.StatusNotImplemented) + return + } + reply := handler(request) + for k, v := range reply.Headers { + w.Header().Set(k, v) + } + status := reply.Status + if status == 0 { + status = http.StatusOK + } + switch { + case reply.JSON != nil: + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + _ = json.NewEncoder(w).Encode(reply.JSON) + default: + w.WriteHeader(status) + _, _ = w.Write([]byte(reply.Body)) + } +} + +// Stdout captures what fn prints. +func Stdout(t *testing.T, fn func() error) (string, error) { + t.Helper() + original := os.Stdout + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + os.Stdout = w + done := make(chan string) + go func() { + var buf bytes.Buffer + _, _ = io.Copy(&buf, r) + done <- buf.String() + }() + runErr := fn() + _ = w.Close() + os.Stdout = original + return <-done, runErr +} diff --git a/internal/schedule/schedule_test.go b/internal/schedule/schedule_test.go new file mode 100644 index 0000000..9464394 --- /dev/null +++ b/internal/schedule/schedule_test.go @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package schedule_test + +import ( + "context" + "net/http" + "os" + "path/filepath" + "testing" + + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/platformtest" + "github.com/steadybit/cli/internal/schedule" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ctx = context.Background() + +const id = "01951394-727f-76a0-8675-c7519ebd0ff5" + +var saved = map[string]any{ + "id": id, "experimentKey": "ADM-1", "cron": "0 0 9 ? * MON-FRI", "timezone": "Europe/Berlin", "enabled": true, + "allowParallel": false, "editedBy": map[string]any{"username": "jane"}, "lastUpdated": "2026-09-01T10:00:00Z", "nextExecution": "2026-09-02T07:00:00Z", +} + +func TestListFiltersWithRepeatedParameters(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/schedules/v2", platformtest.Reply{JSON: []any{saved}}) + + out, err := platformtest.Stdout(t, func() error { + return schedule.List(ctx, p.Client, schedule.ListOptions{Teams: []string{"ADM", "OPS"}, Experiments: []string{"ADM-1"}}) + }) + + require.NoError(t, err) + assert.Equal(t, []string{"ADM", "OPS"}, p.Requests("GET /api/experiments/schedules/v2")[0].Query["team"]) + assert.Contains(t, out, "0 0 9 ? * MON-FRI (Europe/Berlin)") +} + +func TestGetLeavesOutWhatCannotBeSentBack(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/schedules/"+id, platformtest.Reply{Body: `{"id":"` + id + `","experimentKey":"ADM-1","cron":"x","editedBy":{},"lastUpdated":"y","nextExecution":"z"}`}) + file := filepath.Join(t.TempDir(), "schedule.yml") + + _, err := platformtest.Stdout(t, func() error { return schedule.Get(ctx, p.Client, schedule.GetOptions{ID: id, File: file}) }) + + require.NoError(t, err) + content, _ := os.ReadFile(file) + assert.Equal(t, "id: "+id+"\nexperimentKey: ADM-1\ncron: x\n", string(content)) +} + +func TestApplyCreatesAndWritesTheIdFirst(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/schedules", platformtest.Reply{Status: http.StatusCreated, JSON: saved}) + file := filepath.Join(t.TempDir(), "new.yml") + require.NoError(t, os.WriteFile(file, []byte("experimentKey: ADM-1\nstartAt: 2030-06-01T09:00:00Z\n"), 0o644)) + + out, err := platformtest.Stdout(t, func() error { return schedule.Apply(ctx, p.Client, schedule.ApplyOptions{Files: []string{file}}) }) + + require.NoError(t, err) + assert.Equal(t, "Experiment schedule "+id+" for ADM-1 created.\n", out) + // A YAML timestamp was a JavaScript Date: sent, and written back, as an ISO string. + assert.Equal(t, map[string]any{"experimentKey": "ADM-1", "startAt": "2030-06-01T09:00:00.000Z"}, p.Requests("POST /api/experiments/schedules")[0].JSON(t)) + content, _ := os.ReadFile(file) + assert.Equal(t, "id: "+id+"\nexperimentKey: ADM-1\nstartAt: 2030-06-01T09:00:00.000Z\n", string(content)) +} + +func TestApplyKeepsAFileThatHasAnId(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/schedules", platformtest.Reply{JSON: saved}) + file := filepath.Join(t.TempDir(), "s.json") + original := `{"id":"` + id + `","experimentKey":"ADM-1","editedBy":{"username":"jane"}}` + require.NoError(t, os.WriteFile(file, []byte(original), 0o644)) + + _, err := platformtest.Stdout(t, func() error { return schedule.Apply(ctx, p.Client, schedule.ApplyOptions{Files: []string{file}}) }) + + require.NoError(t, err) + assert.Equal(t, map[string]any{"id": id, "experimentKey": "ADM-1"}, p.Requests("POST /api/experiments/schedules")[0].JSON(t)) + content, _ := os.ReadFile(file) + assert.Equal(t, original, string(content)) +} + +func TestCreateAndItsRules(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/experiments/schedules", platformtest.Reply{Status: http.StatusCreated, JSON: saved}) + vars := jsyaml.NewMap() + vars.Set("region", "eu") + no := false + + _, err := platformtest.Stdout(t, func() error { + return schedule.Create(ctx, p.Client, schedule.CreateOptions{Experiment: "ADM-1", Fields: schedule.Fields{Cron: "0 0 9 ? * *", Timezone: "Europe/Berlin", AllowParallel: &no, Variables: vars}}) + }) + + require.NoError(t, err) + assert.Equal(t, map[string]any{"experimentKey": "ADM-1", "cron": "0 0 9 ? * *", "timezone": "Europe/Berlin", "allowParallel": false, "variables": map[string]any{"region": "eu"}, "enabled": true}, + p.Requests("POST /api/experiments/schedules")[0].JSON(t)) + assert.EqualError(t, schedule.Create(ctx, p.Client, schedule.CreateOptions{Experiment: "ADM-1"}), "Either --cron or --start-at must be specified.") + assert.EqualError(t, schedule.Create(ctx, p.Client, schedule.CreateOptions{Experiment: "ADM-1", Fields: schedule.Fields{Cron: "x", StartAt: "y"}}), "--cron and --start-at cannot be combined.") +} + +func TestUpdateSendsOnlyWhatWasGiven(t *testing.T) { + p := platformtest.New(t) + p.Reply("PATCH /api/experiments/schedules/"+id, platformtest.Reply{JSON: saved}) + + out, err := platformtest.Stdout(t, func() error { + if err := schedule.Update(ctx, p.Client, schedule.UpdateOptions{ID: id, Fields: schedule.Fields{Cron: "0 30 8 ? * *"}}); err != nil { + return err + } + return schedule.SetEnabled(ctx, p.Client, id, false) + }) + + require.NoError(t, err) + requests := p.Requests("PATCH /api/experiments/schedules/" + id) + assert.Equal(t, map[string]any{"cron": "0 30 8 ? * *"}, requests[0].JSON(t)) + assert.Equal(t, map[string]any{"enabled": false}, requests[1].JSON(t)) + assert.Contains(t, out, "Experiment schedule "+id+" for ADM-1 disabled.") + assert.EqualError(t, schedule.Update(ctx, p.Client, schedule.UpdateOptions{ID: id}), "Nothing to update. Pass at least one of the options, see --help.") +} + +func TestDelete(t *testing.T) { + p := platformtest.New(t) + p.Reply("DELETE /api/experiments/schedules/"+id, platformtest.Reply{}) + p.Reply("DELETE /api/experiments/schedules/nope", platformtest.Reply{Status: http.StatusNotFound}) + + out, err := platformtest.Stdout(t, func() error { return schedule.Delete(ctx, p.Client, id) }) + + require.NoError(t, err) + assert.Equal(t, "Experiment schedule "+id+" deleted.\n", out) + assert.EqualError(t, schedule.Delete(ctx, p.Client, "nope"), "Experiment schedule nope not found.") +} diff --git a/internal/service/service_test.go b/internal/service/service_test.go new file mode 100644 index 0000000..684ba50 --- /dev/null +++ b/internal/service/service_test.go @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package service_test + +import ( + "context" + "net/http" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/steadybit/cli/internal/experiment" + "github.com/steadybit/cli/internal/jsyaml" + "github.com/steadybit/cli/internal/platformtest" + "github.com/steadybit/cli/internal/service" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ctx = context.Background() + +const id = "019cd80d-a4c9-775b-bdf8-2672a280ce7c" + +const stored = `{"name":"Checkout","environment":"Global","team":"ADM","query":"x","validations":[],"serviceProfile":"P","variables":{"region":"eu"},"id":"` + id + `","version":3,"created":"c","createdBy":{},"edited":"e","editedBy":{}}` + +func TestListWalksEveryPage(t *testing.T) { + p := platformtest.New(t) + p.Handle("GET /api/services", func(r platformtest.Request) platformtest.Reply { + if r.Query["page"][0] == "0" { + return platformtest.Reply{JSON: map[string]any{"items": []any{map[string]any{"id": "a", "name": "Checkout"}}, "nextPage": 1}} + } + return platformtest.Reply{JSON: map[string]any{"items": []any{map[string]any{"id": "b", "name": "Catalog"}}}} + }) + + out, err := platformtest.Stdout(t, func() error { return service.List(ctx, p.Client, service.ListOptions{Teams: []string{"ADM"}}) }) + + require.NoError(t, err) + assert.Contains(t, out, "Checkout") + assert.Contains(t, out, "Catalog") + first := p.Requests("GET /api/services")[0] + assert.Equal(t, []string{"ADM"}, first.Query["teamKey"]) + assert.Equal(t, []string{"100"}, first.Query["size"]) +} + +func TestGetAndApplyRoundTrip(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/services/"+id, platformtest.Reply{Body: stored}) + p.Reply("POST /api/services", platformtest.Reply{JSON: map[string]any{"id": id, "name": "Checkout"}}) + file := filepath.Join(t.TempDir(), "service.yml") + + _, err := platformtest.Stdout(t, func() error { + if err := service.Get(ctx, p.Client, service.GetOptions{ID: id, File: file}); err != nil { + return err + } + return service.Apply(ctx, p.Client, service.ApplyOptions{Files: []string{file}}) + }) + + require.NoError(t, err) + content, _ := os.ReadFile(file) + for _, field := range []string{"version", "created", "edited"} { + assert.NotContains(t, string(content), field+":") + } + sent := p.Requests("POST /api/services")[0] + assert.Equal(t, []string{"false"}, sent.Query["deleteExperiments"]) + assert.NotContains(t, sent.JSON(t), "version") +} + +func TestApplyPointsAtDeleteExperiments(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/services", platformtest.Reply{Status: 422, JSON: map[string]any{"violations": []any{map[string]any{"message": "Cannot remove templates without setting `deleteExperiments` to true."}}}}) + file := filepath.Join(t.TempDir(), "service.yml") + require.NoError(t, os.WriteFile(file, []byte("name: Checkout\n"), 0o644)) + + err := service.Apply(ctx, p.Client, service.ApplyOptions{Files: []string{file}}) + + assert.EqualError(t, err, "Service Checkout was not saved: the change would remove provided experiments. Pass --delete-experiments to delete them.") +} + +func TestRiskGate(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/services/"+id+"/risk", platformtest.Reply{JSON: map[string]any{ + "risk": 42, "categoryRisks": map[string]any{"Redundancy": map[string]any{"total": 40, "experiment": 50}}, + "experimentRisks": []any{map[string]any{"experimentKey": "ADM-1", "risk": 60}}, "lastCalculated": "then", + }}) + at := func(n int) *int { return &n } + + out, err := platformtest.Stdout(t, func() error { return service.Risk(ctx, p.Client, service.RiskOptions{ID: id, FailAbove: at(42)}) }) + + require.NoError(t, err) + assert.True(t, strings.HasPrefix(out, "Risk of service "+id+": 42 (calculated then)\n")) + assert.Contains(t, out, "│ Redundancy │ 40 │ 50 │ │") + _, err = platformtest.Stdout(t, func() error { return service.Risk(ctx, p.Client, service.RiskOptions{ID: id, FailAbove: at(41)}) }) + assert.EqualError(t, err, "Risk of service "+id+" is 42, above the accepted 41.") +} + +func TestExperimentsOfAService(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/services/"+id+"/experiments", platformtest.Reply{JSON: map[string]any{"items": []any{ + map[string]any{"templateId": "t-1", "category": "Scalability", "associationType": "PROVIDED"}, + }}}) + p.Reply("POST /api/services/"+id+"/experiments/custom", platformtest.Reply{Status: http.StatusCreated}) + p.Reply("DELETE /api/services/"+id+"/experiments/custom", platformtest.Reply{}) + p.Reply("POST /api/services/"+id+"/experiments/provided", platformtest.Reply{Status: http.StatusCreated, Headers: map[string]string{"Location": p.URL + "/api/experiments/ADM-9"}}) + + out, err := platformtest.Stdout(t, func() error { + if err := service.ListExperiments(ctx, p.Client, service.ExperimentListOptions{ID: id, Types: []string{"provided"}}); err != nil { + return err + } + if err := service.Link(ctx, p.Client, id, "ADM-1", "Redundancy"); err != nil { + return err + } + if err := service.Unlink(ctx, p.Client, id, "ADM-1"); err != nil { + return err + } + placeholders := jsyaml.NewMap() + placeholders.Set("REPLICAS", "3") + return service.Provide(ctx, p.Client, service.ProvideOptions{ID: id, TemplateOptions: experiment.TemplateOptions{ + Template: "d7e65100-1d20-4980-be87-c351704910b8", Placeholder: placeholders, ResetProperties: true}}) + }) + + require.NoError(t, err) + assert.Contains(t, out, "(not created)") + assert.Contains(t, out, "Provided experiment ADM-9 of service "+id+" created from template d7e65100-1d20-4980-be87-c351704910b8.") + assert.Equal(t, []string{"PROVIDED"}, p.Requests("GET /api/services/"+id+"/experiments")[0].Query["type"]) + assert.Equal(t, map[string]any{"experimentKey": "ADM-1", "category": "Redundancy"}, p.Requests("POST /api/services/"+id+"/experiments/custom")[0].JSON(t)) + assert.Equal(t, []string{"ADM-1"}, p.Requests("DELETE /api/services/"+id+"/experiments/custom")[0].Query["experimentKey"]) +} + +func TestVariables(t *testing.T) { + p := platformtest.New(t) + p.Reply("PATCH /api/services/"+id+"/variables", platformtest.Reply{}) + p.Reply("PUT /api/services/"+id+"/variables", platformtest.Reply{}) + file := filepath.Join(t.TempDir(), "vars.yml") + require.NoError(t, os.WriteFile(file, []byte("hosts: [a, b]\nregion: us\n"), 0o644)) + + _, err := platformtest.Stdout(t, func() error { + if err := service.SetVariables(ctx, p.Client, []string{"region=eu", "url=http://x?a=b"}, service.VariableSetOptions{ID: id, File: file}); err != nil { + return err + } + return service.SetVariables(ctx, p.Client, nil, service.VariableSetOptions{ID: id, Replace: true}) + }) + + require.NoError(t, err) + assert.Equal(t, map[string]any{"hosts": []any{"a", "b"}, "region": "eu", "url": "http://x?a=b"}, p.Requests("PATCH /api/services/"+id+"/variables")[0].JSON(t)) + assert.Equal(t, map[string]any{}, p.Requests("PUT /api/services/"+id+"/variables")[0].JSON(t)) + assert.EqualError(t, service.SetVariables(ctx, p.Client, []string{"novalue"}, service.VariableSetOptions{ID: id}), "'novalue' is not in the form KEY=VALUE.") + assert.EqualError(t, service.SetVariables(ctx, p.Client, nil, service.VariableSetOptions{ID: id}), "No variables given. Pass KEY=VALUE arguments or --file.") +} + +func TestAMalformedIdReadsAsNotFound(t *testing.T) { + assert.EqualError(t, service.Delete(ctx, nil, "nope"), "Service nope not found.") +} diff --git a/internal/serviceprofile/serviceprofile_test.go b/internal/serviceprofile/serviceprofile_test.go new file mode 100644 index 0000000..1469569 --- /dev/null +++ b/internal/serviceprofile/serviceprofile_test.go @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package serviceprofile_test + +import ( + "context" + "net/http" + "os" + "path/filepath" + "testing" + + "github.com/steadybit/cli/internal/platformtest" + "github.com/steadybit/cli/internal/serviceprofile" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ctx = context.Background() + +const id = "019eacd7-fb2c-733a-bed5-99a935323db5" + +func TestListFilters(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/services/profiles", platformtest.Reply{JSON: map[string]any{"items": []any{map[string]any{ + "id": id, "name": "High Redundancy", "origin": "CUSTOM", "templates": []any{map[string]any{"templateIds": []any{"a", "b"}}}}}}}) + + out, err := platformtest.Stdout(t, func() error { + return serviceprofile.List(ctx, p.Client, serviceprofile.ListOptions{Name: "Redund", Origins: []string{"custom"}, Default: true}) + }) + + require.NoError(t, err) + assert.Contains(t, out, "│ "+id+" │ High Redundancy │ CUSTOM │ false │ 2 │") + q := p.Requests("GET /api/services/profiles")[0].Query + assert.Equal(t, []string{"Redund"}, q["name"]) + assert.Equal(t, []string{"CUSTOM"}, q["origin"]) + assert.Equal(t, []string{"true"}, q["defaultProfile"]) +} + +func TestGetWritesWhatApplyCanSend(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/services/profiles/"+id, platformtest.Reply{Body: `{"name":"P","origin":"CUSTOM","templates":[],"id":"` + id + `","defaultProfile":false,"version":1,"created":"c","createdBy":"u","edited":"e","editedBy":"u"}`}) + file := filepath.Join(t.TempDir(), "p.yml") + + _, err := platformtest.Stdout(t, func() error { return serviceprofile.Get(ctx, p.Client, serviceprofile.GetOptions{ID: id, File: file}) }) + + require.NoError(t, err) + content, _ := os.ReadFile(file) + assert.Equal(t, "name: P\norigin: CUSTOM\ntemplates: []\nid: "+id+"\n", string(content)) +} + +func TestApplyDefaultsToACustomProfile(t *testing.T) { + p := platformtest.New(t) + p.Reply("POST /api/services/profiles", platformtest.Reply{Status: http.StatusCreated, JSON: map[string]any{"id": id, "name": "P"}}) + file := filepath.Join(t.TempDir(), "p.yml") + require.NoError(t, os.WriteFile(file, []byte("name: P\ntemplates: []\n"), 0o644)) + + _, err := platformtest.Stdout(t, func() error { return serviceprofile.Apply(ctx, p.Client, serviceprofile.ApplyOptions{Files: []string{file}}) }) + + require.NoError(t, err) + assert.Equal(t, map[string]any{"name": "P", "templates": []any{}, "origin": "CUSTOM"}, p.Requests("POST /api/services/profiles")[0].JSON(t)) + content, _ := os.ReadFile(file) + assert.Equal(t, "id: "+id+"\nname: P\ntemplates: []\n", string(content)) +} + +func TestDeleteExplainsProvidedProfiles(t *testing.T) { + p := platformtest.New(t) + p.Reply("DELETE /api/services/profiles/"+id, platformtest.Reply{Status: http.StatusUnprocessableEntity}) + + err := serviceprofile.Delete(ctx, p.Client, id) + + assert.EqualError(t, err, "Service profile "+id+" is provided by Steadybit and cannot be deleted.") +} diff --git a/internal/template/template_test.go b/internal/template/template_test.go new file mode 100644 index 0000000..72c7f37 --- /dev/null +++ b/internal/template/template_test.go @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package template_test + +import ( + "context" + "net/http" + "testing" + + "github.com/steadybit/cli/internal/platformtest" + "github.com/steadybit/cli/internal/template" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const id = "d7e65100-1d20-4980-be87-c351704910b8" + +func TestListSendsTheFilters(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/templates", platformtest.Reply{JSON: map[string]any{"templates": []any{map[string]any{"id": id, "templateTitle": "Shop survives"}}}}) + + out, err := platformtest.Stdout(t, func() error { + return template.List(context.Background(), p.Client, template.ListOptions{Search: []string{"shop"}, Tags: []string{"k8s", "db"}}) + }) + + require.NoError(t, err) + assert.Contains(t, out, "Shop survives") + q := p.Requests("GET /api/experiments/templates")[0].Query + assert.Equal(t, []string{"shop"}, q["freeTextPhrases"]) + assert.Equal(t, []string{"k8s", "db"}, q["tag"]) +} + +func TestGetPlaceholdersAsAFileToFillIn(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/templates/"+id, platformtest.Reply{JSON: map[string]any{"id": id, "placeholders": []any{map[string]any{"key": "CLUSTER"}, map[string]any{"key": "NAMESPACE"}}}}) + + out, err := platformtest.Stdout(t, func() error { + return template.Get(context.Background(), p.Client, template.GetOptions{ID: id, Placeholders: true}) + }) + + require.NoError(t, err) + assert.Equal(t, "CLUSTER: ''\nNAMESPACE: ''\n\n", out) +} + +func TestGetReportsAMissingTemplate(t *testing.T) { + p := platformtest.New(t) + p.Reply("GET /api/experiments/templates/"+id, platformtest.Reply{Status: http.StatusNotFound}) + + assert.EqualError(t, template.Get(context.Background(), p.Client, template.GetOptions{ID: id}), "Experiment template "+id+" not found.") +} diff --git a/internal/tools/headers_test.go b/internal/tools/headers_test.go new file mode 100644 index 0000000..333cf38 --- /dev/null +++ b/internal/tools/headers_test.go @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package tools + +import ( + "os" + "path/filepath" + "regexp" + "strings" + "testing" +) + +var header = regexp.MustCompile(`^(#!.*\n)?// SPDX-License-Identifier: MIT\n// SPDX-FileCopyrightText: \d{4} Steadybit GmbH\n`) + +// Every hand-written source file starts with the SPDX header; generated ones are exempt. +func TestSourceFilesCarryTheSPDXHeader(t *testing.T) { + root := "../.." + _ = filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() && (d.Name() == "node_modules" || d.Name() == "dist" || strings.HasPrefix(d.Name(), ".")) && path != root { + return filepath.SkipDir + } + if d.IsDir() || !(strings.HasSuffix(path, ".go") || strings.HasSuffix(path, ".js") || strings.HasSuffix(path, ".mjs")) || strings.HasSuffix(path, ".gen.go") { + return nil + } + content, err := os.ReadFile(path) + if err != nil { + return err + } + if !header.Match(content) { + t.Errorf("%s does not start with the SPDX header", path) + } + return nil + }) +} From 4b56dceba614eb0a04e0dd06a375f907ea4ec6f1 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 14:03:39 +0200 Subject: [PATCH 5/9] feat!: replace the TypeScript CLI with the Go CLI The CLI is now a single Go binary. Commands, flags, messages, exit codes, profiles, environment variables and file formats are unchanged. - CI tests on Linux, macOS and Windows, checks the generated client against the committed spec, builds against the live spec daily, and runs the unchanged container e2e suite against the Go image. - Releases build the binaries with goreleaser, create the GitHub release, publish npm packages (one per platform, and steadybit, whose launcher starts the right one: npm 12 no longer runs install scripts) and push a multi-arch image of 18 MB instead of 249 MB. - The spec fetcher is now Go and keeps masking the Slack webhook example. - The js-yaml fixture generator stands alone with a pinned js-yaml. --- .dockerignore | 6 +- .github/dependabot.yml | 10 +- .github/workflows/ci.yml | 129 +- .gitignore | 1 + .goreleaser.yaml | 44 + .nvmrc | 1 - .prettierignore | 5 - .prettierrc | 8 - .trivyignore.yml | 14 - CHANGELOG.md | 24 +- CONTRIBUTING.md | 141 +- Dockerfile | 37 +- Dockerfile.spike | 16 - README.md | 18 +- api/generate.go | 7 + api/oapi-codegen.yaml | 2 +- cli | 12 +- e2e/run.sh | 2 +- eslint-rules/spdx-header.mjs | 65 - eslint.config.mjs | 51 - internal/jsyaml/testdata/generate.mjs | 20 +- internal/jsyaml/testdata/package-lock.json | 41 + internal/jsyaml/testdata/package.json | 9 + internal/tools/npmpkg/main.go | 193 + internal/tools/spec/main.go | 84 + npm/steadybit/bin/steadybit.js | 62 + package-lock.json | 3725 ---- package.json | 78 - scripts/api-spec.mjs | 109 - src/advice/api.ts | 48 - src/advice/types.ts | 28 - src/advice/validateStatus.test.ts | 51 - src/advice/validateStatus.ts | 57 - src/api/common.ts | 65 - src/api/error.ts | 33 - src/api/generated/platform-api.ts | 14387 ---------------- src/api/http.test.ts | 206 - src/api/http.ts | 190 - src/api/paging.test.ts | 29 - src/api/paging.ts | 28 - src/api/rateLimit.test.ts | 137 - src/api/rateLimit.ts | 139 - src/api/schemas.ts | 9 - src/cli/help.ts | 11 - src/cli/options.test.ts | 23 - src/cli/options.ts | 25 - src/cli/requirements.ts | 13 - src/cli/steadybit-advice.ts | 24 - src/cli/steadybit-config-profile.ts | 36 - src/cli/steadybit-config.ts | 19 - src/cli/steadybit-execution.ts | 113 - src/cli/steadybit-experiment.ts | 175 - src/cli/steadybit-schedule.ts | 143 - src/cli/steadybit-service-profile.ts | 82 - src/cli/steadybit-service.ts | 212 - src/cli/steadybit-template.ts | 52 - src/cli/steadybit.ts | 54 - src/colors.ts | 12 - src/concurrency.test.ts | 64 - src/concurrency.ts | 23 - src/config/index.ts | 32 - src/config/profile/add.test.ts | 105 - src/config/profile/add.ts | 78 - src/config/profile/list.ts | 24 - src/config/profile/remove.ts | 10 - src/config/profile/select.test.ts | 53 - src/config/profile/select.ts | 27 - src/config/profile/service.test.ts | 101 - src/config/profile/service.ts | 134 - src/config/profile/types.ts | 8 - src/config/requirePlatformAccess.ts | 24 - src/config/show.ts | 11 - src/config/types.ts | 7 - src/errors.test.ts | 28 - src/errors.ts | 53 - src/execution/api.ts | 79 - src/execution/artifacts.ts | 114 - src/execution/cancel.ts | 17 - src/execution/execution.test.ts | 269 - src/execution/get.ts | 19 - src/execution/property.ts | 43 - src/experiment/__snapshots__/get.test.ts.snap | 140 - src/experiment/api.ts | 333 - src/experiment/apply.test.ts | 68 - src/experiment/apply.ts | 52 - src/experiment/delete.test.ts | 21 - src/experiment/delete.ts | 13 - src/experiment/dump.test.ts | 147 - src/experiment/dump.ts | 213 - src/experiment/exec.interactive.test.ts | 72 - src/experiment/exec.test.ts | 133 - src/experiment/exec.ts | 160 - src/experiment/files.test.ts | 35 - src/experiment/files.ts | 77 - src/experiment/get.test.ts | 70 - src/experiment/get.ts | 26 - src/experiment/template.test.ts | 216 - src/experiment/template.ts | 107 - src/experiment/types.ts | 52 - src/mocks/handlers.ts | 382 - src/mocks/prompts.ts | 36 - src/mocks/recorder.ts | 40 - src/mocks/server.ts | 7 - src/mocks/tempFiles.ts | 29 - src/packageJson.ts | 14 - src/prompt/cancellation.test.ts | 49 - src/prompt/cancellation.ts | 19 - src/prompt/confirm.test.ts | 53 - src/prompt/confirm.ts | 23 - src/prompt/validation.ts | 18 - src/schedule/api.ts | 73 - src/schedule/commands.ts | 185 - src/schedule/schedule.test.ts | 201 - src/service/api.ts | 180 - src/service/commands.ts | 289 - src/service/refusal.ts | 20 - src/service/service.test.ts | 245 - src/serviceProfile/api.ts | 81 - src/serviceProfile/commands.ts | 109 - src/serviceProfile/serviceProfile.test.ts | 93 - src/setupTests.ts | 28 - src/structuredFiles.ts | 54 - src/table.test.ts | 18 - src/table.ts | 14 - src/team/get.ts | 17 - src/team/types.ts | 12 - src/template/api.ts | 47 - src/template/commands.ts | 43 - src/template/template.test.ts | 47 - src/yaml.test.ts | 39 - src/yaml.ts | 28 - tsconfig.build.json | 12 - tsconfig.json | 19 - vitest.config.ts | 11 - 134 files changed, 667 insertions(+), 26441 deletions(-) create mode 100644 .goreleaser.yaml delete mode 100644 .nvmrc delete mode 100644 .prettierignore delete mode 100644 .prettierrc delete mode 100644 Dockerfile.spike create mode 100644 api/generate.go delete mode 100644 eslint-rules/spdx-header.mjs delete mode 100644 eslint.config.mjs create mode 100644 internal/jsyaml/testdata/package-lock.json create mode 100644 internal/jsyaml/testdata/package.json create mode 100644 internal/tools/npmpkg/main.go create mode 100644 internal/tools/spec/main.go create mode 100644 npm/steadybit/bin/steadybit.js delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 scripts/api-spec.mjs delete mode 100644 src/advice/api.ts delete mode 100644 src/advice/types.ts delete mode 100644 src/advice/validateStatus.test.ts delete mode 100644 src/advice/validateStatus.ts delete mode 100644 src/api/common.ts delete mode 100644 src/api/error.ts delete mode 100644 src/api/generated/platform-api.ts delete mode 100644 src/api/http.test.ts delete mode 100644 src/api/http.ts delete mode 100644 src/api/paging.test.ts delete mode 100644 src/api/paging.ts delete mode 100644 src/api/rateLimit.test.ts delete mode 100644 src/api/rateLimit.ts delete mode 100644 src/api/schemas.ts delete mode 100644 src/cli/help.ts delete mode 100644 src/cli/options.test.ts delete mode 100644 src/cli/options.ts delete mode 100644 src/cli/requirements.ts delete mode 100644 src/cli/steadybit-advice.ts delete mode 100644 src/cli/steadybit-config-profile.ts delete mode 100644 src/cli/steadybit-config.ts delete mode 100644 src/cli/steadybit-execution.ts delete mode 100644 src/cli/steadybit-experiment.ts delete mode 100644 src/cli/steadybit-schedule.ts delete mode 100644 src/cli/steadybit-service-profile.ts delete mode 100644 src/cli/steadybit-service.ts delete mode 100644 src/cli/steadybit-template.ts delete mode 100644 src/cli/steadybit.ts delete mode 100644 src/colors.ts delete mode 100644 src/concurrency.test.ts delete mode 100644 src/concurrency.ts delete mode 100644 src/config/index.ts delete mode 100644 src/config/profile/add.test.ts delete mode 100644 src/config/profile/add.ts delete mode 100644 src/config/profile/list.ts delete mode 100644 src/config/profile/remove.ts delete mode 100644 src/config/profile/select.test.ts delete mode 100644 src/config/profile/select.ts delete mode 100644 src/config/profile/service.test.ts delete mode 100644 src/config/profile/service.ts delete mode 100644 src/config/profile/types.ts delete mode 100644 src/config/requirePlatformAccess.ts delete mode 100644 src/config/show.ts delete mode 100644 src/config/types.ts delete mode 100644 src/errors.test.ts delete mode 100644 src/errors.ts delete mode 100644 src/execution/api.ts delete mode 100644 src/execution/artifacts.ts delete mode 100644 src/execution/cancel.ts delete mode 100644 src/execution/execution.test.ts delete mode 100644 src/execution/get.ts delete mode 100644 src/execution/property.ts delete mode 100644 src/experiment/__snapshots__/get.test.ts.snap delete mode 100644 src/experiment/api.ts delete mode 100644 src/experiment/apply.test.ts delete mode 100644 src/experiment/apply.ts delete mode 100644 src/experiment/delete.test.ts delete mode 100644 src/experiment/delete.ts delete mode 100644 src/experiment/dump.test.ts delete mode 100644 src/experiment/dump.ts delete mode 100644 src/experiment/exec.interactive.test.ts delete mode 100644 src/experiment/exec.test.ts delete mode 100644 src/experiment/exec.ts delete mode 100644 src/experiment/files.test.ts delete mode 100644 src/experiment/files.ts delete mode 100644 src/experiment/get.test.ts delete mode 100644 src/experiment/get.ts delete mode 100644 src/experiment/template.test.ts delete mode 100644 src/experiment/template.ts delete mode 100644 src/experiment/types.ts delete mode 100644 src/mocks/handlers.ts delete mode 100644 src/mocks/prompts.ts delete mode 100644 src/mocks/recorder.ts delete mode 100644 src/mocks/server.ts delete mode 100644 src/mocks/tempFiles.ts delete mode 100644 src/packageJson.ts delete mode 100644 src/prompt/cancellation.test.ts delete mode 100644 src/prompt/cancellation.ts delete mode 100644 src/prompt/confirm.test.ts delete mode 100644 src/prompt/confirm.ts delete mode 100644 src/prompt/validation.ts delete mode 100644 src/schedule/api.ts delete mode 100644 src/schedule/commands.ts delete mode 100644 src/schedule/schedule.test.ts delete mode 100644 src/service/api.ts delete mode 100644 src/service/commands.ts delete mode 100644 src/service/refusal.ts delete mode 100644 src/service/service.test.ts delete mode 100644 src/serviceProfile/api.ts delete mode 100644 src/serviceProfile/commands.ts delete mode 100644 src/serviceProfile/serviceProfile.test.ts delete mode 100644 src/setupTests.ts delete mode 100644 src/structuredFiles.ts delete mode 100644 src/table.test.ts delete mode 100644 src/table.ts delete mode 100644 src/team/get.ts delete mode 100644 src/team/types.ts delete mode 100644 src/template/api.ts delete mode 100644 src/template/commands.ts delete mode 100644 src/template/template.test.ts delete mode 100644 src/yaml.test.ts delete mode 100644 src/yaml.ts delete mode 100644 tsconfig.build.json delete mode 100644 tsconfig.json delete mode 100644 vitest.config.ts diff --git a/.dockerignore b/.dockerignore index 164e837..21420b1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,6 @@ -dist -node_modules +.git .github .idea +node_modules +dist +npm/dist diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8208798..f7a881b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,18 +1,12 @@ version: 2 updates: - - package-ecosystem: npm + - package-ecosystem: gomod directory: / schedule: interval: weekly open-pull-requests-limit: 5 groups: - dev-dependencies: - dependency-type: development - update-types: - - minor - - patch - production-dependencies: - dependency-type: production + go-dependencies: update-types: - minor - patch diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a94ec19..2a7f3b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,81 +15,59 @@ on: jobs: verify: - runs-on: ubuntu-latest + # Paths, terminals and line endings differ between them, and users run all three. strategy: matrix: - node: [22, 24, 26] + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v7 - - uses: actions/setup-node@v7 + - uses: actions/setup-go@v7 with: - node-version: ${{ matrix.node }} - cache: 'npm' - - run: npm install -g npm@12 - - run: npm ci - - run: npm run ci + go-version-file: go.mod + - name: Formatting + if: runner.os != 'Windows' + run: test -z "$(gofmt -l api cmd internal)" || { gofmt -l api cmd internal; exit 1; } + - name: The generated client matches the committed spec + if: runner.os != 'Windows' + run: | + go generate ./api + git diff --exit-code -- api/platform.gen.go + - run: go vet ./... + - run: go test ./... # The committed spec is what the CLI was built against; this checks it against the live - # platform. Breaking changes that touch a request or response the CLI uses surface as - # type errors and fail the job, which also blocks a release. Changes the CLI does not - # depend on only produce a warning to refresh the committed spec. + # platform. A breaking change to an endpoint the CLI uses fails to compile and fails the + # job, which also blocks a release. Changes the CLI does not depend on only warn. api-compatibility: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - uses: actions/setup-node@v7 + - uses: actions/setup-go@v7 with: - node-version: 24 - cache: 'npm' - - run: npm install -g npm@12 - - run: npm ci - - run: npm run api:fetch-spec - - run: npm run api:generate - - name: Type check against the live platform API - run: npm run verify:typecheck - - name: Report spec drift + go-version-file: go.mod + - run: go run ./internal/tools/spec fetch + - run: go generate ./api + - name: Build against the live platform API + run: go build ./... && go vet ./... + - name: Report API changes if: always() run: | - if git diff --quiet -- openapi src/api/generated; then - echo "The committed platform spec matches the live platform." >> "$GITHUB_STEP_SUMMARY" + # The raw spec is not compared: the platform orders some of its maps differently + # from one response to the next. The generated client is deterministic. + if git diff --quiet -- api/platform.gen.go; then + echo "The live platform API matches the committed client." >> "$GITHUB_STEP_SUMMARY" else - echo "::warning::The platform API changed. Run \`npm run api:update\` and commit the result." + echo "::warning::The platform API changed. Run \`go run ./internal/tools/spec fetch && go generate ./api\` and commit the result." { echo "### The platform API changed" echo - echo "Run \`npm run api:update\` and commit the result." - echo echo '```' - git diff --stat -- openapi src/api/generated + git diff --stat -- api/platform.gen.go echo '```' } >> "$GITHUB_STEP_SUMMARY" fi - release: - if: startsWith(github.ref, 'refs/tags/v') - needs: [verify, api-compatibility] - runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - steps: - - uses: actions/checkout@v7 - - uses: actions/setup-node@v7 - with: - node-version: 24 - cache: 'npm' - registry-url: 'https://registry.npmjs.org' - - run: npm install -g npm@12 - - run: npm ci - - name: 'snyk monitor' - uses: snyk/actions/node@master - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - args: --prune-repeated-subdependencies - command: monitor - - run: npm publish --provenance --access public - docker-build: if: github.event_name != 'schedule' runs-on: ubuntu-latest @@ -106,12 +84,15 @@ jobs: VERSION="${GITHUB_REF#refs/tags/v}" MAJOR="${VERSION%%.*}" echo "tags=steadybit/cli:latest,steadybit/cli:${MAJOR},steadybit/cli:${VERSION}" >> "$GITHUB_OUTPUT" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "push=true" >> "$GITHUB_OUTPUT" elif [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then echo "tags=steadybit/cli:main" >> "$GITHUB_OUTPUT" + echo "version=main-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" echo "push=true" >> "$GITHUB_OUTPUT" else echo "tags=" >> "$GITHUB_OUTPUT" + echo "version=pr-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" echo "push=false" >> "$GITHUB_OUTPUT" fi - id: build @@ -121,6 +102,7 @@ jobs: with: context: ./ load: true + build-args: VERSION=${{ steps.tags.outputs.version }} - name: Test container run: docker run --rm ${{ steps.build.outputs.imageid }} -V - name: Run container smoke tests @@ -141,6 +123,7 @@ jobs: push: true platforms: linux/amd64,linux/arm64 tags: ${{ steps.tags.outputs.tags }} + build-args: VERSION=${{ steps.tags.outputs.version }} - name: 'snyk monitor docker image' if: startsWith(github.ref, 'refs/tags/v') uses: snyk/actions/docker@master @@ -150,3 +133,45 @@ jobs: command: monitor env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + + release: + if: startsWith(github.ref, 'refs/tags/v') + needs: [verify, api-compatibility, docker-build] + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + - uses: actions/setup-node@v7 + with: + node-version: 24 + registry-url: 'https://registry.npmjs.org' + - run: npm install -g npm@12 + - name: Binaries, archives and the GitHub release + uses: goreleaser/goreleaser-action@v7 + with: + version: '~> v2' + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: npm packages from the same binaries + run: go run ./internal/tools/npmpkg -version "${GITHUB_REF_NAME}" -dist dist -out npm/dist + - name: Publish to npm + # The platform packages first: `steadybit` depends on them. + run: | + for dir in npm/dist/cli-*; do + (cd "$dir" && npm publish --provenance --access public) + done + (cd npm/dist/steadybit && npm publish --provenance --access public) + - name: 'snyk monitor' + uses: snyk/actions/golang@master + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + command: monitor diff --git a/.gitignore b/.gitignore index 2dfc1e8..d7f467d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ build/Release # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- node_modules /dist +/npm/dist /.steadybit.yml .idea diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..337307d --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2026 Steadybit GmbH + +# Builds the release binaries, archives and checksums. The npm packages and the container +# image are made from these binaries by the release workflow, so every channel ships the +# same build. +version: 2 +project_name: steadybit + +builds: + - id: steadybit + main: ./cmd/steadybit + binary: steadybit + env: + - CGO_ENABLED=0 + goos: [linux, darwin, windows] + goarch: [amd64, arm64] + flags: [-trimpath] + ldflags: + - -s -w -X github.com/steadybit/cli/internal/platform.Version={{ .Version }} + mod_timestamp: '{{ .CommitTimestamp }}' + +archives: + - id: steadybit + # Without the version, so that releases/latest/download/ is a stable URL. + name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}' + formats: [tar.gz] + format_overrides: + - goos: windows + formats: [zip] + files: [LICENSE, README.md, CHANGELOG.md] + +checksum: + name_template: checksums.txt + +snapshot: + version_template: '{{ incpatch .Version }}-next' + +release: + # The changelog is written by hand in CHANGELOG.md. + mode: keep-existing + +changelog: + disable: true diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 54c6511..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v24 diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index e101296..0000000 --- a/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -dist -coverage -package-lock.json -openapi -src/api/generated diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index c7d04dc..0000000 --- a/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "trailingComma": "es5", - "tabWidth": 2, - "semi": true, - "singleQuote": true, - "arrowParens": "avoid", - "printWidth": 120 -} diff --git a/.trivyignore.yml b/.trivyignore.yml index 0672972..88d95ca 100644 --- a/.trivyignore.yml +++ b/.trivyignore.yml @@ -1,18 +1,4 @@ vulnerabilities: - - id: CVE-2026-14257 - statement: Waiting for an updated npm version - expired_at: 2027-01-01 - - id: CVE-2026-69192 - statement: Waiting for an updated npm version - expired_at: 2027-01-01 - - id: CVE-2026-69152 - statement: Waiting for an updated npm version - expired_at: 2027-01-01 - - id: CVE-2026-73566 - paths: - - usr/local/lib/node_modules/npm/node_modules/tar/package.json - statement: waiting on npm upstream fix - expired_at: 2027-02-01 - id: CVE-2026-14456 statement: Waiting for an upstream debian fix expired_at: 2027-01-01 diff --git a/CHANGELOG.md b/CHANGELOG.md index a12f76a..d89a48b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## v5.0.0 +- **The CLI is now a single binary written in Go.** It runs without Node.js, and is installed + the same ways: `npm install -g steadybit`, which now installs the binary for your platform + and works with any Node.js from 18 on, or the `steadybit/cli` container image, now 18 MB + instead of 249 MB. It can also be downloaded directly from the GitHub releases. Commands, flags, messages, exit + codes, profiles in `~/.steadybit` and the `STEADYBIT_*` variables are unchanged, and + experiment, schedule and service files are written byte for byte as before. +- Writing a new experiment's key back into a YAML file no longer rewrites the file: the key + is added at the top and comments, anchors and formatting are kept. +- Shell completion: `steadybit completion bash|zsh|fish|powershell`. - `experiment apply --template ` creates an experiment from an experiment template, or updates the one created before with the same `--external-id`. With `-k` it re-renders an existing experiment with new placeholder values. Placeholders are given with @@ -25,14 +34,11 @@ - Every command now shows examples in its `--help`. - Fixed the table printed by `advice validate-status` containing colour escape codes when piped. Tables now follow the same terminal check as the rest of the output. -- The CLI's API types are generated from the platform's OpenAPI spec. CI checks them - against the live platform daily and before every release, so a breaking API change is - caught before it reaches a pipeline. -- **Breaking:** Node.js 22.13.0 or later is now required. Node.js 18 and 20 have reached - end of life and the CLI's dependencies no longer support them. -- The CLI is now published as an ES module. -- **Security:** `-v, --verbose` no longer prints the API access token. Commander passes the - flag on to spawned subcommands, so CI jobs using it had the token in their logs. +- The CLI's API client is generated from the platform's OpenAPI spec. CI builds it against + the live platform daily and before every release, so a breaking API change is caught + before it reaches a pipeline. +- **Security:** `-v, --verbose` no longer prints the API access token, which CI jobs using + it had in their logs. - **Security:** requests are only ever sent to the configured platform. An absolute URL in a platform response, such as the `Location` header of a started run, now has its origin replaced by the configured one so that the access token cannot be sent elsewhere. @@ -70,8 +76,6 @@ `STEADYBIT_RATE_LIMIT_INTERVAL` override the assumed rate limit for deployments configured differently. A value that is not a positive whole number is reported and ignored rather than silently changing how hard the CLI polls. -- Replaced `inquirer` with the `@inquirer/*` prompt packages and `colors` with `picocolors`. -- Replaced `node-fetch` with the Node.js built-in `fetch`. - Dependency updates ## v4.3.2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 421a3ed..fe2966a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,116 +2,113 @@ ## Working Locally -### Initial Setup - ```sh git clone git@github.com:steadybit/cli.git -nvm use # Node.js 22.13 or later, per .nvmrc -npm ci +cd cli +go test ./... ``` -Run `npm run ci` before pushing. It type-checks, tests, lints and builds, and is the -same script CI runs. +Go 1.26 or later, as `go.mod` states. `./cli` runs the CLI from the working tree: -## Tests +```sh +export STEADYBIT_TOKEN="..." +export STEADYBIT_URL="http://localhost:8080" +./cli experiment get -k ADM-1 +``` + +Before pushing, run what CI runs: + +```sh +gofmt -l api cmd internal # prints nothing +go vet ./... +go test ./... +``` + +## Layout -Tests sit at three levels. Put a test at the lowest one that can hold it — the -levels get slower and harder to debug as you go down this list. +| Path | Holds | +| ------------------------ | ---------------------------------------------------------------------- | +| `cmd/steadybit` | The entry point | +| `internal/cli` | The commands and their flags, wired with cobra | +| `internal/` | What each command group does: `experiment`, `schedule`, `service`, ... | +| `internal/platform` | The HTTP client: authentication, retries, rate limiting, errors | +| `internal/jsyaml` | YAML and JSON output byte-compatible with the former TypeScript CLI | +| `api` | The platform client, generated from `openapi/platform-api.json` | +| `internal/tools` | The spec fetcher and the npm package builder, used by CI | +| `npm/steadybit` | The launcher the `steadybit` npm package runs | -| Level | Tool | Covers | -| --------- | ---------------------------------- | ------------------------------------------------------ | -| Unit | vitest | A single function or class, no I/O | -| Command | vitest + msw + `@inquirer/testing` | A command end to end in process, including its prompts | -| Container | `e2e/run.sh` + expect | Only what needs a real process | +## Tests + +Tests sit at three levels. Put a test at the lowest one that can hold it; the levels get +slower and harder to debug as you go down this list. -Prompts are driven through `@inquirer/testing`. Mock the prompt package with -`wrapPrompt` so the application's own call is intercepted, and use the helpers in -`src/mocks/prompts.ts` rather than writing to the screen directly: +| Level | Tool | Covers | +| --------- | ---------------------------------- | ------------------------------------------------------- | +| Unit | `go test` | A single function, no I/O | +| Command | `go test` + `internal/platformtest` | A command end to end against a fake platform | +| Container | `e2e/run.sh` + expect | Only what needs a real process | -```ts -vi.mock('@inquirer/input', async importOriginal => { - const actual = await importOriginal(); - return { ...actual, default: wrapPrompt(actual.default) }; -}); +`internal/platformtest` starts an `httptest` server whose endpoints a test declares, +records every request, and points the configuration at it: -await answerPrompt('Profile name:', 'my-profile'); +```go +p := platformtest.New(t) +p.Reply("GET /api/experiments/TST-1", platformtest.Reply{Body: design}) +out, err := platformtest.Stdout(t, func() error { return experiment.Get(ctx, p.Client, opts) }) ``` -The container tests are deliberately thin. They exist for the four things no in-process -test can reach — real exit codes, a real terminal, the spawn of a subcommand, and -the packaged artifact — and they assert exit status and a line of output, never -content. Anything checking structure belongs at the command level. +The container tests are deliberately thin. They exist for what no in-process test can +reach: real exit codes, a real terminal, and the packaged artifact. They assert exit status +and a line of output, never content. ```sh docker build -t steadybit/cli:under-test . docker run --rm -v "$PWD/e2e:/e2e" --entrypoint sh steadybit/cli:under-test /e2e/run.sh ``` -The scripts are mounted into the image rather than baked into a derived one, and CI runs -the image by id rather than by name. Both avoid the same mistake: a name is resolved -against a registry when it cannot be found locally, so the suite can end up exercising -the last release while reporting success. - -## Platform API Types +### Output compatibility -Request and response types come from the platform's OpenAPI spec, committed as -`openapi/platform-api.json` and generated into `src/api/generated/`. Import them through -`Schemas` from `src/api/schemas.ts` rather than writing the shapes by hand. +Users keep the files `get` writes in Git, so the YAML and JSON the CLI writes must stay +byte for byte what the TypeScript CLI wrote with js-yaml and `JSON.stringify`. +`internal/jsyaml/testdata/cases.json` holds values and what js-yaml rendered for them, +and the tests compare against it. To add a case, add it to `generate.mjs` and regenerate: ```sh -npm run api:update # fetch the live spec, regenerate the types, type-check +cd internal/jsyaml/testdata && npm ci && node generate.mjs ``` -Commit the spec and the generated file together; `npm run verify` fails when they -disagree. CI also type-checks against the live spec daily and before each release, so a -breaking change in the platform surfaces as a type error here. When the API is -versioned, use the latest version only. +## Platform API Client -### Local CLI Execution +The client in `api/platform.gen.go` is generated from the platform's OpenAPI spec, +committed as `openapi/platform-api.json`. Refresh both together: ```sh -# Define environment variables -export STEADYBIT_TOKEN="..." -export STEADYBIT_URL="http://localhost:8080" - -# Build the CLI locally -npm run build - -# Run some CLI commands -./cli experiment get -k ADM-1 +go run ./internal/tools/spec fetch # the live spec into openapi/platform-api.json +go generate ./api # the client from it +go build ./... # a breaking change fails here ``` -### Local CLI installation - -```sh -# Build the CLI locally -npm run build -# Package the CLI locally -npm pack -# Install the local package -npm i -g steadybit-*.tgz -``` +CI fails when the generated client does not match the committed spec, and builds against +the live spec daily and before each release. When the API is versioned, use the latest +version only. Experiment designs and other files users keep pass through as documents, +not generated structs, so that fields the spec does not know yet are never dropped. ## Releasing -Releases are published by CI, not from a workstation: pushing a `v*` tag triggers -[the release workflow](.github/workflows/release.yml), which publishes to npm via -trusted publishing and pushes the Docker image. Never run `npm publish` locally. +Releases are published by CI, not from a workstation: pushing a `v*` tag builds the +binaries with goreleaser, creates the GitHub release, publishes the npm packages (one per +platform, and `steadybit`, which installs the right one), and pushes the Docker image. ```sh # 1. Head the CHANGELOG.md entry with the version being released git commit -am 'chore: prepare release' -# 2. Bump package.json and create the matching v tag -npm run ci -npm version {major|minor|patch} - -# 3. Push the commit together with the tag -git push --follow-tags origin main +# 2. Tag and push +git tag v5.0.0 +git push origin main v5.0.0 ``` -Use `major` for breaking changes, which includes raising the Node.js floor, since -that breaks installs for users on older runtimes. +Use a major version for breaking changes to commands, flags, output or exit codes. ## Contributor License Agreement (CLA) diff --git a/Dockerfile b/Dockerfile index 0a9a348..85311f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,25 @@ -FROM node:26-alpine AS builder +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2026 Steadybit GmbH -COPY ./ ./build -WORKDIR ./build -RUN npm ci && \ - npm run build && \ - npm pack - -FROM node:26-alpine - -COPY --from=builder ./build/steadybit-*.tgz steadybit.tgz - -RUN apk update && apk upgrade --no-cache && rm -rf /var/cache/apk/* && \ - npm -g install npm@12.1.0 && \ - npm -g install ./steadybit.tgz && \ - rm ./steadybit.tgz +# Built natively for the build platform and cross-compiled, so a multi-arch build does +# not run the Go toolchain under emulation. +FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder +ARG TARGETOS +ARG TARGETARCH +ARG VERSION=dev +WORKDIR /build +COPY go.mod go.sum ./ +RUN go mod download +COPY api ./api +COPY cmd ./cmd +COPY internal ./internal +RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -trimpath \ + -ldflags "-s -w -X github.com/steadybit/cli/internal/platform.Version=${VERSION}" \ + -o /steadybit ./cmd/steadybit +# Alpine rather than scratch: pipelines use the image with a shell, and the e2e suite +# installs expect into it. +FROM alpine:3 +RUN apk upgrade --no-cache +COPY --from=builder /steadybit /usr/local/bin/steadybit ENTRYPOINT ["steadybit"] diff --git a/Dockerfile.spike b/Dockerfile.spike deleted file mode 100644 index 0e1a9ad..0000000 --- a/Dockerfile.spike +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: MIT -# SPDX-FileCopyrightText: 2026 Steadybit GmbH - -# Spike: the Go CLI in the same shape as the published image, so e2e/run.sh runs unchanged. -FROM golang:1.26-alpine AS builder -WORKDIR /build -COPY go.mod go.sum ./ -RUN go mod download -COPY api ./api -COPY cmd ./cmd -COPY internal ./internal -RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X github.com/steadybit/cli/internal/platform.Version=spike" -o /steadybit ./cmd/steadybit - -FROM alpine:3 -COPY --from=builder /steadybit /usr/local/bin/steadybit -ENTRYPOINT ["steadybit"] diff --git a/README.md b/README.md index d136aef..dc06235 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,29 @@ You can retrieve, create or adjust experiment designs as well as running them st ## Prerequisites -- You need to have a Steadybit account. You can create a free account [via our website](https://www.steadybit.com/get-started/). -- at least Node.js 22.13 as local runtime +You need a Steadybit account. You can create a free account [via our website](https://www.steadybit.com/get-started/). ## Installation -Via npm +The CLI is a single binary for Linux, macOS and Windows, on amd64 and arm64. + +Via npm, which installs the binary for your platform (any Node.js from 18 on): ```sh npm install -g steadybit ``` +Or download the archive for your platform from the +[releases](https://github.com/steadybit/cli/releases) (`checksums.txt` lists their SHA-256) +and put `steadybit` on your `PATH`: + +```sh +curl -sL https://github.com/steadybit/cli/releases/latest/download/steadybit_linux_amd64.tar.gz | tar -xz steadybit +sudo mv steadybit /usr/local/bin/ +``` + +Shell completion is available for bash, zsh, fish and PowerShell, see `steadybit completion --help`. + ## Authorization You need an API access token. You can grab one via our [platform](https://platform.steadybit.com/settings/api-tokens) through the `Settings -> API Access Tokens` page. diff --git a/api/generate.go b/api/generate.go new file mode 100644 index 0000000..134d95c --- /dev/null +++ b/api/generate.go @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Package api is the platform client, generated from the committed OpenAPI spec. +package api + +//go:generate go tool oapi-codegen --config oapi-codegen.yaml ../openapi/platform-api.json diff --git a/api/oapi-codegen.yaml b/api/oapi-codegen.yaml index d5efe2b..eac7b79 100644 --- a/api/oapi-codegen.yaml +++ b/api/oapi-codegen.yaml @@ -1,5 +1,5 @@ package: api -output: api/platform.gen.go +output: platform.gen.go generate: client: true models: true diff --git a/cli b/cli index c99d62a..d7fdea3 100755 --- a/cli +++ b/cli @@ -1,10 +1,6 @@ #!/usr/bin/env sh +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2026 Steadybit GmbH -set -eou pipefail - -# This small utility can be used to execute the locally build CLI without -# having to constantly reconstruct the ./dist/cli/steadybit.js path. -# -# In production this file is not used. - -./dist/cli/steadybit.js "$@" +# Runs the CLI from the working tree, for development. Releases ship a built binary. +exec go run ./cmd/steadybit "$@" diff --git a/e2e/run.sh b/e2e/run.sh index ef899f0..e2b8167 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -4,7 +4,7 @@ # Smoke tests for the packaged CLI. Everything here needs a real process: an exit # status, a terminal, or the spawn of a subcommand. Anything that can be asserted -# in-process belongs in the vitest suite instead, so these stay at the level of +# in-process belongs in the Go test suite instead, so these stay at the level of # "did it exit correctly" rather than checking output in detail. set -u diff --git a/eslint-rules/spdx-header.mjs b/eslint-rules/spdx-header.mjs deleted file mode 100644 index 242ffa9..0000000 --- a/eslint-rules/spdx-header.mjs +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -const LICENSE_LINE = ' SPDX-License-Identifier: MIT'; -const COPYRIGHT_PATTERN = /^ SPDX-FileCopyrightText: \d{4} Steadybit GmbH$/; - -// A local rule rather than a plugin: the repo dropped eslint-plugin-header when it moved -// to flat config, and enforcing two fixed comment lines does not warrant a dependency. -export const spdxHeader = { - meta: { - type: 'layout', - fixable: 'code', - schema: [], - messages: { - missing: 'Missing the SPDX copyright header.', - malformed: - 'The SPDX copyright header must be "// SPDX-License-Identifier: MIT" and ' + - '"// SPDX-FileCopyrightText: Steadybit GmbH" on the two topmost lines.', - }, - }, - create(context) { - const sourceCode = context.sourceCode; - - return { - Program(node) { - const comments = sourceCode.getAllComments(); - // Both spellings are needed: espree reports the `#!` line of a .mjs file as - // "Hashbang", while typescript-eslint reports it as "Shebang" in the .ts CLI - // entry points. - const hashbang = comments.find(comment => comment.type === 'Hashbang' || comment.type === 'Shebang'); - const header = comments.filter(comment => comment !== hashbang).slice(0, 2); - const startsAfter = hashbang ? hashbang.range[1] : 0; - - const isPresent = - header.length === 2 && - header.every(comment => comment.type === 'Line') && - header[0].value === LICENSE_LINE && - COPYRIGHT_PATTERN.test(header[1].value) && - header[0].loc.start.line + 1 === header[1].loc.start.line && - sourceCode.text.slice(startsAfter, header[0].range[0]).trim() === ''; - - if (isPresent) { - return; - } - - // Only an outright absent header is auto-fixed. Rewriting a header that is - // present but wrong risks stacking a second one on top of the first. - const hasSomeHeader = header.some(comment => comment.value.includes('SPDX-')); - - context.report({ - node, - messageId: hasSomeHeader ? 'malformed' : 'missing', - fix: hasSomeHeader - ? undefined - : fixer => { - const text = `//${LICENSE_LINE}\n// SPDX-FileCopyrightText: ${new Date().getFullYear()} Steadybit GmbH\n`; - return hashbang - ? fixer.insertTextAfterRange(hashbang.range, `\n${text}`) - : fixer.insertTextBeforeRange([0, 0], `${text}\n`); - }, - }); - }, - }; - }, -}; diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index 6b712d7..0000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -// @ts-check - -import eslint from '@eslint/js'; -import tseslint from 'typescript-eslint'; -import globals from 'globals'; -import { spdxHeader } from './eslint-rules/spdx-header.mjs'; - -export default tseslint.config( - // A config object containing only `ignores` acts as a global ignore. Combining it - // with `files` would scope the ignores to that single object instead. - { - ignores: ['**/node_modules/**', '**/dist/**', '**/coverage/**', 'src/api/generated/**'], - }, - { - files: ['**/*.js', '**/*.mjs', '**/*.ts'], - extends: [eslint.configs.recommended, tseslint.configs.recommended], - languageOptions: { - globals: { - ...globals.node, - }, - }, - plugins: { - steadybit: { - rules: { 'spdx-header': spdxHeader }, - }, - }, - rules: { - '@typescript-eslint/no-explicit-any': 0, - 'steadybit/spdx-header': 'error', - // Both wrappers exist to correct a dependency default, and reaching past them - // fails silently: raw js-yaml drops merge keys and timestamps from experiment - // files, raw picocolors puts ANSI escapes into piped output. - 'no-restricted-imports': [ - 'error', - { - paths: [ - { name: 'js-yaml', message: "Use '../yaml.ts', which restores the tag set experiment files rely on." }, - { name: 'picocolors', message: "Use '../colors.ts', which only colours a real terminal." }, - ], - }, - ], - }, - }, - { - files: ['src/yaml.ts', 'src/colors.ts'], - rules: { 'no-restricted-imports': 0 }, - } -); diff --git a/internal/jsyaml/testdata/generate.mjs b/internal/jsyaml/testdata/generate.mjs index 35db336..791615d 100644 --- a/internal/jsyaml/testdata/generate.mjs +++ b/internal/jsyaml/testdata/generate.mjs @@ -2,10 +2,24 @@ // SPDX-FileCopyrightText: 2026 Steadybit GmbH // Regenerates cases.json: values and what the TypeScript CLI rendered for them with -// js-yaml and JSON.stringify. Run from the repository root after `npm run build`: -// node internal/jsyaml/testdata/generate.mjs +// js-yaml and JSON.stringify, which the Go CLI's output is held to. +// cd internal/jsyaml/testdata && npm ci && node generate.mjs import fs from 'node:fs'; -const { dump } = await import(new URL('../../../dist/yaml.js', import.meta.url)); +import { + CORE_SCHEMA, + binaryTag, + dump as dumpYaml, + mergeTag, + omapTag, + pairsTag, + setTag, + timestampTag, +} from 'js-yaml'; + +// The schema the TypeScript CLI dumped with: the YAML core schema plus the tags js-yaml 4 +// enabled by default. +const schema = CORE_SCHEMA.withTags(mergeTag, timestampTag, binaryTag, omapTag, pairsTag, setTag); +const dump = value => dumpYaml(value, { schema }); const long = 'When a single container from steadybit-demo/toys-bestseller fails then within 2m all pods are ready.'; const strings = [ diff --git a/internal/jsyaml/testdata/package-lock.json b/internal/jsyaml/testdata/package-lock.json new file mode 100644 index 0000000..8170750 --- /dev/null +++ b/internal/jsyaml/testdata/package-lock.json @@ -0,0 +1,41 @@ +{ + "name": "jsyaml-fixtures", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "jsyaml-fixtures", + "dependencies": { + "js-yaml": "5.4.1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/js-yaml": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", + "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + } + } +} diff --git a/internal/jsyaml/testdata/package.json b/internal/jsyaml/testdata/package.json new file mode 100644 index 0000000..01e3925 --- /dev/null +++ b/internal/jsyaml/testdata/package.json @@ -0,0 +1,9 @@ +{ + "name": "jsyaml-fixtures", + "private": true, + "type": "module", + "description": "Generates the js-yaml fixtures the Go CLI's YAML output is compared against.", + "dependencies": { + "js-yaml": "5.4.1" + } +} diff --git a/internal/tools/npmpkg/main.go b/internal/tools/npmpkg/main.go new file mode 100644 index 0000000..be8f2d2 --- /dev/null +++ b/internal/tools/npmpkg/main.go @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Command npmpkg turns goreleaser's binaries into the npm packages: one per platform +// holding the binary, and `steadybit`, which depends on all of them optionally and starts +// the one npm installed. npm 12 no longer runs install scripts by default, so downloading +// the binary on install is not an option. +// +// go run ./internal/tools/npmpkg -version 6.0.0 -dist dist -out npm/dist +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +type platform struct { + goos, goarch, nodeOS, nodeCPU string +} + +var platforms = []platform{ + {"darwin", "arm64", "darwin", "arm64"}, + {"darwin", "amd64", "darwin", "x64"}, + {"linux", "arm64", "linux", "arm64"}, + {"linux", "amd64", "linux", "x64"}, + {"windows", "arm64", "win32", "arm64"}, + {"windows", "amd64", "win32", "x64"}, +} + +type artifact struct { + Type string `json:"type"` + Path string `json:"path"` + Goos string `json:"goos"` + Goarch string `json:"goarch"` +} + +func main() { + version := flag.String("version", "", "the version to publish, without a leading v") + dist := flag.String("dist", "dist", "goreleaser's output directory") + out := flag.String("out", "npm/dist", "where to write the packages") + flag.Parse() + if err := run(strings.TrimPrefix(*version, "v"), *dist, *out); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run(version, dist, out string) error { + if version == "" { + return fmt.Errorf("-version is required") + } + content, err := os.ReadFile(filepath.Join(dist, "artifacts.json")) + if err != nil { + return err + } + var artifacts []artifact + if err := json.Unmarshal(content, &artifacts); err != nil { + return err + } + binaries := map[string]string{} + for _, a := range artifacts { + if a.Type == "Binary" { + binaries[a.Goos+"/"+a.Goarch] = a.Path + } + } + + if err := os.RemoveAll(out); err != nil { + return err + } + optional := map[string]string{} + for _, p := range platforms { + binary, ok := binaries[p.goos+"/"+p.goarch] + if !ok { + return fmt.Errorf("no %s/%s binary in %s", p.goos, p.goarch, dist) + } + name := fmt.Sprintf("@steadybit/cli-%s-%s", p.nodeOS, p.nodeCPU) + dir := filepath.Join(out, fmt.Sprintf("cli-%s-%s", p.nodeOS, p.nodeCPU)) + executable := "steadybit" + if p.goos == "windows" { + executable += ".exe" + } + if err := copyFile(binary, filepath.Join(dir, "bin", executable), 0o755); err != nil { + return err + } + if err := writeJSON(filepath.Join(dir, "package.json"), map[string]any{ + "name": name, + "version": version, + "description": fmt.Sprintf("The Steadybit CLI binary for %s %s. Install `steadybit` instead.", p.nodeOS, p.nodeCPU), + "license": "MIT", + "repository": map[string]string{"type": "git", "url": "https://github.com/steadybit/cli.git"}, + "os": []string{p.nodeOS}, + "cpu": []string{p.nodeCPU}, + "files": []string{"bin"}, + "preferUnplugged": true, + }); err != nil { + return err + } + optional[name] = version + } + + main := filepath.Join(out, "steadybit") + if err := copyFile("npm/steadybit/bin/steadybit.js", filepath.Join(main, "bin", "steadybit.js"), 0o755); err != nil { + return err + } + for _, f := range []string{"README.md", "LICENSE", "CHANGELOG.md"} { + if err := copyFile(f, filepath.Join(main, f), 0o644); err != nil { + return err + } + } + return writeJSON(filepath.Join(main, "package.json"), mainPackage{ + Name: "steadybit", + Version: version, + Description: "Command-line interface to interact with the Steadybit API", + Keywords: []string{"steadybit", "cli", "chaos engineering", "resilience engineering", "api", "gitops"}, + License: "MIT", + Author: "Steadybit GmbH", + Repository: repo, + Bin: map[string]string{"steadybit": "bin/steadybit.js"}, + Files: []string{"bin", "README.md", "CHANGELOG.md", "LICENSE"}, + Engines: map[string]string{"node": ">=18"}, + OptionalDependencies: optional, + }) +} + +func copyFile(from, to string, mode os.FileMode) error { + if err := os.MkdirAll(filepath.Dir(to), 0o755); err != nil { + return err + } + src, err := os.Open(from) + if err != nil { + return err + } + defer src.Close() + dst, err := os.OpenFile(to, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + return err + } + defer dst.Close() + _, err = io.Copy(dst, src) + return err +} + +// writeJSON writes a package.json without Go's HTML escaping, which would turn ">=" in +// engines into "\u003e=". +func writeJSON(file string, value any) error { + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + encoder.SetEscapeHTML(false) + encoder.SetIndent("", " ") + if err := encoder.Encode(value); err != nil { + return err + } + return os.WriteFile(file, buf.Bytes(), 0o644) +} + +type repository struct { + Type string `json:"type"` + URL string `json:"url"` +} + +var repo = repository{Type: "git", URL: "https://github.com/steadybit/cli.git"} + +type platformPackage struct { + Name string `json:"name"` + Version string `json:"version"` + Description string `json:"description"` + License string `json:"license"` + Repository repository `json:"repository"` + OS []string `json:"os"` + CPU []string `json:"cpu"` + Files []string `json:"files"` + PreferUnplugged bool `json:"preferUnplugged"` +} + +type mainPackage struct { + Name string `json:"name"` + Version string `json:"version"` + Description string `json:"description"` + Keywords []string `json:"keywords"` + License string `json:"license"` + Author string `json:"author"` + Repository repository `json:"repository"` + Bin map[string]string `json:"bin"` + Files []string `json:"files"` + Engines map[string]string `json:"engines"` + OptionalDependencies map[string]string `json:"optionalDependencies"` +} diff --git a/internal/tools/spec/main.go b/internal/tools/spec/main.go new file mode 100644 index 0000000..6ace71f --- /dev/null +++ b/internal/tools/spec/main.go @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Command spec keeps the committed platform spec and the client generated from it in +// step with the platform. The CLI's requests and responses are compiled against that +// client, so a breaking change in the platform fails the build instead of a customer's +// pipeline. +// +// go run ./internal/tools/spec fetch download the live spec into openapi/platform-api.json +// go generate ./api regenerate api/platform.gen.go from the committed spec +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "regexp" + "time" +) + +const specFile = "openapi/platform-api.json" + +// Examples in the spec end up in the generated doc comments, and one of them is a Slack +// incoming webhook URL, which GitHub push protection rightly refuses. Examples carry no +// type information, so they are masked before anything is written. +var slackWebhook = regexp.MustCompile(`https://hooks\.slack\.com/services/[^"\s\\]+`) + +func main() { + if len(os.Args) != 2 || os.Args[1] != "fetch" { + fmt.Fprintln(os.Stderr, "usage: go run ./internal/tools/spec fetch") + os.Exit(2) + } + if err := fetch(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func fetch() error { + url := os.Getenv("STEADYBIT_SPEC_URL") + if url == "" { + url = "https://platform.steadybit.com/api/spec" + } + client := &http.Client{Timeout: time.Minute} + resp, err := client.Get(url) + if err != nil { + return fmt.Errorf("fetching the platform spec from %s: %w", url, err) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("fetching the platform spec from %s failed with status %d", url, resp.StatusCode) + } + raw, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + var spec struct { + OpenAPI string `json:"openapi"` + Paths map[string]json.RawMessage `json:"paths"` + } + if err := json.Unmarshal(raw, &spec); err != nil { + return fmt.Errorf("%s did not return JSON: %w", url, err) + } + if spec.OpenAPI == "" || spec.Paths == nil { + return fmt.Errorf("%s did not return an OpenAPI document", url) + } + // The platform's own bytes, indented: re-encoding would reorder sections and escape + // every < and > in the descriptions, and a diff of the file should read as a diff of + // the API. + var out bytes.Buffer + if err := json.Indent(&out, raw, "", " "); err != nil { + return err + } + out.WriteByte('\n') + if err := os.WriteFile(specFile, slackWebhook.ReplaceAll(out.Bytes(), []byte("https://hooks.slack.com/services/")), 0o644); err != nil { + return err + } + paths := spec.Paths + fmt.Printf("Wrote %d paths from %s to %s\n", len(paths), url, specFile) + return nil +} diff --git a/npm/steadybit/bin/steadybit.js b/npm/steadybit/bin/steadybit.js new file mode 100644 index 0000000..b50953b --- /dev/null +++ b/npm/steadybit/bin/steadybit.js @@ -0,0 +1,62 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +// Runs the steadybit binary for this platform, which npm installed as one of the +// optional dependencies. The CLI itself is a Go binary; this only finds and starts it. +'use strict'; + +const { spawn } = require('node:child_process'); +const path = require('node:path'); + +const platforms = { + 'darwin arm64': '@steadybit/cli-darwin-arm64', + 'darwin x64': '@steadybit/cli-darwin-x64', + 'linux arm64': '@steadybit/cli-linux-arm64', + 'linux x64': '@steadybit/cli-linux-x64', + 'win32 arm64': '@steadybit/cli-win32-arm64', + 'win32 x64': '@steadybit/cli-win32-x64', +}; + +function binaryPath() { + const pkg = platforms[`${process.platform} ${process.arch}`]; + if (!pkg) { + fail(`The Steadybit CLI is not available for ${process.platform} ${process.arch}.`); + } + const executable = process.platform === 'win32' ? 'steadybit.exe' : 'steadybit'; + try { + return require.resolve(path.posix.join(pkg, 'bin', executable)); + } catch { + fail( + `The package ${pkg}, which holds the CLI for this platform, is not installed. ` + + 'It is an optional dependency; reinstall without --no-optional / --omit=optional:\n\n npm install -g steadybit' + ); + } +} + +function fail(message) { + console.error(message); + process.exit(1); +} + +const child = spawn(binaryPath(), process.argv.slice(2), { stdio: 'inherit' }); + +// Ctrl-C reaches the CLI directly, as they share the terminal; this process only waits +// for it, so that the CLI decides how to end, and its exit status (130 for Ctrl-C) +// becomes ours. A signal sent to this process alone, as a CI runner does, is passed on. +for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) { + process.on(signal, () => { + if (signal !== 'SIGINT') { + child.kill(signal); + } + }); +} + +child.on('error', error => fail(`Failed to start the Steadybit CLI: ${error.message}`)); +child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + process.exit(code ?? 1); +}); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f9b3199..0000000 --- a/package-lock.json +++ /dev/null @@ -1,3725 +0,0 @@ -{ - "name": "steadybit", - "version": "4.4.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "steadybit", - "version": "4.4.0", - "license": "MIT", - "dependencies": { - "@inquirer/confirm": "^6.1.1", - "@inquirer/input": "^5.1.2", - "@inquirer/password": "^5.1.1", - "@inquirer/select": "^5.2.1", - "commander": "^15.0.0", - "console-table-printer": "^2.12.1", - "js-yaml": "^5.2.2", - "picocolors": "^1.1.1", - "rxjs": "^7.5.5", - "semver": "^7.3.5" - }, - "bin": { - "steadybit": "dist/cli/steadybit.js" - }, - "devDependencies": { - "@eslint/js": "^10.0.1", - "@inquirer/testing": "^3.3.9", - "@types/node": "^26.1.2", - "@types/semver": "^7.3.9", - "eslint": "^10.8.0", - "globals": "^17.8.0", - "msw": "^2.15.0", - "openapi-typescript": "^7.13.0", - "prettier": "^3.9.6", - "typescript": "~6.0.3", - "typescript-eslint": "^8.65.0", - "vitest": "^4.1.10" - }, - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@cacheable/memory": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", - "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cacheable/utils": "^2.5.0", - "@keyv/bigmap": "^1.3.1", - "hookified": "^1.15.1", - "keyv": "^5.6.0" - } - }, - "node_modules/@cacheable/utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", - "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hashery": "^1.5.1", - "keyv": "^5.6.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", - "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^3.0.5", - "debug": "^4.3.1", - "minimatch": "^10.2.4" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", - "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/js": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", - "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "eslint": "^10.0.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", - "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@inquirer/ansi": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.8.tgz", - "integrity": "sha512-WpQM+Ti6Z40EFwwt+uL2p4UabT+W179zHp6HhLVOzfbwnVn05IPO/eXIZXGNqcT1jbQ15SujNLzQ39k4QPPxBQ==", - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - } - }, - "node_modules/@inquirer/confirm": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.3.2.tgz", - "integrity": "sha512-Xvr/0HggjddPtGppuqVmxhTw+Hr8PvsZ/k0HmOEaAqQEt80OITNkFWnsdNmyT0/eM4Ab+iJLx2R8rctlEyfSVg==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^12.0.3", - "@inquirer/type": "4.1.1" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", - "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.8", - "@inquirer/figures": "^2.0.9", - "@inquirer/type": "4.1.1", - "cli-width": "^4.1.0", - "fast-wrap-ansi": "^0.2.0", - "mute-stream": "^3.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/figures": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.9.tgz", - "integrity": "sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==", - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - } - }, - "node_modules/@inquirer/input": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.1.6.tgz", - "integrity": "sha512-HtcJhB2QFVXbLuJ5S3syhNbTUVxYvwqV4VRBDkQceBloC9bmTViUoRFP5PbSaDZb3HzfPmpuU/gG4ybVBz4FHA==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^12.0.3", - "@inquirer/type": "4.1.1" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.2.2.tgz", - "integrity": "sha512-W9zYdyzogK+6110mqwaSJWCBu2yA5Q/OfnGSjjZB1bNpHlmUozXxTl0+QOZBNeVd6Qo81/qT75gW05gLAtITxw==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.8", - "@inquirer/core": "^12.0.3", - "@inquirer/type": "4.1.1" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.2.5.tgz", - "integrity": "sha512-9kc15hr8r/kI+3DO/xLog5nOzTz1jqsHXa6JBFzmQKhkoJ8Slda1I1L/uD8ZSZ9tF1yp79wwXe7mclvX1rqR2Q==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.8", - "@inquirer/core": "^12.0.3", - "@inquirer/figures": "^2.0.9", - "@inquirer/type": "4.1.1" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/testing": { - "version": "3.3.13", - "resolved": "https://registry.npmjs.org/@inquirer/testing/-/testing-3.3.13.tgz", - "integrity": "sha512-B6CucnYryJq5sy6emWhtnLwoNe3pqPsl8BvPdV8/ids3BnOG5VelYaGVf36aPenVpS1bCIPo0RZ28BEAnWNcEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/type": "4.1.1", - "@xterm/headless": "^6.0.0", - "mute-stream": "^3.0.0" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - }, - "peerDependencies": { - "@inquirer/checkbox": ">=1.0.0", - "@inquirer/confirm": ">=1.0.0", - "@inquirer/editor": ">=1.0.0", - "@inquirer/expand": ">=1.0.0", - "@inquirer/external-editor": ">=1.0.0", - "@inquirer/input": ">=1.0.0", - "@inquirer/number": ">=1.0.0", - "@inquirer/password": ">=1.0.0", - "@inquirer/prompts": ">=1.0.0", - "@inquirer/rawlist": ">=1.0.0", - "@inquirer/search": ">=1.0.0", - "@inquirer/select": ">=1.0.0", - "@types/jest": ">=29.0.0", - "@types/node": ">=18", - "jest": ">=29.0.0", - "vitest": ">=1.0.0" - }, - "peerDependenciesMeta": { - "@inquirer/checkbox": { - "optional": true - }, - "@inquirer/confirm": { - "optional": true - }, - "@inquirer/editor": { - "optional": true - }, - "@inquirer/expand": { - "optional": true - }, - "@inquirer/external-editor": { - "optional": true - }, - "@inquirer/input": { - "optional": true - }, - "@inquirer/number": { - "optional": true - }, - "@inquirer/password": { - "optional": true - }, - "@inquirer/prompts": { - "optional": true - }, - "@inquirer/rawlist": { - "optional": true - }, - "@inquirer/search": { - "optional": true - }, - "@inquirer/select": { - "optional": true - }, - "@types/jest": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "jest": { - "optional": true - }, - "vitest": { - "optional": true - } - } - }, - "node_modules/@inquirer/type": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.1.1.tgz", - "integrity": "sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A==", - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@keyv/bigmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", - "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "hashery": "^1.4.0", - "hookified": "^1.15.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "keyv": "^5.6.0" - } - }, - "node_modules/@keyv/serialize": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", - "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@mswjs/interceptors": { - "version": "0.41.9", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.9.tgz", - "integrity": "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/logger": "^0.3.0", - "@open-draft/until": "^2.0.0", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "strict-event-emitter": "^0.5.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@open-draft/deferred-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz", - "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@open-draft/logger": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", - "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-node-process": "^1.2.0", - "outvariant": "^1.4.0" - } - }, - "node_modules/@open-draft/until": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", - "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@oxc-project/types": { - "version": "0.147.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.147.0.tgz", - "integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@redocly/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js-replace": "^1.0.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@redocly/ajv/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@redocly/config": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.0.tgz", - "integrity": "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@redocly/openapi-core": { - "version": "1.34.20", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.20.tgz", - "integrity": "sha512-ypeBZ/6BKXR9+7/TtbKhbl4UgD7raHhPS12oknlKno2A8+lnFkxIwiE/Aklu6L2cd/ioH+fCWuMxi9/p3EyAPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@redocly/ajv": "8.11.2", - "@redocly/config": "0.22.0", - "colorette": "1.4.0", - "https-proxy-agent": "7.0.6", - "js-levenshtein": "1.1.6", - "js-yaml": "4.3.2", - "minimatch": "5.1.9", - "pluralize": "8.0.0", - "yaml-ast-parser": "0.0.43" - }, - "engines": { - "node": ">=18.17.0", - "npm": ">=9.5.0" - } - }, - "node_modules/@redocly/openapi-core/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@redocly/openapi-core/node_modules/brace-expansion": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", - "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@redocly/openapi-core/node_modules/js-yaml": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", - "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@redocly/openapi-core/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@rolldown/binding-android-arm-eabi": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz", - "integrity": "sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.6.tgz", - "integrity": "sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.6.tgz", - "integrity": "sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.6.tgz", - "integrity": "sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.6.tgz", - "integrity": "sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.6.tgz", - "integrity": "sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.6.tgz", - "integrity": "sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.6.tgz", - "integrity": "sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.6.tgz", - "integrity": "sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.6.tgz", - "integrity": "sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.6.tgz", - "integrity": "sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.6.tgz", - "integrity": "sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.6.tgz", - "integrity": "sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.6.tgz", - "integrity": "sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.6.tgz", - "integrity": "sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.5.0.tgz", - "integrity": "sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~8.9.0" - } - }, - "node_modules/@types/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/set-cookie-parser": { - "version": "2.4.10", - "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", - "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/statuses": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", - "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz", - "integrity": "sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.70.0", - "@typescript-eslint/type-utils": "8.70.0", - "@typescript-eslint/utils": "8.70.0", - "@typescript-eslint/visitor-keys": "8.70.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.70.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", - "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.70.0.tgz", - "integrity": "sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.70.0", - "@typescript-eslint/types": "8.70.0", - "@typescript-eslint/typescript-estree": "8.70.0", - "@typescript-eslint/visitor-keys": "8.70.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.0.tgz", - "integrity": "sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.70.0", - "@typescript-eslint/types": "^8.70.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.0.tgz", - "integrity": "sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.0", - "@typescript-eslint/visitor-keys": "8.70.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.0.tgz", - "integrity": "sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.0.tgz", - "integrity": "sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.0", - "@typescript-eslint/typescript-estree": "8.70.0", - "@typescript-eslint/utils": "8.70.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.0.tgz", - "integrity": "sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.0.tgz", - "integrity": "sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.70.0", - "@typescript-eslint/tsconfig-utils": "8.70.0", - "@typescript-eslint/types": "8.70.0", - "@typescript-eslint/visitor-keys": "8.70.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.0.tgz", - "integrity": "sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.70.0", - "@typescript-eslint/types": "8.70.0", - "@typescript-eslint/typescript-estree": "8.70.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.0.tgz", - "integrity": "sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.70.0", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@vitest/expect": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", - "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.11", - "@vitest/utils": "4.1.11", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", - "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "4.1.11", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", - "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", - "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.11", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", - "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.11", - "@vitest/utils": "4.1.11", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", - "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", - "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.11", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@xterm/headless": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.0.0.tgz", - "integrity": "sha512-5Yj1QINYCyzrZtf8OFIHi47iQtI+0qYFPHmouEfG8dHNxbZ9Tb9YGSuLcsEwj9Z+OL75GJqPyJbyoFer80a2Hw==", - "dev": true, - "license": "MIT", - "workspaces": [ - "addons/*" - ] - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/cacheable": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", - "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cacheable/memory": "^2.2.0", - "@cacheable/utils": "^2.5.0", - "hookified": "^1.15.0", - "keyv": "^5.6.0", - "qified": "^0.10.1" - } - }, - "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/change-case": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", - "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", - "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", - "license": "MIT", - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/console-table-printer": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.16.1.tgz", - "integrity": "sha512-Sc9FRJ4O9xKGNrvulNdPfK5SyBcZ6lcaRnDE4AQ/uw6IDtjHhsqyzzqcnMikjyGaiOOF2tNOKoBhbVjRvFy9Lw==", - "license": "MIT", - "dependencies": { - "simple-wcswidth": "^1.1.2" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-module-lexer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", - "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.10.0.tgz", - "integrity": "sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==", - "dev": true, - "license": "MIT", - "workspaces": [ - "packages/*" - ], - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.7.0", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.3", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "11.1.5 || >11.1.6 <12", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expect-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", - "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-string-truncated-width": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", - "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", - "license": "MIT" - }, - "node_modules/fast-string-width": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", - "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", - "license": "MIT", - "dependencies": { - "fast-string-truncated-width": "^3.0.2" - } - }, - "node_modules/fast-wrap-ansi": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", - "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", - "license": "MIT", - "dependencies": { - "fast-string-width": "^3.0.2" - } - }, - "node_modules/file-entry-cache": { - "version": "11.1.5", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", - "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^6.1.23" - } - }, - "node_modules/flat-cache": { - "version": "6.1.23", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", - "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cacheable": "^2.5.0", - "flatted": "^3.4.2", - "hookified": "^1.15.0" - } - }, - "node_modules/flatted": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", - "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "17.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz", - "integrity": "sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graphql": { - "version": "16.14.2", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", - "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, - "node_modules/hashery": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", - "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "hookified": "^1.15.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/headers-polyfill": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz", - "integrity": "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/set-cookie-parser": "^2.4.10", - "set-cookie-parser": "^3.0.1" - } - }, - "node_modules/hookified": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", - "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/index-to-position": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", - "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-node-process": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", - "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.mjs" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", - "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@keyv/serialize": "^1.1.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", - "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.33.0", - "lightningcss-darwin-arm64": "1.33.0", - "lightningcss-darwin-x64": "1.33.0", - "lightningcss-freebsd-x64": "1.33.0", - "lightningcss-linux-arm-gnueabihf": "1.33.0", - "lightningcss-linux-arm64-gnu": "1.33.0", - "lightningcss-linux-arm64-musl": "1.33.0", - "lightningcss-linux-x64-gnu": "1.33.0", - "lightningcss-linux-x64-musl": "1.33.0", - "lightningcss-win32-arm64-msvc": "1.33.0", - "lightningcss-win32-x64-msvc": "1.33.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", - "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", - "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", - "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", - "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", - "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", - "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", - "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", - "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", - "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", - "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", - "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/msw": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.15.0.tgz", - "integrity": "sha512-2wQAmKkQKxRuXvYJxVhPGG0wZNBQyD06oJvxqw90XqLvptdqxdlHrFUfEteKkpaNORX3Xzc+HtEl/q0nfmN2wQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@inquirer/confirm": "^6.0.11", - "@mswjs/interceptors": "^0.41.3", - "@open-draft/deferred-promise": "^3.0.0", - "@types/statuses": "^2.0.6", - "cookie": "^1.1.1", - "graphql": "^16.13.2", - "headers-polyfill": "^5.0.1", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "path-to-regexp": "^6.3.0", - "picocolors": "^1.1.1", - "rettime": "^0.11.11", - "statuses": "^2.0.2", - "strict-event-emitter": "^0.5.1", - "tough-cookie": "^6.0.1", - "type-fest": "^5.5.0", - "until-async": "^3.0.2", - "yargs": "^17.7.2" - }, - "bin": { - "msw": "cli/index.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mswjs" - }, - "peerDependencies": { - "typescript": ">= 4.8.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/msw/node_modules/type-fest": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz", - "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "dependencies": { - "tagged-tag": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mute-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", - "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/obug": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", - "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT", - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/openapi-typescript": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", - "integrity": "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@redocly/openapi-core": "^1.34.6", - "ansi-colors": "^4.1.3", - "change-case": "^5.4.4", - "parse-json": "^8.3.0", - "supports-color": "^10.2.2", - "yargs-parser": "^21.1.1" - }, - "bin": { - "openapi-typescript": "bin/cli.js" - }, - "peerDependencies": { - "typescript": "^5.x" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/outvariant": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", - "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", - "dev": true, - "license": "MIT" - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-json": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", - "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "index-to-position": "^1.1.0", - "type-fest": "^4.39.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.17", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.9.6", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", - "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qified": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", - "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hookified": "^2.1.1" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/qified/node_modules/hookified": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", - "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rettime": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.11.tgz", - "integrity": "sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/rolldown": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.6.tgz", - "integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.147.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm-eabi": "1.2.6", - "@rolldown/binding-android-arm64": "1.2.6", - "@rolldown/binding-darwin-arm64": "1.2.6", - "@rolldown/binding-darwin-x64": "1.2.6", - "@rolldown/binding-freebsd-x64": "1.2.6", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.6", - "@rolldown/binding-linux-arm64-gnu": "1.2.6", - "@rolldown/binding-linux-arm64-musl": "1.2.6", - "@rolldown/binding-linux-ppc64-gnu": "1.2.6", - "@rolldown/binding-linux-s390x-gnu": "1.2.6", - "@rolldown/binding-linux-x64-gnu": "1.2.6", - "@rolldown/binding-linux-x64-musl": "1.2.6", - "@rolldown/binding-openharmony-arm64": "1.2.6", - "@rolldown/binding-win32-arm64-msvc": "1.2.6", - "@rolldown/binding-win32-x64-msvc": "1.2.6" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz", - "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==", - "dev": true, - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-wcswidth": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz", - "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==", - "license": "MIT" - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", - "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", - "dev": true, - "license": "MIT" - }, - "node_modules/strict-event-emitter": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", - "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", - "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/tagged-tag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", - "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyrainbow": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", - "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tldts": { - "version": "7.4.9", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.9.tgz", - "integrity": "sha512-3kZ8wQQ/k5DrChD4X4FVvr2D7E5uoRgAqkPyLpSCGUvqOvqu+JEdr3mwMUaVWb+vMHZaKhF5fp2PBigKsui7hA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^7.4.9" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "7.4.9", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.9.tgz", - "integrity": "sha512-DxKfPBI52p2msTEu7MPhdpdDTBhhVQg1a/8PjQckeyAvO13eMYElX545grIp6nnTGIMZlRvFZPvFhvI/WIz2Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tough-cookie": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", - "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^7.0.5" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.70.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.70.0.tgz", - "integrity": "sha512-P/W5cz70/cQAuKfY3xwQMWWTV7BvJ0mAQmi+9mBcsVPaBUpd6Ohpa+fECv9rBFrQcig86jAiNBFNWUqnTjr4pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.70.0", - "@typescript-eslint/parser": "8.70.0", - "@typescript-eslint/typescript-estree": "8.70.0", - "@typescript-eslint/utils": "8.70.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/undici-types": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", - "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", - "dev": true, - "license": "MIT" - }, - "node_modules/until-async": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", - "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/kettanaito" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js-replace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", - "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", - "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.33.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.26", - "rolldown": "~1.2.4", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0 || ^0.5.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vitest": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", - "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.11", - "@vitest/mocker": "4.1.11", - "@vitest/pretty-format": "4.1.11", - "@vitest/runner": "4.1.11", - "@vitest/snapshot": "4.1.11", - "@vitest/spy": "4.1.11", - "@vitest/utils": "4.1.11", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.11", - "@vitest/browser-preview": "4.1.11", - "@vitest/browser-webdriverio": "4.1.11", - "@vitest/coverage-istanbul": "4.1.11", - "@vitest/coverage-v8": "4.1.11", - "@vitest/ui": "4.1.11", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/coverage-istanbul": { - "optional": true - }, - "@vitest/coverage-v8": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yaml-ast-parser": { - "version": "0.0.43", - "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", - "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/yargs": { - "version": "17.7.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", - "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index b30191c..0000000 --- a/package.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "name": "steadybit", - "version": "4.4.0", - "description": "Command-line interface to interact with the Steadybit API", - "type": "module", - "keywords": [ - "steadybit", - "cli", - "chaos engineering", - "resilience engineering", - "api", - "gitops" - ], - "files": [ - "dist" - ], - "bin": { - "steadybit": "dist/cli/steadybit.js" - }, - "repository": { - "type": "git", - "url": "https://github.com/steadybit/cli.git" - }, - "scripts": { - "clean": "rm -rf dist", - "verify": "npm run verify:typecheck && npm run verify:api-types && npm run verify:unit-test && npm run verify:lint && npm run prettier-check", - "verify:typecheck": "tsc --noEmit", - "verify:api-types": "node scripts/api-spec.mjs check", - "verify:unit-test": "vitest run", - "verify:lint": "eslint .", - "prettier-check": "prettier . --check", - "prettier-write": "prettier . --write", - "build": "npm run clean && npm run build:ts && npm run build:permissions", - "build:ts": "tsc -p tsconfig.build.json", - "build:permissions": "chmod 755 dist/cli/*.js", - "ci": "npm run verify && npm run build", - "prepublishOnly": "npm run verify && npm run build", - "api:fetch-spec": "node scripts/api-spec.mjs fetch", - "api:generate": "node scripts/api-spec.mjs generate", - "api:update": "npm run api:fetch-spec && npm run api:generate && npm run verify:typecheck" - }, - "engines": { - "node": ">=22.13.0" - }, - "author": "Steadybit GmbH", - "license": "MIT", - "dependencies": { - "@inquirer/confirm": "^6.1.1", - "@inquirer/input": "^5.1.2", - "@inquirer/password": "^5.1.1", - "@inquirer/select": "^5.2.1", - "commander": "^15.0.0", - "console-table-printer": "^2.12.1", - "js-yaml": "^5.2.2", - "picocolors": "^1.1.1", - "rxjs": "^7.5.5", - "semver": "^7.3.5" - }, - "devDependencies": { - "@eslint/js": "^10.0.1", - "@inquirer/testing": "^3.3.9", - "@types/node": "^26.1.2", - "@types/semver": "^7.3.9", - "eslint": "^10.8.0", - "globals": "^17.8.0", - "msw": "^2.15.0", - "openapi-typescript": "^7.13.0", - "prettier": "^3.9.6", - "typescript": "~6.0.3", - "typescript-eslint": "^8.65.0", - "vitest": "^4.1.10" - }, - "overrides": { - "openapi-typescript": { - "typescript": "$typescript" - } - } -} diff --git a/scripts/api-spec.mjs b/scripts/api-spec.mjs deleted file mode 100644 index cfb044f..0000000 --- a/scripts/api-spec.mjs +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -// The CLI's request and response types are generated from the platform's OpenAPI spec, -// so that a breaking change in the platform shows up as a type error here instead of as -// a failing command in a customer's pipeline. -// -// fetch download the live spec into openapi/platform-api.json -// generate regenerate src/api/generated/platform-api.ts from the committed spec -// check fail if the generated types are not what the committed spec produces -// -// `fetch` followed by `generate` and a type check is what CI runs against the live -// platform. `check` runs as part of `npm run verify`, so the committed types can never -// silently drift from the committed spec. - -import fs from 'node:fs/promises'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import openapiTS, { astToString } from 'openapi-typescript'; - -const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); -const specFile = path.join(root, 'openapi', 'platform-api.json'); -const typesFile = path.join(root, 'src', 'api', 'generated', 'platform-api.ts'); -const specUrl = process.env.STEADYBIT_SPEC_URL || 'https://platform.steadybit.com/api/spec'; - -const HEADER = `// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -// Generated from openapi/platform-api.json by \`npm run api:generate\`. Do not edit. - -`; - -// Examples in the spec are copied into the generated doc comments, and one of them is a -// Slack incoming webhook URL, which GitHub push protection rightly refuses to accept. -// The examples carry no type information, so they are masked before anything is written. -function redactSecrets(text) { - return text.replace( - /https:\/\/hooks\.slack\.com\/services\/[^"\s\\]+/g, - 'https://hooks.slack.com/services/' - ); -} - -async function fetchSpec() { - const response = await fetch(specUrl, { headers: { Accept: 'application/json' } }); - if (!response.ok) { - throw new Error(`Fetching the platform spec from ${specUrl} failed with status ${response.status}`); - } - const spec = await response.json(); - if (!spec.openapi || !spec.paths) { - throw new Error(`${specUrl} did not return an OpenAPI document`); - } - await fs.mkdir(path.dirname(specFile), { recursive: true }); - await fs.writeFile(specFile, `${redactSecrets(JSON.stringify(spec, undefined, 2))}\n`); - console.log(`Wrote ${Object.keys(spec.paths).length} paths from ${specUrl} to ${path.relative(root, specFile)}`); -} - -// The platform models polymorphism the way springdoc emits it: a base schema lists its -// subtypes under `oneOf`, and every subtype pulls the base back in through `allOf`. That -// is valid OpenAPI but a type that contains itself in TypeScript. Each subtype is given -// the base's own fields inline instead, which keeps them while breaking the cycle. -function breakPolymorphicCycles(spec) { - const schemas = spec.components?.schemas ?? {}; - const refTo = name => `#/components/schemas/${name}`; - for (const [baseName, base] of Object.entries(schemas)) { - const subtypes = (base.oneOf ?? []).map(member => member.$ref?.split('/').pop()).filter(Boolean); - const ownFields = structuredClone(base); - delete ownFields.oneOf; - delete ownFields.discriminator; - for (const subtypeName of subtypes) { - const subtype = schemas[subtypeName]; - if (!subtype?.allOf) { - continue; - } - subtype.allOf = subtype.allOf.map(part => (part.$ref === refTo(baseName) ? structuredClone(ownFields) : part)); - } - } - return spec; -} - -async function render() { - const spec = breakPolymorphicCycles(JSON.parse(await fs.readFile(specFile, 'utf8'))); - const ast = await openapiTS(spec, { alphabetize: true }); - return HEADER + astToString(ast); -} - -async function generate() { - await fs.mkdir(path.dirname(typesFile), { recursive: true }); - await fs.writeFile(typesFile, await render()); - console.log(`Wrote ${path.relative(root, typesFile)}`); -} - -async function check() { - const [expected, actual] = await Promise.all([render(), fs.readFile(typesFile, 'utf8').catch(() => '')]); - if (expected !== actual) { - console.error( - `${path.relative(root, typesFile)} is out of date with ${path.relative(root, specFile)}. Run \`npm run api:generate\`.` - ); - process.exit(1); - } -} - -const commands = { fetch: fetchSpec, generate, check }; -const command = commands[process.argv[2]]; -if (!command) { - console.error(`Usage: api-spec.mjs <${Object.keys(commands).join('|')}>`); - process.exit(2); -} -await command(); diff --git a/src/advice/api.ts b/src/advice/api.ts deleted file mode 100644 index 0325104..0000000 --- a/src/advice/api.ts +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH - -import type { AdviceStatus, FetchAdviceRequest, FetchAdviceResponse } from './types.ts'; - -import { abortExecutionWithError } from '../errors.ts'; -import { executeApiCall } from '../api/http.ts'; -import { format } from 'node:util'; - -async function fetchAdvice(offset: number, environment: string, query?: string): Promise { - const body: FetchAdviceRequest = { - offset: offset, - environmentName: environment, - }; - if (query) { - body.query = query; - } - try { - const response = await executeApiCall({ - method: 'POST', - path: '/api/advice', - body, - }); - return (await response.json()) as FetchAdviceResponse; - } catch (e) { - throw abortExecutionWithError(e, 'Failed to fetch advice status. HTTP request failed.'); - } -} - -export async function fetchAllAdvice(environment: string, query?: string): Promise { - let offset = 0; - const allAdvice: AdviceStatus[] = []; - do { - const response = await fetchAdvice(offset, environment, query); - if (response.nextOffset) { - offset = response.nextOffset; - } else { - offset = -1; - } - if (response.items.length > 0) { - allAdvice.push(...response.items); - console.log(format('Fetched %d of %d matching advice.', allAdvice.length, response.totalItems)); - } else { - console.log('No matching advice.'); - } - } while (offset > 0); - return allAdvice; -} diff --git a/src/advice/types.ts b/src/advice/types.ts deleted file mode 100644 index 0e67b32..0000000 --- a/src/advice/types.ts +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH - -export type FetchAdviceRequest = { - environmentName: string; - query?: string; - offset?: number; -}; - -export type FetchAdviceResponse = { - totalItems: number; - nextOffset?: number; - items: AdviceStatus[]; -}; - -export type AdviceStatus = Record & { - target: Record & { - type: string; - reference: string; - label: string; - }; - advice: Record & { - type: string; - label: string; - status: string; - }; - url: string; -}; diff --git a/src/advice/validateStatus.test.ts b/src/advice/validateStatus.test.ts deleted file mode 100644 index 8c73ac2..0000000 --- a/src/advice/validateStatus.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH - -import { describe, expect, it, vi } from 'vitest'; -import { validateAdviceStatus } from './validateStatus.ts'; - -describe('advice', () => { - describe('validate-status', () => { - const logSpy = vi.spyOn(console, 'log'); - it('should exit with != 0 if status not matching', async () => { - await expect( - validateAdviceStatus({ environment: 'Global', query: 'mock.response=fail', status: 'Implemented' }) - ).rejects.toThrow('2 of 3 advice did not match the expected status.'); - expect(logSpy).toHaveBeenCalledWith('Fetched 3 of 3 matching advice.'); - }); - - it('should exit with 0 if all ok', async () => { - const logSpy = vi.spyOn(console, 'log'); - await validateAdviceStatus({ environment: 'Global', query: 'mock.response=ok', status: 'Implemented' }); - expect(logSpy).toHaveBeenCalledWith('Fetched 1 of 1 matching advice.'); - }); - - // The platform reports IMPLEMENTED while the default for --status is written - // Implemented. Comparing them exactly meant the command failed even when every - // piece of advice was implemented, which is the whole point of the check. - it.each(['Implemented', 'IMPLEMENTED', 'implemented', ' Implemented '])( - 'should accept %s as the expected status', - async status => { - await expect( - validateAdviceStatus({ environment: 'Global', query: 'mock.response=ok', status }) - ).resolves.toBeUndefined(); - } - ); - - it.each([ - ['ACTION_NEEDED', 2], - ['action needed', 2], - ['Action needed', 2], - ])('should treat %s as the same status the platform reports', async (status, expectedFailures) => { - await expect( - validateAdviceStatus({ environment: 'Global', query: 'mock.response=fail', status: String(status) }) - ).rejects.toThrow(`${expectedFailures} of 3 advice did not match the expected status.`); - }); - - it('should still reject a status that genuinely differs', async () => { - await expect( - validateAdviceStatus({ environment: 'Global', query: 'mock.response=ok', status: 'ACTION_NEEDED' }) - ).rejects.toThrow('1 of 1 advice did not match the expected status.'); - }); - }); -}); diff --git a/src/advice/validateStatus.ts b/src/advice/validateStatus.ts deleted file mode 100644 index 4051050..0000000 --- a/src/advice/validateStatus.ts +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH - -import { fetchAllAdvice } from './api.ts'; -import { COLOR } from 'console-table-printer'; -import { createTable } from '../table.ts'; -import { abortExecution } from '../errors.ts'; - -export interface Options { - environment: string; - query?: string; - status: string; -} - -const red_color: COLOR = 'red'; -const green_color: COLOR = 'green'; - -// The platform reports IMPLEMENTED, ACTION_NEEDED and VALIDATION_NEEDED, while the -// default for --status is written Implemented, so an exact comparison never matched and -// the command failed even when every piece of advice was implemented. Case and the -// separator are both ignored, so either spelling works whichever way round it is given. -function sameStatus(reported: string, expected: string): boolean { - const normalise = (status: string) => - status - .trim() - .toLowerCase() - .replace(/[\s_-]+/g, '_'); - return normalise(reported) === normalise(expected); -} - -export async function validateAdviceStatus(options: Options) { - const allAdvice = await fetchAllAdvice(options.environment, options.query); - if (allAdvice.length === 0) { - return; - } - - let errorCount = 0; - const p = createTable(); - for (const advice of allAdvice) { - const statusMatch = sameStatus(advice.advice.status, options.status); - if (!statusMatch) { - errorCount++; - } - p.addRow( - { - target: advice.target.reference, - advice: advice.advice.label, - status: advice.advice.status, - }, - { color: statusMatch ? green_color : red_color } - ); - } - p.printTable(); - if (errorCount > 0) { - throw abortExecution('%d of %d advice did not match the expected status.', errorCount, allAdvice.length); - } -} diff --git a/src/api/common.ts b/src/api/common.ts deleted file mode 100644 index 77ccae2..0000000 --- a/src/api/common.ts +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { getConfiguration } from '../config/index.ts'; -import { abortExecution } from '../errors.ts'; -import { packageJson } from '../packageJson.ts'; - -export type QueryParameters = Record; - -export async function toUrl(path: string, queryParameters?: QueryParameters): Promise { - const config = await getConfiguration(); - let url = isAbsoluteUrl(path) ? onConfiguredOrigin(path, config.baseUrl) : `${config.baseUrl}${path}`; - const query = toSearchParams(queryParameters).toString(); - if (query) { - url = `${url}${url.includes('?') ? '&' : '?'}${query}`; - } - return url; -} - -// The platform takes a list filter such as `?team=A&team=B` as the parameter repeated, -// which a plain record cannot express. An omitted optional flag arrives as undefined and -// is left out rather than sent as the string "undefined". -function toSearchParams(queryParameters: QueryParameters = {}): URLSearchParams { - const params = new URLSearchParams(); - for (const [key, value] of Object.entries(queryParameters)) { - for (const item of Array.isArray(value) ? value : [value]) { - if (item !== undefined) { - params.append(key, item); - } - } - } - return params; -} - -function isAbsoluteUrl(path: string): boolean { - return /^https?:\/\//i.test(path); -} - -// Absolute URLs reach us from platform responses, most notably the Location header a -// run returns, and every request carries the API access token. A platform behind a -// proxy legitimately names its public host there, which need not be the host the CLI -// was configured with, so only the origin is replaced and the path is kept verbatim. -// That keeps such deployments working while the token never leaves the configured host. -function onConfiguredOrigin(url: string, baseUrl: string): string { - let target: URL; - let base: URL; - try { - target = new URL(url); - base = new URL(baseUrl); - } catch { - throw abortExecution("Cannot request '%s' relative to the configured platform at '%s'.", url, baseUrl); - } - - return target.origin === base.origin ? url : `${base.origin}${target.pathname}${target.search}`; -} - -export async function getHeaders(): Promise> { - const config = await getConfiguration(); - return { - Authorization: `accessToken ${config.apiAccessToken}`, - 'Content-Type': 'application/json', - Accept: 'application/json, */*', - 'User-Agent': `${packageJson.name}@${packageJson.version}`, - }; -} diff --git a/src/api/error.ts b/src/api/error.ts deleted file mode 100644 index 2e0e16c..0000000 --- a/src/api/error.ts +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -// The status and the problem body used to travel as untyped properties on a plain Error, -// which every caller duck-typed back out. Naming the type lets callers branch on the -// status through the compiler instead of by convention. -// -// This lives apart from http.ts so that errors.ts can recognise it without the two -// modules having to import each other. -export class ApiError extends Error { - constructor( - message: string, - readonly response: Response, - // The response body can only be read once, so the text read to build the message - // above is carried here rather than left on the unusable response. - private readonly responseBody: string - ) { - super(message); - this.name = 'ApiError'; - } - - get status(): number { - return this.response.status; - } - - problemBody(): T | undefined { - try { - return this.responseBody ? JSON.parse(this.responseBody) : undefined; - } catch { - return undefined; - } - } -} diff --git a/src/api/generated/platform-api.ts b/src/api/generated/platform-api.ts deleted file mode 100644 index f6bec8d..0000000 --- a/src/api/generated/platform-api.ts +++ /dev/null @@ -1,14387 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -// Generated from openapi/platform-api.json by `npm run api:generate`. Do not edit. - -export interface paths { - "/api/access-tokens": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get access token list - * @deprecated - * @description Deprecated, use v2 instead. Get a list of all access tokens. The access token itself is abbreviated for security reasons. Access tokens with v2 features are not returned, as they can not be represented cleanly in the old format. - */ - get: operations["getAccessTokens"]; - put?: never; - /** - * Add a access token - * @deprecated - * @description Deprecated, use v2 instead. Generate a new access token associated to. This access token can be used for e.g. creating new experiments and running experiments. - */ - post: operations["createAccessToken"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/access-tokens/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - /** - * Delete access token - * @deprecated - * @description Remove the access token associated. After that, the access token can't be used anymore for e.g. creating a new experiment or running an experiment. - */ - delete: operations["deleteAccessToken"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/access-tokens/v2": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get access token list - * @description Get a list of all access tokens. The access token itself is abbreviated for security reasons. - */ - get: operations["getAccessTokens_1"]; - put?: never; - /** - * Create an access token - * @description Generate a new access token. - */ - post: operations["createAccessToken_1"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/access-tokens/v2/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - /** - * Delete access token - * @description Remove the access token. After that, the access token can't be used anymore. - */ - delete: operations["deleteAccessToken_1"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/access-tokens/v2/{id}/recreate": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Recreate an access token - * @description Recreate an existing access token with a new expiration date. The old token is deleted and a new one is generated with the same name, type, and team associations. - */ - post: operations["recreateAccessToken"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/actions": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get all actions. */ - get: operations["findAllActions"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/actions/{actionId}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a single action description - * @description Get action including their parameters. - */ - get: operations["getAction"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/advice": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Get all currently active advice for a given environment and query. */ - post: operations["getTargetAdviceSummary"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/audit-log": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get all audit log entries - * @description Retrieve all audit logs in the given time-frame.
This endpoint requires an admin-token and can't be used with a team-based token. - */ - get: operations["find"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/badges/link": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Forward to Steadybit platform to either create an experiment associated to the `tag` or forward to the experiments linked already to the `tag` - * @description This endpoint can be used as a link for the badge of the `/api/badges/linked-badge.svg` API to either create a new experiment or show the linked experiments in Steadybit. This will help to link it correctly e.g. in your CMS-systems. - */ - get: operations["forwardToPlatform"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/badges/linked-badge.svg": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get badge for create experiment or run status as SVG image - * @description Creates an image badge that is either for creating a new experiment linked to an `externalReference` or - if an experiment with the given `externalReference` already exists - a badge showing the run status of the experiment. The badge is return as SVG to integrate it nicely e.g. into your CMS-systems. You can use the `/api/badges/link` endpoint to link it appropriately - */ - get: operations["getLinkedBadge"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/environments": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a list of all environments - * @description Get a list of all environments that exist. - */ - get: operations["getEnvironments"]; - put?: never; - /** - * Create or update an environment - * @description Insert or update the environment in Steadybit. The `id` will be used to identify whether the environment exists already and should be updated or newly inserted. - */ - post: operations["upsertEnvironment"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/environments/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a single environment - * @description Get all details of a single existing environment. - */ - get: operations["getEnvironment"]; - put?: never; - post?: never; - /** - * Delete environment - * @description Remove the given environment from the Steadybit platform. - */ - delete: operations["deleteEnvironment"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/environments/{id}/variables": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get environment variables - * @description Get all environment variables associated to a single environment - */ - get: operations["getEnvironmentVariables"]; - /** - * Add / merge all environment variables - * @description All provided environment variables will be associated to the given environment.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it continues to exist. - */ - put: operations["updateEnvironmentVariables"]; - /** - * Replace all environment variables - * @description All provided environment variables will be associated to the given environment and existing ones removed.
If an environment variable key is already in use, it's value is updated with the value being provided.
If an environment variable is already associated to the environment but not provided, it will be removed. - */ - post: operations["setEnvironmentVariables"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a list of all experiments - * @description Get a list of all experiments that exist. - */ - get: operations["getExperiments"]; - put?: never; - /** - * Create or update an experiment - * @description Insert or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. - */ - post: operations["createOrUpdateExperiment"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/{key}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a single experiment - * @description Get all details of a single existing experiment. - */ - get: operations["getExperiment"]; - put?: never; - /** - * Update an experiment - * @description Update the experiment identified by the experiment `key`. - */ - post: operations["updateExperiment"]; - /** - * Delete experiment - * @description Remove the given experiment. The associated number is still reserved afterwards and will not be reused. - */ - delete: operations["deleteExperiment"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/{key}/badge.svg": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get experiment run status as SVG image - * @description Get the status of the latest experiment run of the associated experiment as SVG to integrate it nicely e.g. into your CMS-systems. - */ - get: operations["getExperimentBadge"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/{key}/execute": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Execute an experiment - * @description Trigger execution of a single experiment specified by `key`. The body is optional and allows to specify overrides and custom properties for the experiment execution. - * - * Examples: - * - Override environment from the experiment for a single run: - * ``` - * { - * "environment": "Shop Stage" - * } - * ``` - * - Override the variables for a single execution: - * ``` - * { - * "variables": { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - * } - * ``` - */ - post: operations["executeExperiment"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/{key}/executions": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a list of all experiment executions of a single experiment - * @description Get a list of all experiment executions that were performed for a specific experiment. - */ - get: operations["getExperimentExecutions_3"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/execute": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Save and run experiment - * @description Save the given experiment and immediately run it. - */ - post: operations["saveAndRun"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/executions": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a list of all experiment executions - * @deprecated - * @description Get a list of all experiment executions that exist. - */ - get: operations["getExperimentExecutions_1"]; - put?: never; - /** - * Fetch a list of experiment executions - * @description Get list of experiment executions given a set of filters. The result is sorted by creation date in descending order. The result is paged with a page size of 50. - */ - post: operations["getExperimentExecutions_2"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/executions/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a single experiment executions of a single experiment - * @description Get a single experiment execution that was performed for a specific experiment. - */ - get: operations["getExperimentExecution"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/executions/{id}/artifacts/{targetExecutionId}/{artifactId}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["getArtifact"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/executions/{id}/cancel": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Cancel a running experiment execution of a single experiment - * @description Cancels a currently running experiment execution to be stopped as soon as possible. - */ - post: operations["cancelExperimentExecution"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/executions/{id}/properties": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Update properties of an experiment execution - * @description Update properties of an experiment execution. This is only possible for associated properties with `editableInExecution` set to `true` or for properties that have been added after the execution. - */ - post: operations["updateExecutionProperties"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/executions/{id}/properties/{key}/add": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Add a single value to a list property of an experiment execution. - * @description This operation will fail if the property identified by `key` is not a list property. Only properties with `editableInExecution` set to `true` can be modified. - */ - post: operations["addExecutionPropertyValue"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/executions/{id}/properties/{key}/set": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Set the value of a property of an experiment execution. - * @description Only properties with `editableInExecution` set to `true` can be modified. - */ - post: operations["setExecutionPropertyValue"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/schedules": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Create or update an experiment schedule - * @description Insert or update the experiment schedule. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. - */ - post: operations["upsertSchedule"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/schedules/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get experiment schedules for a specific experiment schedule id */ - get: operations["getSchedules"]; - put?: never; - post?: never; - /** Remove an existing experiment schedule */ - delete: operations["removeExperimentScheduleById"]; - options?: never; - head?: never; - /** - * Partially update an experiment schedule - * @description Update specific fields of an existing experiment schedule. Only non-null fields in the request body will be updated. - */ - patch: operations["patchSchedule"]; - trace?: never; - }; - "/api/experiments/schedules/v2": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get all current experiment schedule configurations */ - get: operations["getAllSchedulesV2"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/templates": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a list of all templates - * @description Get a list of all templates that exist. - */ - get: operations["getExperimentTemplates"]; - put?: never; - /** - * Create or update an experiment template - * @description Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the experiment template exists already and should be updated or newly inserted. If this template is used in a service profile, existing provided service experiments will get updated. - */ - post: operations["upsertExperimentTemplate"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/templates/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a single experiment template - * @description Get all details of a single existing experiment template. - */ - get: operations["getExperimentTemplate"]; - put?: never; - post?: never; - /** - * Delete experiment template - * @description Remove the given experiment template from the Steadybit platform. If this template is used in a service profile, it will be removed from the profile and all provided service experiments will get deleted. - */ - delete: operations["deleteExperimentTemplate"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/templates/{id}/experiment-create": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Create an experiment based on an experiment template - * @description Use the given experiment template id and the placeholder values to create or update the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. - */ - post: operations["createExperimentByTemplate"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/templates/{id}/experiment-execute": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Create an experiment based on an experiment template and run experiment - * @description Use the given experiment template id and the placeholder values to create or update and immediately run the experiment. The `externalId` will be used to identify whether the experiment exists already and should be updated or newly inserted. - */ - post: operations["saveAndRunFromTemplate"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/templates/{id}/experiment-update/{key}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Update an existing experiment based on a template - * @description Use the given experiment template id and the placeholder values to create or update the experiment. Placeholders that have been used for the initial creation will be reused. Provided placeholders from the body will overwrite existing placeholders. - */ - post: operations["updateExperimentByTemplate"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/experiments/templates/imports": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Import experiment templates - * @description Import experiment templates with given IDs from linked hub. - */ - post: operations["importFromHub"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/explore/landscape/views": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch all saved landscape views of a team - * @description Get a list of all saved explorer landscape views that belong to the given team. - */ - get: operations["getLandscapeViews"]; - put?: never; - /** - * Create a saved landscape view - * @description Create a new saved explorer landscape view for a team. - */ - post: operations["createLandscapeView"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/explore/landscape/views/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a single saved landscape view - * @description Get all details of a single saved explorer landscape view. - */ - get: operations["getLandscapeView"]; - /** - * Update a saved landscape view - * @description Update an existing saved explorer landscape view. The view's thumbnail is rendered by the UI and cannot be produced through the API. When an update changes a field that affects how the landscape renders (environment, filter query, group-by, size-by, color-by or show-advice), the thumbnail is cleared so it is not left stale; a metadata-only change (e.g. name or description) keeps it. The UI regenerates the thumbnail on its next save. - */ - put: operations["updateLandscapeView"]; - post?: never; - /** - * Delete a saved landscape view - * @description Remove the given saved explorer landscape view from the Steadybit platform. - */ - delete: operations["deleteLandscapeView"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/health": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["health"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/health/liveness": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["liveness"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/health/readiness": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["readiness"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/hubs": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a list of all hubs - * @description Get a list of all hubs that are currently connected. - */ - get: operations["getHubs"]; - put?: never; - /** - * Create or update a hub - * @description Insert or update a hub. The `id` will be used to identify whether the hub exists already and should be updated or newly inserted. The hub content can be synchronized depending on the `resync` parameter. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. - */ - post: operations["upsertHub"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/hubs/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a single hub - * @description Get all details of a single hub. - */ - get: operations["getHubById"]; - put?: never; - post?: never; - /** - * Delete a hub - * @description Remove the given hub. - */ - delete: operations["deleteHub"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/hubs/{id}/resync": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Re-synchronize a hub - * @description Fetch the latest hub definition based on `hubRepository`. This operation is executed synchronously and may take some time to complete depending on the network connection to hub address. - */ - post: operations["resyncHub"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/hubs/connection-check": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Check a hub connection - * @description Check if the given hub connection details point to a valid hub. - */ - post: operations["connectionCheck"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/integrations/preflight": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a list of preflight webhooks - * @description Get a list of all existing preflight webhooks. - */ - get: operations["getPreflightWebhooks"]; - put?: never; - /** - * Create or update a preflight webhook - * @description Insert or update a preflight webhook.
Experiment runs that were not executed due to engaged / active kill switch will not be automatically executed, they need to be triggered again. - */ - delete: operations["disengageKillswitch"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/license": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get license summary. */ - get: operations["getLicenseSummary"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/license/report": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get license report. */ - get: operations["getReport"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/preflight/actions": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get all preflight actions. */ - get: operations["getPreflightActionSummary"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/properties/associations": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get all current associations. */ - get: operations["getAssociations"]; - put?: never; - /** - * Create or update a property association - * @description Insert or update the property association. The `id` will be used to identify whether the schedule exists already and should be updated or newly inserted. - * - * Examples: - * - Assign the property `RESULT_COLOR` to all experiment designs: - * ``` - * { - * "key": "RESULT_COLOR", - * "editableInExecution": false, - * "required": true - * } - * ``` - * - Assign the property `RESULT_COLOR` to the design ADM-15: - * ``` - * { - * "key": "RESULT_COLOR", - * "editableInExecution": false, - * "experimentKey": "ADM-15", - * "required": true - * } - * ``` - * - Assign the property `RESULT_COLOR` that can be edited in each experiment execution of the experiment with key `ADM-15`: - * ``` - * { - * "key": "RESULT_COLOR", - * "editableInExecution": true, - * "experimentKey": "ADM-15", - * "required": false - * } - * ``` - * - Assign the property `RESULT_COLOR` to a service `0a2d67b9-1d5a-4179-8c32-e5296be1f56f`: - * ``` - * { - * "key": "RESULT_COLOR", - * "associationType": "SERVICE", - * "serviceId": "0a2d67b9-1d5a-4179-8c32-e5296be1f56f", - * "required": false - * } - * ``` - */ - post: operations["upsertPropertyAssociation"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/properties/associations/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get property association by a given id. */ - get: operations["getPropertyDefinition_1"]; - put?: never; - post?: never; - /** Remove an existing property association. */ - delete: operations["deletePropertyAssociation"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/properties/definitions": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["getPropertyDefinitions"]; - put?: never; - /** - * Create or update property definition - * @description Insert or update the property definition specified by the given `key`. - */ - post: operations["upsertPropertyDefinition"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/properties/definitions/{key}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get property definition for a specific property definition key. */ - get: operations["getPropertyDefinition"]; - put?: never; - post?: never; - /** Remove an existing property definition */ - delete: operations["deletePropertyDefinition"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/reports/environments": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get environment counts over time - * @description Returns the number of environments in the tenant aggregated into time buckets. - */ - post: operations["getEnvironmentCounts"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/reports/experiments/created": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get experiment creation counts over time - * @description Returns experiment creation counts aggregated into time buckets, optionally grouped by creation method or origin. - */ - post: operations["getExperimentCreations"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/reports/experiments/executed": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get experiment execution counts over time - * @description Returns experiment execution counts aggregated into time buckets, optionally grouped by state, trigger, or attack action. - */ - post: operations["getExperimentExecutions"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/reports/services/average": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get average service risk over time - * @description Returns the average risk across services aggregated into time buckets. Risk is reported as an integer 0-100. - */ - post: operations["getAverageRisk"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/reports/services/by-category": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get average service risk grouped by category over time - * @description Returns the average risk per category (key from the categoryRisks map) across services, aggregated into time buckets. - */ - post: operations["getRiskByCategory"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/reports/services/distribution": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get service risk level distribution over time - * @description Returns the count of services in each risk level (LOW, MEDIUM, HIGH) aggregated into time buckets. - */ - post: operations["getRiskDistribution"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/reports/teams": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get team counts over time - * @description Returns the number of teams in the tenant aggregated into time buckets. - */ - post: operations["getTeamCounts"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/reports/users": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get user counts over time - * @description Returns the number of users in the tenant aggregated into time buckets. - */ - post: operations["getUserCounts"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Fetch a list of services */ - get: operations["getServiceList"]; - put?: never; - /** - * Create or update service - * @description Insert or update the service specified by the given `id`. - */ - post: operations["upsertService"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get service by id. */ - get: operations["getService"]; - put?: never; - post?: never; - /** Delete an existing service */ - delete: operations["deleteService"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{id}/experiments": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get experiments associated to an service. */ - get: operations["getServiceExperiments"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{id}/experiments/custom": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Link a custom experiment to a service. */ - post: operations["linkCustomExperiment"]; - /** Remove a linked custom experiment from a service. */ - delete: operations["unlinkCustomExperiment"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{id}/experiments/provided": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Create or update a provided experiment. */ - post: operations["upsertProvidedExperiment"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{id}/risk": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get the risk score for a service */ - get: operations["getRisk"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/{id}/variables": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get service variables - * @description Get all variables owned by the service. - */ - get: operations["getServiceVariables"]; - /** - * Replace all service variables - * @description All provided variables will be associated with the given service and existing ones removed. - */ - put: operations["setServiceVariables"]; - post?: never; - delete?: never; - options?: never; - head?: never; - /** - * Add / merge service variables - * @description All provided variables will be associated with the given service.
If a variable key is already in use, its value is updated.
If a variable is already associated but not provided, it continues to exist. - */ - patch: operations["mergeServiceVariables"]; - trace?: never; - }; - "/api/services/profiles": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Fetch a list of service profiles */ - get: operations["getProfiles"]; - put?: never; - /** - * Create or update service profile - * @description Insert or update the service profile specified by the given `id`. - */ - post: operations["upsertProfile"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/services/profiles/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get service profile by id. */ - get: operations["getProfile"]; - put?: never; - post?: never; - /** Delete an existing service profile */ - delete: operations["deleteProfile"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/target-stats": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Gather target statistics without any filters */ - get: operations["getTargetsStats"]; - put?: never; - /** Gather target statistics for a given predicate or query */ - post: operations["getTargetsStats_1"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/targets": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get targets - * @description Get targets. - */ - get: operations["getTargets"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/targets/attributes/keys": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get attribute key - * @description Get all available attribute keys for a specific target type in a given environment. - */ - get: operations["getTargetAttributeKeys"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/targets/attributes/values": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get attribute values - * @description Get all available attribute values for a specific attribute and target type in a given environment. - */ - get: operations["getTargetAttributeValues"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/teams": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a list of all teams - * @description Get a list of all teams that exist.
If used with a team-associated `accessToken` and `onlyAccessible` is set to `true` you only get the team of the `accessToken`. - */ - get: operations["getTeams"]; - put?: never; - /** - * Create or update a team - * @description Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. If a provided member's username or email is not yet known it will be skipped. - */ - post: operations["upsertTeam"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/teams/{key}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Fetch a single team - * @description Get all details of a single existing teams. - */ - get: operations["getTeam"]; - put?: never; - post?: never; - /** - * Delete team - * @description Remove the given team from the Steadybit platform. This will only work, if there are no experiments running at the moment. - */ - delete: operations["deleteTeam"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/teams/{key}/environments": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get all environments assigned to the team - * @description Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). - */ - get: operations["getTeamEnvironments"]; - /** - * Update the environments of a specific team - * @description The allowed environments of the specified team will be updated with these provided. New environments will be added to the team, environments not provided in the request will be removed from the team. - */ - put: operations["setTeamEnvironments"]; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/teams/{key}/environments/add": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Add an allowed environment to a team - * @description The given environments will be added to the specified team. - */ - post: operations["addTeamEnvironments"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/teams/{key}/environments/remove": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Remove allowed environment from a team - * @description The given environments will be removed from the specified team. - */ - post: operations["removeTeamEnvironments"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/teams/{key}/members": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get all members being part of the team - * @description Get a list of members that are part of the specified team. The list contains the username, being a Steadybit user id, and the role in this particular team (owner or member). - */ - get: operations["getTeamMembers"]; - /** - * Update the members of a specific team - * @description The members of the specified team will be updated with these provided. New team members will be added to the team, team members not provided in the request will be removed from the team. - */ - put: operations["setTeamMembers"]; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/teams/{key}/members/add": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Add team members to a team - * @description The given members will be added to the specified team. - */ - post: operations["addTeamMembers"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/teams/{key}/members/remove": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Remove team members from a team - * @description The given members will be removed from the specified team. However, they are still able to login, view the content of the team and may still be member of another team. - */ - post: operations["removeTeamMembers"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/api/users/invite": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Invite users to a tenant - * @description Invite users to a tenant. The invited users will receive an email with an invitation link to join the tenant. - */ - post: operations["inviteUser"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; -} -export type webhooks = Record; -export interface components { - schemas: { - /** - * @description A step that is executed as part of an experiment. - * @example [ - * { - * "id": "40b0f797-912d-4256-8887-1553561962a9", - * "predecessorId": null, - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * } - * ] - */ - AbstractExperimentExecutionStepAO: { - /** - * @description Custom label assigned during experiment design to express the intention of this step - * @example Container 'xyz' can not be reached - */ - customLabel?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: uuid - * @description Unique identifier of this step execution - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - id?: string; - /** - * @description Whether the experiment should fail/error immediately in case this step fails/errors. - * @example false - */ - ignoreFailure?: boolean; - /** - * @description Step-specific parameters of the experiment step configuration - * @example { - * "duration": "10s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - /** - * Format: uuid - * @description Unique identifier of the step execution that precedes this step, null if it is the first step of a lane - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - predecessorId?: string; - /** - * @description Reason in case this experiment step execution failed or errored - * @example Couldn't read state of container... - */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step was started - * @example 2023-01-01T09:00:00Z - */ - started?: string; - /** - * @description Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** - * @description Type of this step execution (e.g. ACTION, WAIT) - * @example ACTION - */ - stepType: string; - } & (components["schemas"]["ExperimentExecutionStepActionAO"] | components["schemas"]["ExperimentExecutionStepWaitAO"] | components["schemas"]["ExperimentExecutionStepServiceValidationAO"]); - /** - * @description A step that is executed as part of an experiment. - * @example [ - * { - * "id": "40b0f797-912d-4256-8887-1553561962a9", - * "predecessorId": null, - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * } - * ] - */ - AbstractWebhookPayloadExecutionStepAO: { - /** - * @description Custom label assigned during experiment design to express the intention of this step - * @example Container 'xyz' can not be reached - */ - customLabel?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: uuid - * @description Unique identifier of this step execution - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - id?: string; - /** - * @description Whether the experiment should fail/error immediately in case this step fails/errors. - * @example false - */ - ignoreFailure?: boolean; - /** - * @description Step-specific parameters of the experiment step configuration - * @example { - * "duration": "10s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - /** - * Format: uuid - * @description Unique identifier of the step execution that precedes this step, null if it is the first step of a lane - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - predecessorId?: string; - /** - * @description Reason in case this experiment step execution failed or errored - * @example Couldn't read state of container... - */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step was started - * @example 2023-01-01T09:00:00Z - */ - started?: string; - /** - * @description Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - } & (components["schemas"]["WebhookPayloadExecutionStepWaitAO"] | components["schemas"]["WebhookPayloadExecutionStepActionAO"] | components["schemas"]["WebhookPayloadExecutionStepServiceValidationAO"]); - /** - * @description The logged event was performed via API authorized via access token - * @example { - * "id": "VDKTEBLl", - * "name": "CI/CD", - * "tokenType": "TEAM", - * "principalType": "ACCESS_TOKEN" - * } - */ - AccessTokenPrincipalAL: { - /** - * @description Unique identifier of this access token principal - * @example VDKTEBLl - */ - id: string; - /** - * @description Name of the access token that was used - * @example CI/CD - */ - name: string; - /** - * @description Principal type for access token based principal - * @example ACCESS_TOKEN - * @enum {string} - */ - principalType: "USER" | "ACCESS_TOKEN" | "BATCH_JOB"; - /** - * @description Access token type that was used to perform the logged event - * @example TEAM - * @enum {string} - */ - tokenType: "ADMIN" | "TEAM" | "WILDCARD"; - }; - /** - * @description A single access token of a team. The token itself can't be read again - * @example { - * "id": "aP4cDVfA", - * "name": "CI/CD" - * } - */ - AccessTokensPageItemAO: { - /** - * @description Unique identifier of the access token - * @example CQer2Oar - */ - id?: string; - /** - * @description Name of the Access Token to document e.g. its purpose - * @example CI/CD access token - */ - name?: string; - /** - * @description Team associated with this token or null if this is an admin token - * @example ADM - */ - team?: string | null; - /** - * @description Type of the access token - * @example ADMIN - * @enum {string} - */ - type?: "ADMIN" | "TEAM"; - }; - /** @description A single access token */ - AccessTokensPageItemV2AO: { - /** - * Format: date-time - * @description Expiration date of the token. Null means the token never expires. - */ - expiresAt?: string; - /** @description Unique identifier of this access token */ - id?: string; - /** - * Format: date-time - * @description Date of the last token usage. Null means the token was never used. - */ - lastUsed?: string; - /** @description Name of this access token */ - name?: string; - /** @description Teams associated with this token. */ - teams?: string[]; - /** - * @description Type of this token - * @enum {string} - */ - type?: "ADMIN" | "TEAM" | "WILDCARD"; - }; - /** @description An action that is currently registered. */ - ActionAO: { - /** - * @description Category grouping similar actions. - * @example Resource - */ - category?: string; - defaultBlastRadius: components["schemas"]["DefaultBlastRadiusAO"]; - /** @description Description of the action. */ - description: string; - hint?: components["schemas"]["HintAO"]; - hubSummary?: string | null; - /** @description Icon of the action as a data URI (may be a large base64-encoded image). */ - icon?: string | null; - /** - * @description Unique identifier of the action. - * @example com.steadybit.extension_container.stress_cpu - */ - id: string; - /** - * @description Kind of the action. - * @example ATTACK - * @enum {string} - */ - kind: "ATTACK" | "CHECK" | "LOAD_TEST" | "OTHER" | "BASIC"; - /** @description Parameters that describe how to fetch metrics for this action. */ - metricQueryParameters: components["schemas"]["ParameterAO"][]; - /** - * @description Behavior when the target query does not match any targets. - * @example INCLUDE_NONE - * @enum {string} - */ - missingQuerySelection: "INCLUDE_ALL" | "INCLUDE_NONE"; - /** - * @description Display name of the action. - * @example Stress CPU - */ - name: string; - parameters?: components["schemas"]["ParameterAO"][]; - /** - * @description Restriction on the number of targets this action may operate on. - * @example NONE - * @enum {string} - */ - quantityRestriction: "EXACTLY_ONE" | "ALL" | "NONE"; - /** @description Whether this action supports metric queries. */ - supportsMetricQueries?: boolean; - target?: components["schemas"]["TargetSelectorAO"]; - /** @description Predefined target predicate templates offered to the user when configuring this action. */ - targetPredicateTemplates: components["schemas"]["TargetPredicateTemplateAO"][]; - /** - * @description Technology the action belongs to. - * @example Container - */ - technology?: string; - /** - * @description Version of the action. - * @example 1.2.3 - */ - version?: string; - }; - /** @description List of actions. */ - ActionSummariesAO: { - /** @description List of actions. */ - actions?: components["schemas"]["ActionAO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - /** - * @description Add a value to a list property of an execution - * @example { - * "type": "add_value_to_list_property", - * "propertyKey": "observations", - * "value": "This looks interesting!" - * } - */ - AddValueToListProperty: { - type: "AddValueToListProperty"; - } & (Omit & { - /** - * @description The key of the property. - * @example observations - */ - propertyKey: string; - /** - * @description The value that should be added to the list. Number in case of a numeric list, String otherwise - * @example This looks interesting! - */ - value: Record; - }); - AdvancedRadiusAO: { - /** - * @description The target attribute that should be picked randomly - * @example aws.zone - */ - attribute: string; - /** - * @description Only in execution - the values that has been picked by the randomizer for the given execution - * @example ['us-east-1a','us-east-1b'] - */ - pickedValues?: string[] | null; - /** - * @description The percentage (example: `50%`) or fixed amount (example: `15#`) - * @example 50% - */ - value: string; - }; - /** - * @description A pageable list of pieces of advice. - * @example { - * "totalItems": 108, - * "nextOffset": 3, - * "items": [ - * { - * "target": { - * "type": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "reference": "prod-demo/steadybit-demo/gateway", - * "label": "gateway" - * }, - * "advice": { - * "type": "com.steadybit.extension_kubernetes.advice.k8s-cpu-limit", - * "label": "Limit CPU Resources", - * "tags": [ - * "kubernetes", - * "limit", - * "cpu" - * ], - * "status": "Validation needed", - * "summary": "You already took action and configured a CPU limit. Validate your configuration via an experiment." - * }, - * "url": "https://platform.steadybit.com/permalink/advice/eyAiZW52..." - * }, - * { - * "target": { - * "type": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "reference": "prod-demo/steadybit-demo/gateway", - * "label": "gateway" - * }, - * "advice": { - * "type": "com.steadybit.extension_kubernetes.advice.k8s-cpu-request", - * "label": "Requesting Reasonable CPU Resources", - * "tags": [ - * "kubernetes", - * "request", - * "cpu" - * ], - * "status": "Validation needed", - * "summary": "You specified a CPU request that informs Kubernetes decision where to schedule your pods of *activemq*.\nPlease confirm that your requested CPU share is reasonable for your type of application." - * }, - * "url": "https://platform.steadybit.com/permalink/advice/eyAiZW52..." - * } - * ] - * } - */ - AdviceSummaryAO: { - items?: components["schemas"]["TargetAdviceAO"][]; - /** - * Format: int32 - * @description Next queryable offset to query for next batch of advice - * @example 21 - */ - nextOffset?: number | null; - /** - * Format: int64 - * @description Total amount of advice matching your query - * @example 241 - */ - totalItems?: number; - }; - /** - * @description An attributes (key-value-pair) that is associated to a target - * @example { - * "key": "container.port", - * "value": "51152:2376" - * } - */ - Attribute: { - /** - * @description The key of the attribute, may be associated multiple times to the same target - * @example container.engine - */ - key: string; - /** - * @description The value of the attribute - * @example docker - */ - value: string; - }; - /** - * @description An attributes (key-value-pair) that is associated to a target - * @example { - * "key": "container.port", - * "value": "51152:2376" - * } - */ - AttributeAO: { - /** - * @description The key of the attribute, may be associated multiple times to the same target - * @example container.engine - */ - key: string; - /** - * @description The value of the attribute - * @example docker - */ - value: string; - }; - /** - * @description Audit log entry. - * @example { - * "id": "14av1421-aol3-4159-8ae2-47f5a9ba119e", - * "tenant": { - * "key": "Demo", - * "name": "Demo Tenant" - * }, - * "trigger": { - * "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", - * "triggerType": "HTTP_REQUEST" - * }, - * "eventName": "experiment.created", - * "eventTime": "2023-01-03T09:13:00Z", - * "experiment": { - * "key": "ADM-1" - * } - * } - */ - AuditLogEntry: { - environment?: components["schemas"]["EnvironmentAL"]; - /** - * @description Event name that was audited - * @example experiment.created - */ - eventName: string; - /** - * Format: date-time - * @description The time at which the event was audited - * @example 2023-01-03T09:13:00Z - */ - eventTime: string; - /** - * Format: uuid - * @description Unique identifier of the audit log entry - */ - id: string; - principal?: components["schemas"]["PrincipalAL"]; - team?: components["schemas"]["TeamAL"]; - tenant: components["schemas"]["TenantAL"]; - trigger?: components["schemas"]["AuditLogTrigger"]; - }; - /** - * @description The trigger that caused the event to happen - * @example { - * "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", - * "triggerType": "HTTP_REQUEST" - * } - */ - AuditLogTrigger: { - triggerType: string; - } | null; - /** - * @description A single step in a lane. - * @example { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - */ - BaseExperimentStepAO: { - customLabel?: string; - /** - * @description Ignore any errors and failures of this single step and continue the execution of an experiment run - * @example false - */ - ignoreFailure?: boolean; - /** @description Optional metric checks used to define success or failure of this step */ - metricChecks?: components["schemas"]["MetricCheckAO"][]; - /** @description Optional metric queries used of this step to filter e.g. monitoring data */ - metricQueries?: components["schemas"]["MetricQueryAO"][]; - /** - * @description Configuration parameters of this step that are saved during experiment design and evaluated at execution time. - * @example { - * "duration": "30s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - type: string; - } & (components["schemas"]["ExperimentStepActionAO"] | components["schemas"]["ExperimentStepWaitAO"] | components["schemas"]["ExperimentStepServiceValidationAO"]); - /** - * @description A batch job has performed the logged event - * @example { - * "username": "af1bw7kj-d299-47ab-998f-c2a53b433820", - * "principalType": "BATCH_JOB" - * } - */ - BatchPrincipalAL: { - /** - * @description Principal type for batch based principal - * @example BATCH_JOB - * @enum {string} - */ - principalType: "USER" | "ACCESS_TOKEN" | "BATCH_JOB"; - /** - * @description Username of the user, internal identifier of Steadybit - * @example 13av2737-b318-4048-a79d-4789d645bc31 - */ - username?: string; - }; - /** - * @description Blast radius that is applied to define the set of targets as well as an optional random subset - * @example { - * "targetType": "com.steadybit.extension_container.container", - * "percentage": 50, - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "container.host/name", - * "operator": "EQUALS", - * "values": [ - * "docker-desktop/minikube" - * ] - * } - * ] - * } - * } - */ - BlastRadiusAO: { - /** - * Format: int32 - * @description In case a fixed number of as subset of specified targets should be effected - * @example 2 - */ - maximum?: number; - /** - * Format: int32 - * @description In case a percentage subset of the specified targets should be effected - * @example 40 - */ - percentage?: number; - predicate?: components["schemas"]["TargetPredicateAO"]; - /** - * @description Target type that is effected by that action - * @example container - */ - targetType?: string; - }; - /** - * @description The risk for a given category in a service - * @example { - * "total": 50, - * "experiment": 50, - * "advice": 50 - * } - */ - CategoryRiskAO: { - /** - * Format: int32 - * @description The advice risk for this category, or null when no advice could be found for the given target selection - */ - advice?: number; - /** - * Format: int32 - * @description The experiment risk for this category - */ - experiment?: number; - /** - * Format: int32 - * @description The total risk for this category - */ - total?: number; - }; - ComparableValueAO: { - type?: string; - }; - CreateAccessTokenRequestAO: { - /** - * @description Name of the Access Token to document its purpose - * @example CI/CD access token - */ - name: string; - /** - * @description Team associated with this token or null if this is an admin token - * @example ADM - */ - team?: string | null; - /** - * @description Type of this token. - * @example TEAM - * @enum {string} - */ - type: "ADMIN" | "TEAM"; - }; - CreateAccessTokenRequestV2AO: { - /** - * Format: date-time - * @description Expiration date of the token. If not set, the token will never expire. - * @example 2027-01-01T00:00:00Z - */ - expiresAt?: string | null; - /** - * @description Name of the Access Token to document its purpose - * @example CI/CD access token - */ - name: string; - /** - * @description Keys of teams to associate with this token. Required when type TEAM, must be empty for type ADMIN. - * @example [ - * "ADM", - * "DEV" - * ] - */ - teams?: string[] | null; - /** - * @description Type of this token. - * @example TEAM - * @enum {string} - */ - type: "ADMIN" | "TEAM" | "WILDCARD"; - }; - CreateAccessTokenResponseAO: { - /** - * @description Unique identifier of the access token - * @example CQer2Oar - */ - id?: string; - /** - * @description Token to be used to authenticate in the API.
Make sure to save the generated token as you can't read it again afterwards for security-reasons. - * @example a1fDXcA0.P.2dFfGl3fAq126mnVCxyPZLoEmLwPi2 - */ - token?: string; - }; - CreateAccessTokenResponseV2AO: { - /** @description Unique identifier of this access token */ - id?: string; - /** @description The access token. Make sure to save it as you can't read it again afterwards for security-reasons. */ - token?: string; - }; - /** - * @description Create or update the experiment with the given experiment design. - * @example { - * "name": "Blackhole Hot-deals", - * "team": "ADM", - * "environment": "Global", - * "lanes": [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ], - * "properties": { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - * } - */ - CreateAndRunExperimentAO: { - /** - * @description The name of the environment to be used - * @example Global - */ - environment?: string; - /** - * @description Variables that will be merged for the single experiment execution with the variables defined of the experiment or environment that the experiment will be executed in. A `key` that exists already in the experiment or environment variables will be overridden for this execution, all others will be added solely in the context of the first experiment execution. Each value is either a constant string, an array of constant strings, or a select expression object. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - executionVariables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * @description Variables that will be used when the experiment will be executed. Experiment variables will override existing environment variables. Each value is either a constant string, an array of constant strings, or a select expression object (`{"type":"select",...}`). - * @example { - * "httpEndpoint": "http://dev.shop.products.internal", - * "targetServices": [ - * "gateway", - * "hot-deals", - * "fashion-bestseller" - * ], - * "httpEndpointZones": { - * "type": "select", - * "targetType": "com.steadybit.extension_container.container", - * "attribute": "aws.zone", - * "mode": "fixed", - * "count": 2 - * } - * } - */ - experimentVariables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * @description An optional external identifier used for create-or-update semantics. - * @example 1234567 - */ - externalId?: string; - /** - * @deprecated - * @description An optional external reference. Will be removed and is replaced by tags. If used with experiment creation, the value will be added as a tag. - * @example INCIDENT-4711 - */ - externalReference?: string; - /** - * @description The hypothesis that is validated by the experiment - * @example System is able to survive a latency in the network of 1500ms - */ - hypothesis?: string; - /** - * @description The lanes (steps executed in parallel) in the experiment. Each lane consists of multiple steps that are executed sequential per lane. - * @example [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ] - */ - lanes: components["schemas"]["ExperimentLaneAO"][]; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name: string; - /** - * @description The properties of the experiment - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description Team keys with which the experiment is shared with - * @example [OPS, SHOP] - */ - sharedTeams?: string[]; - /** - * @description An optional set of tags you can use to search for. - * @example [ - * "myTag", - * "myOtherTag" - * ] - */ - tags?: string[]; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - }; - /** - * @description Create or update an experiment based on an experiment template and run it. - * @example { - * "environment": "steadybit-demo", - * "team": "DEMO", - * "placeholders": [ - * { - * "key": "CLUSTER", - * "value": "demo-cluster" - * }, - * { - * "key": "BOOL", - * "value": true - * }, - * { - * "key": "NUMBER", - * "value": 15 - * }, - * { - * "key": "KEYVALUE", - * "value": [ - * { - * "key": "example-a", - * "value": "abc" - * }, - * { - * "key": "example-b", - * "value": "123" - * } - * ] - * }, - * { - * "key": "LIST", - * "value": [ - * "entry1", - * "entry2", - * "entry3" - * ] - * }, - * { - * "key": "FILE", - * "value": { - * "fileName": "example.txt", - * "data": "SGVsbG8gV29ybGQh" - * } - * } - * ], - * "externalId": "1234567", - * "executionVariables": { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - * } - */ - CreateAndRunExperimentFromTemplateAO: { - /** - * @description The name of the environment to be used - * @example Global - */ - environment?: string; - /** - * @description Variables that will be merged for the single experiment execution with the variables defined of the experiment or environment that the experiment will be executed in. A `key` that exists already in the experiment or environment variables will be overridden for this execution, all others will be added solely in the context of the first experiment execution. Each value is either a constant string, an array of constant strings, or a select expression object. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - executionVariables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * @description Variables that will be added to the created experiment design. A `key` that exists already in the environment variables will be overridden. Each value is either a constant string, an array of constant strings, or a select expression object. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - experimentVariables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * @description An optional external identifier used for create-or-update semantics. - * @example 1234567 - */ - externalId?: string; - /** @description List of template placeholder values */ - placeholders?: components["schemas"]["ExperimentTemplatePlaceholderValueAO"][]; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - }; - /** - * @description Create or update the experiment with the given experiment design. - * @example { - * "name": "Blackhole Hot-deals", - * "team": "ADM", - * "environment": "Global", - * "sharedTeams": [ - * "SHOP" - * ], - * "lanes": [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ], - * "properties": { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - * } - */ - CreateExperimentAO: { - /** - * @description The name of the environment to be used - * @example Global - */ - environment?: string; - /** - * @description Variables that will be used when the experiment will be executed. Experiment variables will override existing environment variables. Each value is either a constant string, an array of constant strings, or a select expression object (`{"type":"select",...}`). - * @example { - * "httpEndpoint": "http://dev.shop.products.internal", - * "targetServices": [ - * "gateway", - * "hot-deals", - * "fashion-bestseller" - * ], - * "httpEndpointZones": { - * "type": "select", - * "targetType": "com.steadybit.extension_container.container", - * "attribute": "aws.zone", - * "mode": "fixed", - * "count": 2 - * } - * } - */ - experimentVariables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * @description An optional external identifier used for create-or-update semantics. - * @example 1234567 - */ - externalId?: string; - /** - * @deprecated - * @description An optional external reference. Will be removed and is replaced by tags. If used with experiment creation, the value will be added as a tag. - * @example INCIDENT-4711 - */ - externalReference?: string; - /** - * @description The hypothesis that is validated by the experiment - * @example System is able to survive a latency in the network of 1500ms - */ - hypothesis?: string; - /** - * @description The lanes (steps executed in parallel) in the experiment. Each lane consists of multiple steps that are executed sequential per lane. - * @example [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ] - */ - lanes: components["schemas"]["ExperimentLaneAO"][]; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name: string; - /** - * @description The properties of the experiment - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description Team keys with which the experiment is shared with - * @example [OPS, SHOP] - */ - sharedTeams?: string[]; - /** - * @description An optional set of tags you can use to search for. - * @example [ - * "myTag", - * "myOtherTag" - * ] - */ - tags?: string[]; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - }; - /** - * @description Create or update an experiment based on an experiment template. - * @example { - * "environment": "steadybit-demo", - * "team": "DEMO", - * "placeholders": [ - * { - * "key": "CLUSTER", - * "value": "demo-cluster" - * }, - * { - * "key": "BOOL", - * "value": true - * }, - * { - * "key": "NUMBER", - * "value": 15 - * }, - * { - * "key": "KEYVALUE", - * "value": [ - * { - * "key": "example-a", - * "value": "abc" - * }, - * { - * "key": "example-b", - * "value": "123" - * } - * ] - * }, - * { - * "key": "LIST", - * "value": [ - * "entry1", - * "entry2", - * "entry3" - * ] - * }, - * { - * "key": "FILE", - * "value": { - * "fileName": "example.txt", - * "data": "SGVsbG8gV29ybGQh" - * } - * } - * ], - * "externalId": "1234567", - * "experimentVariables": { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - * } - */ - CreateExperimentFromTemplateAO: { - /** - * @description The name of the environment to be used - * @example Global - */ - environment?: string; - /** - * @description Variables that will be added to the created experiment design. A `key` that exists already in the environment variables will be overridden. Each value is either a constant string, an array of constant strings, or a select expression object. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - experimentVariables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * @description An optional external identifier used for create-or-update semantics. - * @example 1234567 - */ - externalId?: string; - /** @description List of template placeholder values */ - placeholders?: components["schemas"]["ExperimentTemplatePlaceholderValueAO"][]; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - }; - CursorSliceResponseAOTargetAO: { - /** - * @description Are there more items that can be fetched with the given nextCursor? - * @example true - */ - hasNext?: boolean; - items?: components["schemas"]["TargetAO"][]; - /** - * @description The cursor to use to fetch the next page - * @example eyJhZ2VudElkIjogImFnZW50LTEyMyIsICJuYW1lIjogImRlcGxveW1lbnQtYSIsICJ0eXBlIjogImNvbS5zdGVhZHliaXQuZXh0ZW5zaW9uX2t1YmVybmV0ZXMua3ViZXJuZXRlcy1kZXBsb3ltZW50In0= - */ - nextCursor?: string | null; - }; - CustomWebhookAO: { - /** - * @description The events that are being sent or a list containing a single `*` if all supported event types should be used. - * - * Supported Events: - * - "experiment.execution.requested" - * - "experiment.execution.created" - * - "experiment.execution.preflight" - * - "experiment.execution.completed" - * - "experiment.execution.failed" - * - "experiment.execution.errored" - * - "experiment.execution.canceled" - * - "experiment.execution.step-started" - * - "experiment.execution.step-completed" - * - "experiment.execution.step-failed" - * - "experiment.execution.step-errored" - * - "experiment.execution.step-canceled" - * - "experiment.execution.step-skipped" - * - "killswitch.engaged" - * - "killswitch.disengaged" - * @example [ - * "experiment.execution.created", - * "experiment.execution.completed" - * ] - */ - events: string[]; - /** - * @description Additional headers to include in the webhook request. - * @example { - * "X-Custom-Header": "CustomValue", - * "X-Another-Header": "AnotherValue" - * } - */ - headers?: { - [key: string]: string; - }; - /** - * Format: uuid - * @description The id of the webhook - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - /** - * @description The name of the webhook - * @example Custom Webhook - */ - name: string; - /** - * @description The scope of the webhook / integration - * @example TEAM - * @enum {string} - */ - scope: "GLOBAL" | "TEAM"; - /** - * @description If a secret is provided a signature of the body is computed using `HMAC SHA-256` and sent as `X-SB-Signature` http header. You can use this header to verify the message. - * @example secret123!! - */ - secret?: string; - /** - * @description The body size can get very large as we include all target attributes for each target of your experiments. When having experiments with many targets, it might be useful to filter the attributes to only include the ones you are interested in. You can use the wildcard character '*' to match all attributes or a comma-separated-list of attribute-names. If the field is empty, no attributes will be included. - * @example [ - * "k8s.cluster-name", - * "k8s.deployment" - * ] - */ - targetAttributeIncludes: string[]; - /** - * @description The key of the team if the scope is `TEAM` - * @example ADM - */ - team?: string; - /** - * @description The URL of the webhook - * @example https://example.com/webhook - */ - url: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version: number; - }; - CustomWebhookUpsertAO: { - /** - * @description The events that are being sent or a list containing a single `*` if all supported event types should be used. - * - * Supported Events: - * - "experiment.execution.requested" - * - "experiment.execution.created" - * - "experiment.execution.preflight" - * - "experiment.execution.completed" - * - "experiment.execution.failed" - * - "experiment.execution.errored" - * - "experiment.execution.canceled" - * - "experiment.execution.step-started" - * - "experiment.execution.step-completed" - * - "experiment.execution.step-failed" - * - "experiment.execution.step-errored" - * - "experiment.execution.step-canceled" - * - "experiment.execution.step-skipped" - * - "killswitch.engaged" - * - "killswitch.disengaged" - * @example [ - * "experiment.execution.created", - * "experiment.execution.completed" - * ] - */ - events: string[]; - /** - * @description Additional headers to include in the webhook request. - * @example { - * "X-Custom-Header": "CustomValue", - * "X-Another-Header": "AnotherValue" - * } - */ - headers?: { - [key: string]: string; - }; - /** - * Format: uuid - * @description The id of the webhook or null if a new webhook should be created. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id?: string | null; - /** - * @description The name of the webhook - * @example Custom Webhook - */ - name: string; - /** - * @description The scope of the webhook / integration - * @example TEAM - * @enum {string} - */ - scope: "GLOBAL" | "TEAM"; - /** - * @description If a secret is provided a signature of the body is computed using `HMAC SHA-256` and sent as `X-SB-Signature` http header. You can use this header to verify the message. - * @example secret123!! - */ - secret?: string; - /** - * @description The body size can get very large as we include all target attributes for each target of your experiments. When having experiments with many targets, it might be useful to filter the attributes to only include the ones you are interested in. You can use the wildcard character '*' to match all attributes or a comma-separated-list of attribute-names. If the field is empty, no attributes will be included. - * @example [ - * "k8s.cluster-name", - * "k8s.deployment" - * ] - */ - targetAttributeIncludes: string[]; - /** - * @description The key of the team if the scope is `TEAM` - * @example ADM - */ - team?: string; - /** - * @description The URL of the webhook - * @example https://example.com/webhook - */ - url: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** @description Default blast radius configuration for an action. */ - DefaultBlastRadiusAO: { - /** - * @description Mode of the default blast radius. - * @example PERCENTAGE - */ - mode: string; - /** - * Format: int32 - * @description Value for the mode. Percentage (0-100) when mode is PERCENTAGE, max target count when mode is MAXIMUM. - * @example 100 - */ - value?: number; - }; - /** - * @description The environment in which the event was triggered - * @example { - * "id": "1avfd231-8322-42f2-bad9-307dc962ec37", - * "name": "Global", - * "predicate": { - * "operator": "AND", - * "predicates": [] - * } - * } - */ - EnvironmentAL: { - /** Format: uuid */ - id: string; - name: string; - predicate: components["schemas"]["TargetPredicateAO"]; - } | null; - /** - * @description An environment for limiting the access to discovered systems for a team. - * @example { - * "id": "2v1av42-e525-4c00-a13a-1ac32d170724", - * "name": "Global", - * "version": 0, - * "query": "aws.account=\"123\" OR aws.account=\"456\"", - * "state": "READY" - * } - */ - EnvironmentAO: { - /** - * Format: uuid - * @description Unique identifier of a environment - */ - id?: string; - /** - * @description Name of the environment. - * @example Global - */ - name: string; - predicate: components["schemas"]["TargetPredicateAO"]; - /** - * @description Alternative to `predicate`. If both `query` and `predicate` will be provided, `query` will override the `predicate`. - * @example (aws.account="123" OR aws.account="456" - */ - query?: string | null; - /** - * @description State of the environment to indicate current background tasks. - * @example "READY" - * @enum {string} - */ - state: "CREATED" | "UPDATED" | "READY" | "ERROR" | "UNKNOWN"; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number; - }; - /** - * @description List of environments. - * @example { - * "environments": [ - * { - * "id": "2v1av42-e525-4c00-a13a-1ac32d170724", - * "name": "Global", - * "version": 0, - * "query": "aws.account=\"123\" OR aws.account=\"456\"", - * "state": "READY" - * } - * ] - * } - */ - EnvironmentSummariesAO: { - environments?: components["schemas"]["EnvironmentAO"][]; - }; - /** - * @description Experiment execution data that should be used only for that specific experiment execution and will not update the experiment design. - * @example {} - */ - ExecuteExperimentRequestAO: { - /** - * @description The name of the environment with which the experiment execution should be overridden once and executed in - * @example Shop Stage - */ - environment?: string; - /** - * @description Variables that will be merged for the single experiment execution with the variables defined of the experiment or environment that the experiment will be executed in. A `key` that exists already in the experiment or environment variables will be overridden for this execution, all others will be added solely in the context of this experiment execution. Existing variables don't have to be repeated in this parameter. Each value is either a constant string, an array of constant strings, or a select expression object. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - variables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - } | null; - /** - * @description A single experiment execution that was triggered from a single experiment. - * @example { - * "id": 58070, - * "key": "SHOP-1" - * "apiLocation": "https://api.steadybit.com/experiments/execute/SHOP-1", - * "uiLocation": "https://platform.steadybit.com/experiments/edit/SHOP-1/executions/1234" - * } - */ - ExecuteExperimentResponseAO: { - /** - * @description A link to the API for the experiment execution - * @example https://api.steadybit.com/experiments/execute/SHOP-1 - */ - apiLocation: string; - /** - * Format: int64 - * @description Unique experiment execution id that identifies this specific experiment execution - * @example 1234 - */ - executionId?: number; - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example SHOP-1 - */ - key: string; - /** - * @description A link to the UI for the experiment execution - * @example https://platform.steadybit.com/experiments/edit/SHOP-1/executions/1234 - */ - uiLocation: string; - }; - /** - * @description Modifications that should be applied to the execution if the preflight check was successful. - * @example [ - * { - * "type": "set_property_value", - * "propertyKey": "approvedBy", - * "value": "Daniel" - * }, - * { - * "type": "add_value_to_list_property", - * "propertyKey": "observations", - * "value": "This looks interesting!" - * } - * ] - */ - ExecutionModification: { - type: string; - }; - /** - * @example { - * "key": "ADM-2", - * "name": "Blackhole Hot-deals", - * "team": "ADM", - * "sharedTeams": [ - * "SHOP" - * ], - * "environment": "Global", - * "created": "2023-05-03T08:24:30.183237Z", - * "createdBy": { - * "username": "ag1hb7ap-d299-47ab-998f-c2a53b433820", - * "name": "Manuel", - * "pictureUrl": "https://.../picture.png" - * }, - * "edited": "2023-05-03T13:31:12.533664Z", - * "editedBy": { - * "username": "ag1hb7ap-d299-47ab-998f-c2a53b433820", - * "name": "Manuel", - * "pictureUrl": "https://.../picture.png" - * }, - * "lanes": [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ], - * "properties": { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - * } - */ - ExperimentAO: { - /** - * Format: date-time - * @description Timestamp when the experiment was created - * @example 2023-01-01T09:00:00Z - */ - created: string; - createdBy: components["schemas"]["UserSummaryAO"]; - /** - * Format: date-time - * @description Timestamp when the experiment was edited the last time - * @example 2023-01-01T09:00:00Z - */ - edited: string; - editedBy: components["schemas"]["UserSummaryAO"]; - /** - * @description The name of the environment to be used - * @example Global - */ - environment?: string; - /** - * @description Variables that will be used when the experiment will be executed. Experiment variables will override existing environment variables. Each value is either a constant string, an array of constant strings, or a select expression object (`{"type":"select",...}`). - * @example { - * "httpEndpoint": "http://dev.shop.products.internal", - * "targetServices": [ - * "gateway", - * "hot-deals", - * "fashion-bestseller" - * ], - * "httpEndpointZones": { - * "type": "select", - * "targetType": "com.steadybit.extension_container.container", - * "attribute": "aws.zone", - * "mode": "fixed", - * "count": 2 - * } - * } - */ - experimentVariables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * @description An optional external identifier used for create-or-update semantics. - * @example 1234567 - */ - externalId?: string; - /** - * @deprecated - * @description An optional external reference. Will be removed and is replaced by tags. If used with experiment creation, the value will be added as a tag. - * @example INCIDENT-4711 - */ - externalReference?: string; - /** - * @description The hypothesis that is validated by the experiment - * @example System is able to survive a latency in the network of 1500ms - */ - hypothesis?: string; - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - key: string; - /** - * @description The lanes (steps executed in parallel) in the experiment. Each lane consists of multiple steps that are executed sequential per lane. - * @example [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ] - */ - lanes: components["schemas"]["ExperimentLaneAO"][]; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name: string; - /** - * @description The properties of the experiment - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description Team keys with which the experiment is shared with - * @example [OPS, SHOP] - */ - sharedTeams?: string[]; - /** - * @description An optional set of tags you can use to search for. - * @example [ - * "myTag", - * "myOtherTag" - * ] - */ - tags?: string[]; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - /** @description The placeholders that were used to create this experiment from a template */ - templatePlaceholders?: components["schemas"]["ExperimentTemplatePlaceholderValueAO"][]; - /** @description The title of the template that was used to create this experiment */ - templateTitle?: string; - /** - * Format: int32 - * @description Experiment database version. - * @example 1 - */ - version?: number; - }; - /** - * @description A single experiment execution that was triggered from a single experiment. - * @example { - * "id": 58070, - * "key": "SHOP-1", - * "name": "Shop should survive a single pod outage", - * "hypothesis": "When a single container from steadybit-demo/fashion-bestseller fails the shop is still working as expected.", - * "requested": "2023-01-01T09:00:00.000000Z", - * "created": "2023-01-01T09:00:01.000000Z", - * "createdBy": { - * "username": "ag1hb7ap-d299-47ab-998f-c2a53b433820", - * "name": "Manuel", - * "pictureUrl": "https://.../picture.png" - * }, - * "createdVia": "UI", - * "experimentVersion": "5", - * "ended": "2023-01-01T09:10:00.000000Z", - * "state": "FAILED", - * "reason": "Check failure." - * } - */ - ExperimentExecutionAO: { - canceledBy?: components["schemas"]["UserSummaryAO"]; - /** - * Format: date-time - * @description Timestamp when the experiment was created - * @example 2023-01-01T09:00:01Z - */ - created?: string; - createdBy?: components["schemas"]["UserSummaryAO"]; - /** - * @description The creation trigger that caused this experiment execution to be started - * @example UI - * @enum {string} - */ - createdVia?: "API" | "CLI" | "UI" | "SCHEDULE" | "SUITE" | "MCP"; - /** - * Format: date-time - * @description Timestamp when the experiment ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: int32 - * @description Experiment design version which can be used to identify changes between experiment runs - * @example 5 - */ - experimentVersion?: number; - /** - * @description The hypothesis that is validated by the experiment - * @example System is able to survive a latency in the network of 1500ms - */ - hypothesis?: string; - /** - * Format: int32 - * @description Unique experiment execution id that identifies this specific experiment execution - * @example 1523 - */ - id?: number; - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - key?: string; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name?: string; - /** - * @description The properties of the experiment execution - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "Chuck Norris allows that execution" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * Format: int32 - * @description Version of the properties for optimistic locking (optional in the Update-API) - * @example 1 - */ - propertiesVersion?: number; - /** - * @description Reason in case the experiment execution failed or errored - * @example Action error - */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when the experiment was requested - * @example 2023-01-01T09:00:00Z - */ - requested?: string; - /** - * Format: date-time - * @description Timestamp when the experiment was started - * @example 2023-01-01T09:00:02Z - */ - started?: string; - /** - * @description Current state of the experiment (e.g. CREATED, RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** - * @description The steps that are executed in parallel or sequence in the experiment. - * @example [ - * { - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * }, - * { - * "predecessorId": "40b0f797-912d-4256-8887-1553561962a9", - * "ignoreFailure": false, - * "parameters": { - * "cpuLoad": 100, - * "workers": 0, - * "duration": "30s" - * }, - * "actionId": "com.steadybit.extension_container.stress_cpu", - * "actionKind": "ATTACK", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "percentage": 50, - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "container.host/name", - * "operator": "EQUALS", - * "values": [ - * "docker-desktop/minikube" - * ] - * } - * ] - * } - * }, - * "targetExecutions": [ - * { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - * ], - * "totalTargetCount": 1 - * } - * ] - */ - steps?: components["schemas"]["AbstractExperimentExecutionStepAO"][]; - /** - * @description Tags of the experiment at the time the execution was requested - * @example [ - * "resilience", - * "shop" - * ] - */ - tags?: string[]; - /** - * @description Variables and their origins that have been used for this execution - * @example { - * "httpEndpoint": { - * "value": "http://dev.shop.products.internal", - * "origin": "ENVIRONMENT" - * } - * } - */ - variables?: { - [key: string]: components["schemas"]["ExperimentExecutionVariableAO"]; - }; - }; - ExperimentExecutionPageItemAO: { - /** - * Format: date-time - * @description Timestamp when the experiment execution was created - * @example 2023-01-01T09:00:01Z - */ - created?: string; - createdBy?: string; - createdByDetails?: components["schemas"]["UserSummaryAO"]; - /** - * Format: date-time - * @description Timestamp when the experiment execution was ended - * @example 2023-01-01T09:00:01Z - */ - ended?: string; - /** - * @description The name of the environment that this execution was using - * @example Global - */ - environment?: string; - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - experimentKey?: string; - /** - * Format: int64 - * @description Unique experiment execution id that identifies a single experiment execution - * @example 123 - */ - id?: number; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name?: string; - /** - * @description The properties of the experiment execution - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "Chuck Norris allows that execution" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** @description Details about the failure/error reason. */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when the experiment execution was requested - * @example 2023-01-01T09:00:01Z - */ - requested?: string; - /** - * @description Was this execution triggered by a schedule? - * @example true - */ - scheduled?: boolean; - /** - * Format: date-time - * @description Timestamp when the experiment execution was started - * @example 2023-01-01T09:00:01Z - */ - started?: string; - /** - * @description Current state of the experiment execution (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** - * @description The key of the team that this experiment is assigned to - * @example ADM - */ - teamKey?: string; - }; - /** @description Filter for experiment execution report data, optionally scoped to specific teams, environments, and services. */ - ExperimentExecutionReportFilterAO: { - /** @description Restrict results to the given environment IDs. If not provided, all environments are included. */ - environmentIds?: string[] | null; - /** - * Format: date - * @description Start date of the report range (inclusive). - * @example 2026-01-01 - */ - from: string; - /** - * @description The time bucket granularity for report aggregation. - * @example MONTHLY - * @enum {string} - */ - rollup?: "MONTHLY" | "DAILY"; - /** @description Restrict results to the given service IDs. If not provided, all services are included. */ - serviceIds?: string[] | null; - /** @description Restrict results to the given team IDs. If not provided, all teams are included. */ - teamIds?: string[] | null; - /** - * Format: date - * @description End date of the report range (inclusive). - * @example 2026-03-01 - */ - to: string; - }; - /** - * @description Filters are defined in the body of the request. - * @example { - * "page": 0, - * "environments": [ - * "Global" - * ], - * "experimentKeys": [ - * "ADM-9" - * ], - * "teamKeys": [ - * "GITHUB" - * ], - * "teamKeysExclude": [ - * "ADM" - * ], - * "services": [ - * "shopping-cart" - * ], - * "states": [ - * "errored", - * "canceled" - * ], - * "requestedFrom": "2024-05-17T00:00:00Z", - * "requestedTo": "2024-06-24T00:00:00Z", - * "endedFrom": "2024-05-17T00:00:00Z", - * "endedTo": "2024-06-24T00:00:00Z" - * } - */ - ExperimentExecutionsRequestAO: { - /** - * Format: date-time - * @description Filter results by range of created date - * @example 2021-01-01T00:00:00Z - */ - createdFrom?: string | null; - /** - * Format: date-time - * @description Filter results by range of created date - * @example 2021-01-01T00:00:00Z - */ - createdTo?: string | null; - /** - * Format: date-time - * @description Filter results by range of ended date - * @example 2021-01-01T00:00:00Z - */ - endedFrom?: string | null; - /** - * Format: date-time - * @description Filter results by range of ended date - * @example 2021-01-01T00:00:00Z - */ - endedTo?: string | null; - /** - * @description Filter results by one or more environments - * @example [ - * "Global" - * ] - */ - environments?: string[] | null; - /** - * @description Filter results by one or more experiment-keys - * @example [ - * "ADM-9" - * ] - */ - experimentKeys?: string[] | null; - /** - * @description Filter results by name and/or key of the experiment - * @example Outage - */ - name?: string | null; - /** Format: int32 */ - page?: number; - /** - * Format: date-time - * @description Filter results by range of requested date - * @example 2021-01-01T00:00:00Z - */ - requestedFrom?: string | null; - /** - * Format: date-time - * @description Filter results by range of requested date - * @example 2021-01-01T00:00:00Z - */ - requestedTo?: string | null; - /** - * @description Filter results by one or more service names that should be included in the result - * @example [ - * "shopping-cart" - * ] - */ - services?: string[] | null; - /** Format: int32 */ - size?: number; - /** - * @description Filter results by one or more states. Possible values: [CREATED, PREPARED, RUNNING, FAILED, CANCELED, COMPLETED, ERRORED] - * @example [ - * "CREATED" - * ] - */ - states?: string[] | null; - /** - * @description Filter results by one or more team-keys that should be included in the result - * @example [ - * "ADM" - * ] - */ - teamKeys?: string[] | null; - /** - * @description Filter results by one or more team-keys that should be excluded in the result - * @example [ - * "ADM" - * ] - */ - teamKeysExclude?: string[] | null; - }; - /** - * @description An action-step that is executed as part of an experiment. - * @example { - * "stepType": "ACTION", - * "id": "0199c3f2-48c7-706d-b102-9cb09dd41b5d", - * "state": "COMPLETED", - * "started": "2025-10-08T13:11:01.487541Z", - * "ended": "2025-10-08T13:11:11.490268Z", - * "predecessorId": "40b0f797-912d-4256-8887-1553561962a9", - * "ignoreFailure": false, - * "parameters": { - * "cpuLoad": 100, - * "workers": 0, - * "duration": "30s" - * }, - * "actionId": "com.steadybit.extension_container.stress_cpu", - * "actionKind": "ATTACK", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "percentage": 50, - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "container.host/name", - * "operator": "EQUALS", - * "values": [ - * "docker-desktop/minikube" - * ] - * } - * ] - * } - * }, - * "targetExecutions": [ - * { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - * ], - * "totalTargetCount": 1 - * } - */ - ExperimentExecutionStepActionAO: { - /** - * @description Unique identifier of the action that is executed in this step - * @example com.steadybit.extension_container.stress_cpu - */ - actionId?: string; - /** - * @description Kind of the action (e.g. attack, check, loadtest) - * @example ATTACK - * @enum {string} - */ - actionKind?: "ATTACK" | "CHECK" | "LOAD_TEST" | "OTHER" | "BASIC"; - /** - * @description Custom label assigned during experiment design to express the intention of this step - * @example Container 'xyz' can not be reached - */ - customLabel?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: uuid - * @description Unique identifier of this step execution - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - id?: string; - /** - * @description Whether the experiment should fail/error immediately in case this step fails/errors. - * @example false - */ - ignoreFailure?: boolean; - /** - * @description Step-specific parameters of the experiment step configuration - * @example { - * "duration": "10s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - /** - * Format: uuid - * @description Unique identifier of the step execution that precedes this step, null if it is the first step of a lane - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - predecessorId?: string; - radius?: components["schemas"]["BlastRadiusAO"]; - /** - * @description Reason in case this experiment step execution failed or errored - * @example Couldn't read state of container... - */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step was started - * @example 2023-01-01T09:00:00Z - */ - started?: string; - /** - * @description Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** - * @description Type of this step execution (e.g. ACTION, WAIT) (enum property replaced by openapi-typescript) - * @enum {string} - */ - stepType: "ACTION"; - /** - * @description List of targets that are expected to be effected by this action. This list may change in case targets aren't available at the specific time of execution - * @example [ - * { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - * ] - */ - targetExecutions?: components["schemas"]["TargetExecutionAO"][]; - /** - * Format: int64 - * @description Amount of targets that are effect int total - * @example 23 - */ - totalTargetCount?: number; - }; - /** - * @description A service validation step that is executed as part of an experiment. - * @example { - * "stepType": "SERVICE-VALIDATION", - * "id": "40b0f797-912d-4256-8887-1553561962a9", - * "state": "COMPLETED", - * "started": "2025-06-18T08:32:01.850479Z", - * "ended": "2025-06-18T08:32:11.886043Z", - * "predecessorId": null, - * "ignoreFailure": false, - * "parameters": { - * "duration": "60s" - * }, - * "serviceId": "cc06f132-0694-4ffa-aee2-13d8fafa3a8b", - * "validations": [] - * } - */ - ExperimentExecutionStepServiceValidationAO: { - /** - * @description Custom label assigned during experiment design to express the intention of this step - * @example Container 'xyz' can not be reached - */ - customLabel?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: uuid - * @description Unique identifier of this step execution - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - id?: string; - /** - * @description Whether the experiment should fail/error immediately in case this step fails/errors. - * @example false - */ - ignoreFailure?: boolean; - /** - * @description Step-specific parameters of the experiment step configuration - * @example { - * "duration": "10s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - /** - * Format: uuid - * @description Unique identifier of the step execution that precedes this step, null if it is the first step of a lane - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - predecessorId?: string; - /** - * @description Reason in case this experiment step execution failed or errored - * @example Couldn't read state of container... - */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step was started - * @example 2023-01-01T09:00:00Z - */ - started?: string; - /** - * @description Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** - * @description Type of this step execution (e.g. ACTION, WAIT) - * @example ACTION - */ - stepType: string; - } & { - /** - * Format: uuid - * @description Unique identifier of the service. - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - serviceId?: string; - /** @description List of actions performed as part of this service validation step. */ - validations?: components["schemas"]["ExperimentExecutionStepActionAO"][]; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - stepType: "SERVICE-VALIDATION"; - }; - /** - * @description A wait step that is executed as part of an experiment. - * @example { - * "stepType": "WAIT", - * "id": "40b0f797-912d-4256-8887-1553561962a9", - * "state": "COMPLETED", - * "started": "2025-06-18T08:32:01.850479Z", - * "ended": "2025-06-18T08:32:11.886043Z", - * "predecessorId": null, - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * } - */ - ExperimentExecutionStepWaitAO: { - /** - * @description Custom label assigned during experiment design to express the intention of this step - * @example Container 'xyz' can not be reached - */ - customLabel?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: uuid - * @description Unique identifier of this step execution - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - id?: string; - /** - * @description Whether the experiment should fail/error immediately in case this step fails/errors. - * @example false - */ - ignoreFailure?: boolean; - /** - * @description Step-specific parameters of the experiment step configuration - * @example { - * "duration": "10s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - /** - * Format: uuid - * @description Unique identifier of the step execution that precedes this step, null if it is the first step of a lane - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - predecessorId?: string; - /** - * @description Reason in case this experiment step execution failed or errored - * @example Couldn't read state of container... - */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step was started - * @example 2023-01-01T09:00:00Z - */ - started?: string; - /** - * @description Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** - * @description Type of this step execution (e.g. ACTION, WAIT) (enum property replaced by openapi-typescript) - * @enum {string} - */ - stepType: "WAIT"; - }; - /** - * @description List of experiment exeuctions. - * @example { - * "executions": [ - * { - * "id": 102, - * "key": "SHOP-1", - * "name": "Shop survives outage of a single pod", - * "created": "2023-01-01T09:00:01.000000Z", - * "ended": "2023-01-01T09:01:00.000000Z", - * "state": "FAILED" - * }, - * { - * "id": 103, - * "key": "SHOP-1", - * "name": "Shop survives outage of a single pod", - * "created": "2023-01-01T09:10:00.000000Z", - * "ended": "2023-01-01T09:11:00.000000Z", - * "state": "COMPLETED" - * }, - * { - * "id": 110, - * "key": "SHOP-2", - * "name": "DataDog monitors notices pod unavailability", - * "created": "2023-01-01T09:30:00.000000Z", - * "ended": "2023-01-01T09:41:00.000000Z", - * "state": "COMPLETED" - * } - * ] - * } - */ - ExperimentExecutionSummariesAO: { - /** - * @description List of experiment executions - * @example [ - * { - * "id": 102, - * "key": "SHOP-1", - * "name": "Shop survives outage of a single pod", - * "requested": "2023-01-01T09:00:00.000000Z", - * "created": "2023-01-01T09:00:01.000000Z", - * "started": "2023-01-01T09:00:02.000000Z", - * "ended": "2023-01-01T09:01:00.000000Z", - * "state": "FAILED" - * }, - * { - * "id": 103, - * "key": "SHOP-1", - * "name": "Shop survives outage of a single pod", - * "requested": "2023-01-01T09:10:00.000000Z", - * "created": "2023-01-01T09:10:01.000000Z", - * "started": "2023-01-01T09:10:02.000000Z", - * "ended": "2023-01-01T09:11:00.000000Z", - * "state": "COMPLETED" - * }, - * { - * "id": 110, - * "key": "SHOP-2", - * "name": "DataDog monitors notices pod unavailability", - * "requested": "2023-01-01T09:30:00.000000Z", - * "created": "2023-01-01T09:30:01.000000Z", - * "started": "2023-01-01T09:30:02.000000Z", - * "ended": "2023-01-01T09:41:00.000000Z", - * "state": "COMPLETED" - * } - * ] - */ - executions?: components["schemas"]["ExperimentExecutionSummaryAO"][]; - }; - /** - * @description List of experiment executions - * @example [ - * { - * "id": 102, - * "key": "SHOP-1", - * "name": "Shop survives outage of a single pod", - * "requested": "2023-01-01T09:00:00.000000Z", - * "created": "2023-01-01T09:00:01.000000Z", - * "started": "2023-01-01T09:00:02.000000Z", - * "ended": "2023-01-01T09:01:00.000000Z", - * "state": "FAILED" - * }, - * { - * "id": 103, - * "key": "SHOP-1", - * "name": "Shop survives outage of a single pod", - * "requested": "2023-01-01T09:10:00.000000Z", - * "created": "2023-01-01T09:10:01.000000Z", - * "started": "2023-01-01T09:10:02.000000Z", - * "ended": "2023-01-01T09:11:00.000000Z", - * "state": "COMPLETED" - * }, - * { - * "id": 110, - * "key": "SHOP-2", - * "name": "DataDog monitors notices pod unavailability", - * "requested": "2023-01-01T09:30:00.000000Z", - * "created": "2023-01-01T09:30:01.000000Z", - * "started": "2023-01-01T09:30:02.000000Z", - * "ended": "2023-01-01T09:41:00.000000Z", - * "state": "COMPLETED" - * } - * ] - */ - ExperimentExecutionSummaryAO: { - /** - * Format: date-time - * @description Timestamp when the experiment execution was created - * @example 2023-01-01T09:00:01Z - */ - created?: string; - /** - * Format: date-time - * @description Timestamp when the experiment execution ended - * @example 2023-01-01T09:01:00Z - */ - ended?: string; - /** - * Format: int32 - * @description Unique experiment execution id that identifies a single experiment execution - * @example 123 - */ - id?: number; - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - key?: string; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name?: string; - /** - * @description The properties of the experiment execution - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "Chuck Norris allows that execution" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * Format: date-time - * @description Timestamp when the experiment execution was requested - * @example 2023-01-01T09:00:00Z - */ - requested?: string; - /** - * Format: date-time - * @description Timestamp when the experiment execution started - * @example 2023-01-01T09:00:02Z - */ - started?: string; - /** - * @description Current state of the experiment execution (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - }; - /** - * @description The variables resolved for this specific execution, keyed by name. Each entry carries the resolved value(s) and the tier the winning value originated from (ENVIRONMENT, SERVICE, EXPERIMENT, SCHEDULE, EXECUTION). A single-value variable's value is a string, a multi-value variable's value is an array of strings. Empty until the execution starts, as dynamic values are resolved once at run start and then stay stable for the whole run. - * @example { - * "httpEndpoint": { - * "value": "http://shop.products.internal", - * "origin": "EXECUTION" - * } - * } - */ - ExperimentExecutionVariableAO: { - /** @enum {string} */ - origin?: "ENVIRONMENT" | "SERVICE" | "EXPERIMENT" | "SCHEDULE" | "EXECUTION"; - /** @description Either a single value (the common case, including single-element select results) or an array of values (multi-value select expressions). */ - value?: string | string[]; - }; - /** - * @description A single lane of an experiment design. This lane can contain multiple steps that are executed sequentially - * @example { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - */ - ExperimentLaneAO: { - /** - * @description A list of steps that are executed sequentially in this lane. - * @example [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - */ - steps: components["schemas"]["BaseExperimentStepAO"][]; - }; - /** @description Filter for experiment report data, optionally scoped to specific teams and environments. */ - ExperimentReportFilterAO: { - /** @description Restrict results to the given environment IDs. If not provided, all environments are included. */ - environmentIds?: string[] | null; - /** - * Format: date - * @description Start date of the report range (inclusive). - * @example 2026-01-01 - */ - from: string; - /** - * @description The time bucket granularity for report aggregation. - * @example MONTHLY - * @enum {string} - */ - rollup?: "MONTHLY" | "DAILY"; - /** @description Restrict results to the given team IDs. If not provided, all teams are included. */ - teamIds?: string[] | null; - /** - * Format: date - * @description End date of the report range (inclusive). - * @example 2026-03-01 - */ - to: string; - }; - /** - * @description The risk for a single experiment linked to a service - * @example { - * "experimentKey": "ADM-8", - * "risk": 42 - * } - */ - ExperimentRiskAO: { - /** @description The experiment key */ - experimentKey?: string; - /** - * Format: int32 - * @description The calculated risk score for this experiment (0-100) - */ - risk?: number; - }; - /** - * @description A schedule for an experiment. - * @example { - * "experimentKey": "ADM-8", - * "cron": "30 * * * * ? *", - * "enabled": true, - * "allowParallel": true, - * "timezone": "Europe/Berlin", - * "variables": {}, - * "id": "01951394-727f-76a0-8675-c7519ebd0ff5", - * "lastUpdated": "2025-02-17T11:04:10.623486Z", - * "editedBy": { - * "username": "ag1hb7ap-d299-47ab-998f-c2a53b433820", - * "name": "Manuel", - * "pictureUrl": "https://.../picture.png", - * "email": "manuel@example.org" - * }, - * "nextExecution": "2025-02-20T05:54:30Z" - * } - */ - ExperimentScheduleAO: { - /** - * @description Should the experiment run if another experiment is running? Default is true. - * @example true - */ - allowParallel?: boolean; - /** - * @description Cron expression for the experiment schedule. Can't be used in combination with `startAt`. - * @example 0 15 10 ? * * - */ - cron?: string | null; - editedBy: components["schemas"]["UserSummaryAO"]; - /** - * @description If `false`, the schedule is deactivated and no experiment will be executed. Default is true. - * @example false - */ - enabled?: boolean; - /** - * @description The experiment that should be scheduled. - * @example ADM-123 - */ - experimentKey: string; - id: string; - /** Format: date-time */ - lastUpdated: string; - /** Format: date-time */ - nextExecution?: string | null; - /** - * Format: date-time - * @description Start date for a single execution. Can't be used in combination with `cron`. - */ - startAt?: string | null; - /** - * @description Optional timezone for a experiment schedule. Can only be used with `cron`. - * @example Europe/Berlin - */ - timezone?: string | null; - /** - * @description Variables that will be used when the experiment will be executed. The variables will override existing environment or experiment variables. Each value is either a constant string, an array of constant strings, or a select expression object. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - variables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - }; - /** - * @description A single step in a lane executing always exactly one action. - * @example { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - */ - ExperimentStepActionAO: { - customLabel?: string; - /** - * @description Ignore any errors and failures of this single step and continue the execution of an experiment run - * @example false - */ - ignoreFailure?: boolean; - /** @description Optional metric checks used to define success or failure of this step */ - metricChecks?: components["schemas"]["MetricCheckAO"][]; - /** @description Optional metric queries used of this step to filter e.g. monitoring data */ - metricQueries?: components["schemas"]["MetricQueryAO"][]; - /** - * @description Configuration parameters of this step that are saved during experiment design and evaluated at execution time. - * @example { - * "duration": "30s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - type: string; - } & { - /** - * @description The specific action that is used in this step - * @example com.steadybit.extension_host.stress-cpu - */ - actionType: string; - radius?: components["schemas"]["ExperimentStepRadiusAO"]; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "action"; - }; - /** - * @description Specifying the targets and random blast radius of the available targets - * @example { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * } - * } - */ - ExperimentStepRadiusAO: { - advanced?: components["schemas"]["AdvancedRadiusAO"][]; - /** Format: int32 */ - maximum?: number; - /** Format: int32 */ - percentage?: number; - predicate?: components["schemas"]["TargetPredicateAO"]; - query?: components["schemas"]["TargetPredicateAO"]; - targetType?: string; - }; - /** - * @description A step in a lane executing the defined validations of a service - * @example { - * "type": "service-validation", - * "serviceId": "1a04288d-6c85-4ba6-80d7-24ded64dd009", - * "parameters": { - * "duration": "120s" - * } - * } - */ - ExperimentStepServiceValidationAO: { - customLabel?: string; - /** - * @description Ignore any errors and failures of this single step and continue the execution of an experiment run - * @example false - */ - ignoreFailure?: boolean; - /** @description Optional metric checks used to define success or failure of this step */ - metricChecks?: components["schemas"]["MetricCheckAO"][]; - /** @description Optional metric queries used of this step to filter e.g. monitoring data */ - metricQueries?: components["schemas"]["MetricQueryAO"][]; - /** - * @description Configuration parameters of this step that are saved during experiment design and evaluated at execution time. - * @example { - * "duration": "30s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - type: string; - } & { - /** - * @description The name of the service to validate. - * @example 1a04288d-6c85-4ba6-80d7-24ded64dd009 - */ - serviceId: string; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "service-validation"; - }; - /** - * @description A single step in a lane waiting for a specified duration. - * @example { - * "type": "wait", - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * } - */ - ExperimentStepWaitAO: { - customLabel?: string; - /** - * @description Ignore any errors and failures of this single step and continue the execution of an experiment run - * @example false - */ - ignoreFailure?: boolean; - /** @description Optional metric checks used to define success or failure of this step */ - metricChecks?: components["schemas"]["MetricCheckAO"][]; - /** @description Optional metric queries used of this step to filter e.g. monitoring data */ - metricQueries?: components["schemas"]["MetricQueryAO"][]; - /** - * @description Configuration parameters of this step that are saved during experiment design and evaluated at execution time. - * @example { - * "duration": "30s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - type: string; - } & { - /** - * @description discriminator enum property added by openapi-typescript - * @enum {string} - */ - type: "wait"; - }; - /** - * @description List of experiments. - * @example { - * "experiments": [ - * { - * "key": "ADM-1", - * "name": "Shop survives unavailability of hot-deals products" - * }, - * { - * "key": "SHOP-2", - * "name": "Network latency of Message Broker doesn't interfere with Online shop" - * } - * ] - * } - */ - ExperimentSummariesAO: { - /** - * @description List of experiment summaries - * @example [ - * { - * "key": "ADM-1", - * "name": "Shop survives unavailability of hot-deals products" - * }, - * { - * "key": "SHOP-2", - * "name": "Network latency of Message Broker doesn't interfere with Online shop" - * } - * ] - */ - experiments?: components["schemas"]["ExperimentSummaryAO"][]; - }; - /** - * @description Summary of a single experiment. - * @example { - * "key": "ADM-1", - * "name": "Shop survives unavailability of hot-deals products" - * } - */ - ExperimentSummaryAO: { - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - key?: string; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name?: string; - }; - /** - * @example { - * "id": "6bea7aec-3572-44cf-9151-c6ada57d08ca", - * "version": 0, - * "templateTitle": "HTTP Endpoint remains functional during Kubernetes Rollout Restart", - * "templateDescription": "Test if a given HTTP Endpoint remains funcitonal if a Kubernetes deployment is restarted.", - * "placeholders": [ - * { - * "key": "HTTP_ENDPOINT", - * "name": "HTTP Endpoint", - * "description": "Which HTTP Endpoint should be checked during experiment execution?" - * }, - * { - * "key": "DEPLOYMENT", - * "name": "Kubernetes Deployment", - * "description": "Which Kubernetes deployment do you want to restart?" - * }, - * { - * "key": "CLUSTER", - * "name": "Kubernetes Cluster", - * "description": "In which Kubernetes cluster is the deployment deployed to?" - * }, - * { - * "key": "NAMESPACE", - * "name": "Kubernetes Namespace", - * "description": "In which Kubernetes namespace is the deployment deployed to?" - * } - * ], - * "tags": [ - * "Kubernetes" - * ], - * "lanes": [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "60s", - * "headers": [], - * "method": "GET", - * "successRate": "100", - * "maxConcurrent": 5, - * "followRedirects": false, - * "readTimeout": "5s", - * "connectTimeout": "5s", - * "requestsPerSecond": 1, - * "url": "[[HTTP_ENDPOINT]]", - * "statusCode": "200-299" - * }, - * "actionType": "com.steadybit.extension_http.check.periodically", - * "radius": {} - * } - * ] - * }, - * { - * "steps": [ - * { - * "type": "wait", - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * }, - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "wait": false - * }, - * "actionType": "com.steadybit.extension_kubernetes.rollout-restart", - * "radius": { - * "targetType": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.cluster-name", - * "operator": "EQUALS", - * "values": [ - * "[[CLUSTER]]" - * ] - * }, - * { - * "key": "k8s.namespace", - * "operator": "EQUALS", - * "values": [ - * "[[NAMESPACE]]" - * ] - * }, - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "[[DEPLOYMENT]]" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 50 - * } - * }, - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "10m" - * }, - * "actionType": "com.steadybit.extension_kubernetes.rollout-status", - * "radius": { - * "targetType": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.cluster-name", - * "operator": "EQUALS", - * "values": [ - * "[[CLUSTER]]" - * ] - * }, - * { - * "key": "k8s.namespace", - * "operator": "EQUALS", - * "values": [ - * "[[NAMESPACE]]" - * ] - * }, - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "[[DEPLOYMENT]]" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 50 - * } - * } - * ] - * } - * ], - * "properties": { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * }, - * "propertiesMetadata": [ - * { - * "key": "EXAMPLE_CUSTOM_PROPERTY", - * "required": true, - * "editableInExecution": false - * } - * ], - * "hidden": false, - * "created": "2024-03-22T09:24:12.802961166Z", - * "createdBy": { - * "username": "2bd1c2d7-4051-46ad-9f05-315062edd85e", - * "name": "Daniel", - * "pictureUrl": "https://s.gravatar.com/avatar/4f27f3856530f8f2e4ec050b1d594306?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fda.png" - * }, - * "edited": "2024-03-22T09:24:12.802961166Z", - * "editedBy": { - * "username": "2bd1c2d7-4051-46ad-9f05-315062edd85e", - * "name": "Daniel", - * "pictureUrl": "https://s.gravatar.com/avatar/4f27f3856530f8f2e4ec050b1d594306?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fda.png" - * } - * } - */ - ExperimentTemplateAO: { - /** - * Format: date-time - * @description Timestamp when the experiment template was created - * @example 2023-01-01T09:00:00Z - */ - created: string; - createdBy: components["schemas"]["UserSummaryAO"]; - /** - * Format: date-time - * @description Timestamp when the experiment template was edited the last time - * @example 2023-01-01T09:00:00Z - */ - edited: string; - editedBy: components["schemas"]["UserSummaryAO"]; - /** - * @description Name of the experiment created by this template. If omitted, the name needs to be added when the template is used. - * @example Shop survives unavailability of database - */ - experimentName?: string | null; - /** - * @description Should the experiment template be hidden - * @example false - */ - hidden?: boolean; - /** - * @description The hypothesis that is validated by the experiment - * @example System is able to survive a latency in the network of 1500ms - */ - hypothesis?: string; - /** Format: uuid */ - id?: string | null; - /** - * @description The lanes (steps executed in parallel) in the experiment template. Each lane consists of multiple steps that are executed sequential per lane. - * @example [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ] - */ - lanes: components["schemas"]["ExperimentLaneAO"][]; - /** @description A list of placeholders used in this experiment template. */ - placeholders?: components["schemas"]["ExperimentTemplatePlaceholderAO"][]; - /** - * @description The properties of the experiment - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description Metadata for properties used in this template. - * @example [ - * { - * "key": "EXAMPLE_CUSTOM_PROPERTY", - * "required": true, - * "editableInExecution": false - * } - * ] - */ - propertiesMetadata?: components["schemas"]["PropertyMetadataAO"][]; - /** @description A list of tags for this experiment template. (Up to 5) */ - tags?: string[]; - /** @description A brief description what the template is doing. */ - templateDescription: string; - /** - * @description The title of the template - * @example Shop survives unavailability of database - */ - templateTitle: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** @description A list of placeholders used in this experiment template. */ - ExperimentTemplatePlaceholderAO: { - description: string; - key: string; - name: string; - }; - /** @description List of template placeholder values */ - ExperimentTemplatePlaceholderValueAO: { - /** - * @description The key of a template placeholder - * @example cluster-name - */ - key: string; - /** - * @description The value of a template placeholder, can be a string, a number a boolean or an object with a given structure like `[{"key": "CLUSTER","value": "demo-cluster"}]` - * @example prod-cluster-1 - */ - value: Record; - }; - /** @description Request to import templates from a hub. */ - ExperimentTemplatesImportAO: { - /** - * Format: uuid - * @description ID of the hub to import templates from. - */ - hubId: string; - /** @description Optional list of Template IDs to import into the platform. If not provided, all templates from the hub will be imported. */ - templateIds?: string[] | null; - }; - /** - * @description List of experiment template summaries. - * @example { - * "templates": [ - * { - * "id": "f5990c81-6427-4144-8304-eda765a3f852", - * "templateTitle": "xxx" - * }, - * { - * "id": "d7e65100-1d20-4980-be87-c351704910b8", - * "templateTitle": "yyy" - * } - * ] - * } - */ - ExperimentTemplateSummariesAO: { - /** - * @description List of experiment template summaries. - * @example [ - * { - * "id": "f5990c81-6427-4144-8304-eda765a3f852", - * "templateTitle": "xxx" - * }, - * { - * "key": "d7e65100-1d20-4980-be87-c351704910b8", - * "templateTitle": "yyy" - * } - * ] - */ - templates?: components["schemas"]["ExperimentTemplateSummaryAO"][]; - }; - /** - * @description Summary of a single experiment template. - * @example { - * "id": "e50deab2-2636-4a5b-ad5a-6cf904ed56c4", - * "templateTitle": "HTTP Endpoint remains functional during Kubernetes Rollout Restart" - * } - */ - ExperimentTemplateSummaryAO: { - /** - * @description Is the template currently hidden? - * @example true - */ - hidden?: boolean; - /** - * Format: uuid - * @description Unique id that identifies the experiment template. - * @example b9f4aae2-9b03-4ad3-a1a7-654774cc04eb - */ - id?: string; - /** - * @description Description of the experiment template - * @example Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - */ - templateDescription?: string; - /** - * @description Title of the experiment template - * @example Shop survives unavailability of hot-deals products - */ - templateTitle?: string; - }; - /** - * @description Request for getting and filtering pieces of advice. - * @example { - * "environmentName": "Global", - * "query": "k8s.cluster-name=sandbox-demo and k8s.namespace=steadybit-demo", - * "offset": 0 - * } - */ - GetAdviceApiRequestAO: { - /** - * @description The name of the environment of which the pieces of Advice should be listed - * @example Global - */ - environmentName: string; - /** - * Format: int64 - * @description The offset to be returned in the paginated result set - * @example 20 - */ - offset?: number | null; - /** - * @description An additional optional filter to search only for pieces of advice, whose target is included in the filter - * @example k8s.cluster-name=prod-demo and k8s.namespace=steadybit-demo - */ - query?: string | null; - }; - GetLicenseSummaryAO: { - /** Format: date-time */ - expires?: string | null; - features?: components["schemas"]["LicenseFeatureSummaryAO"][] | null; - license?: components["schemas"]["LicenseSummaryAO"]; - tenantKey?: string | null; - }; - /** @description An informational or warning hint displayed to the user. */ - HintAO: { - /** @description Content of the hint as markdown text. */ - content: string; - /** - * @description Type of the hint. - * @example INFO - */ - type: string; - } | null; - HubAO: { - /** - * Format: date-time - * @description Timestamp when the hub was connected - * @example 2023-01-01T09:00:00Z - */ - created: string; - createdBy: components["schemas"]["UserSummaryAO"]; - /** - * Format: date-time - * @description Timestamp when the hub was edited the last time - * @example 2023-01-01T09:00:00Z - */ - edited: string; - editedBy: components["schemas"]["UserSummaryAO"]; - /** - * @description Website address of the the hub - * @example https://hub.steadybit.com/ - */ - hubLink?: string | null; - /** @description Name of the hub */ - hubName: string; - /** Format: uuid */ - id: string; - /** - * Format: date-time - * @description Timestamp of last change as defined in the repository content - */ - lastRepositoryChange?: string | null; - /** - * Format: date-time - * @description Timestamp of last hub synchronization - */ - lastSync?: string | null; - /** - * @description HTTP address of the the hub's repository - * @example https://github.com/steadybit/reliability-hub-db - */ - repositoryUrl: string; - /** @description Last synchronization error description, if an error occurred */ - syncError?: string | null; - /** @description List of templates published in the hub. */ - templates: components["schemas"]["ExperimentTemplateSummaryAO"][]; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version: number; - }; - HubConnectionCheckAO: { - /** - * @description HTTP address of the the hub's repository - * @example https://github.com/steadybit/reliability-hub-db - */ - repositoryUrl: string; - }; - HubConnectionCheckResponseAO: { - error?: string | null; - }; - /** - * @description List of all hubs. Fetch a single hub by `id` to get more information. - * @example { - * "hubs": [ - * { - * "id": "1b267dc1-5f4e-4803-894d-92ecd9b83413", - * "hubName": "Hub Name" - * } - * ] - * } - */ - HubSummariesAO: { - hubs?: components["schemas"]["HubSummaryAO"][]; - }; - /** - * @description Summary containing the most important hub details. - * @example { - * "id": "1b267dc1-5f4e-4803-894d-92ecd9b83413", - * "hubName": "Hub Name" - * } - */ - HubSummaryAO: { - /** @description Name of the hub */ - hubName: string; - /** Format: uuid */ - id: string; - }; - /** - * @description Request to invite users to the platform - * @example { - * "email": "aa@bb.com", - * "role": "USER", - * "teamKey": "TST" - * } - */ - InvitationAO: { - /** Format: email */ - email: string; - /** @enum {string} */ - role?: "ADMIN" | "USER"; - teamKey?: string | null; - }; - /** - * @description Request to invite users to the platform - * @example { - * "invitations": [ - * { - * "email": "aa@bb.com", - * "role": "ADMIN", - * "teamKey": "ADM" - * } - * ] - * } - */ - InviteUsersRequestAO: { - invitations: components["schemas"]["InvitationAO"][]; - }; - /** - * @description Determines the current status of the kill switch (emergency stop). If the kill switch is active, all experiments are cancelled immediately and no new experiments can be executed. - * @example { - * "active": "true", - * "engagedBy": "71ab0180-8abc-4d30-8acb-6aa024e3065f", - * "engaged": "2023-01-01T09:00:00Z" - * } - */ - KillswitchAO: { - /** - * @description Determines whether the kill switch is currently active / engaged. - * @example true - */ - active?: boolean; - /** - * Format: date-time - * @description Time at which the kill switch was activated / engaged - * @example 2023-01-01T09:00:00Z - */ - engaged?: string; - /** - * @description Username (internal identifier of Steadybit) of the user that has activated / engaged the kill switch - * @example 13av2737-b318-4048-a79d-4789d645bc31 - */ - engagedBy?: string; - engagedByDetails?: components["schemas"]["UserSummaryAO"]; - }; - /** @description A saved view of the explorer landscape. */ - LandscapeViewAO: { - colorBy?: components["schemas"]["LandscapeViewColorByAO"]; - /** - * @description Description of the saved view. - * @example All shop workloads grouped by namespace. - */ - description?: string; - /** - * @description Name of the environment the view is scoped to. - * @example Global - */ - environment?: string; - /** - * @description Explorer filter query narrowing the targets shown on the landscape. - * @example k8s.namespace="shop" - */ - filterQuery?: string; - /** @description Ordered list of group-by dimensions the targets are grouped by, each with its own advanced configuration. */ - groupBy?: components["schemas"]["LandscapeViewGroupByAO"][]; - /** - * Format: uuid - * @description Unique identifier of the saved view. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id?: string; - /** - * Format: date-time - * @description Point in time the saved view was last updated. - * @example 2026-07-23T10:15:30Z - */ - lastUpdated?: string; - /** - * @description Title of the saved view. - * @example Kubernetes by namespace - */ - name?: string; - /** - * @description Whether reliability advice is shown on the landscape. - * @example false - */ - showAdvice?: boolean; - /** - * @description Attribute key the size of a target is derived from. - * @example k8s.container.cpu.limit - */ - sizeBy?: string; - /** - * @description Key of the team the saved view belongs to. - * @example ADM - */ - team: string; - }; - /** @description The color-by dimension: the attribute the targets are colored by, together with its advanced configuration. */ - LandscapeViewColorByAO: { - /** - * @description Attribute key the color of a target is derived from. - * @example k8s.namespace - */ - attribute?: string; - /** @description Buckets that map specific attribute values to named color groups. */ - mappings?: components["schemas"]["LandscapeViewMappedGroupingAO"][]; - /** - * @description Explicit color overrides keyed by the color-by attribute value. Each color must be one of the predefined landscape colors. - * @example { - * "shop": "GREEN" - * } - */ - overrides?: { - [key: string]: "RED" | "ORANGE_DARK" | "ORANGE_LIGHT" | "YELLOW" | "GREEN" | "TIFFANY" | "TEAL" | "BLUE_LIGHT" | "BLUE" | "PLUM" | "BERRIES" | "VIOLET" | "ROSE_PINK" | "PINK" | "GREY"; - } | null; - }; - /** @description A single group-by dimension: the attribute the targets are grouped by, together with its advanced configuration. */ - LandscapeViewGroupByAO: { - /** - * @description Attribute key the targets are grouped by. - * @example k8s.namespace - */ - attribute: string; - /** @description Buckets that map specific attribute values to named groups. */ - mappings?: components["schemas"]["LandscapeViewMappedGroupingAO"][]; - /** - * @description Whether attribute values that are not mapped to any bucket are merged into the unknown group. - * @example false - */ - mergeUnmappedToUnknown?: boolean | null; - /** - * @description Whether an additional group collecting all targets without a value for this dimension is shown. - * @example true - */ - showUnknown?: boolean; - }; - /** @description A bucket that groups multiple attribute values under a single named group. */ - LandscapeViewMappedGroupingAO: { - /** - * @description Attribute values that are collected into this bucket. - * @example [ - * "prod", - * "production" - * ] - */ - attributeValues?: string[]; - /** - * @description Display name of the bucket. - * @example Production - */ - groupName?: string; - /** - * Format: uuid - * @description Unique identifier of the bucket. Optional on create/update — a new identifier is generated when omitted; supply the returned identifier to keep a bucket stable across updates. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id?: string | null; - }; - LicenseFeatureSummaryAO: { - /** Format: int32 */ - hardLimit?: number | null; - name: string; - /** Format: int32 */ - softLimit?: number | null; - /** @enum {string} */ - type: "SIMPLE" | "SOFT_LIMIT" | "HARD_LIMIT"; - /** Format: int32 */ - usage?: number | null; - }; - LicenseSummaryAO: { - /** Format: int64 */ - id?: number; - /** @enum {string} */ - licenseType?: "NONE" | "TRIAL" | "STARTUP" | "PROFESSIONAL" | "ENTERPRISE"; - orderNumber?: string; - /** Format: date */ - validFrom: string; - /** Format: date */ - validTo: string; - } | null; - LinkCustomExperimentRequestAO: { - /** - * @description The category to which the experiment should be linked - * @example Scalability - */ - category: string; - /** - * @description The experiment that should be linked - * @example ADM-18 - */ - experimentKey: string; - }; - ListResponseCustomWebhookAO: { - content?: components["schemas"]["CustomWebhookAO"][]; - }; - ListResponseLandscapeViewAO: { - content?: components["schemas"]["LandscapeViewAO"][]; - }; - ListResponsePreflightActionIntegrationAO: { - content?: components["schemas"]["PreflightActionIntegrationAO"][]; - }; - ListResponsePreflightWebhookAO: { - content?: components["schemas"]["PreflightWebhookAO"][]; - }; - ListResponseSlackWebhookAO: { - content?: components["schemas"]["SlackWebhookAO"][]; - }; - /** - * @description Member of a team. - * @example { - * "username": "13av2737-b318-4048-a79d-4789d645bc31", - * "role": "OWNER", - * "name": "Max Mustermann", - * "email": "aa@bb.com" - * } - */ - MemberAO: { - email?: string | null; - /** - * @description How a team or team membership is managed - * @example MANUAL - * @enum {string} - */ - managedBy?: "MANUAL" | "OIDC" | "LDAP"; - /** - * @description Name of the user - * @example Jane Doe - */ - name?: string; - pictureUrl?: string | null; - /** - * @description Role of the team member - * @example OWNER - * @enum {string} - */ - role: "MEMBER" | "OWNER"; - /** - * @description Username of the user, internal identifier of Steadybit - * @example 13av2737-b318-4048-a79d-4789d645bc31 - */ - username: string; - }; - /** - * @description Add a Member to a Team by providing the username or the email. - * @example { - * "members": [ - * { - * "username": "example", - * "email": "example@example.com", - * "role": "MEMBER" - * } - * ] - * } - */ - MemberUpdateAO: { - /** - * @description E-mail of the user, unique within Steadybit - * @example example@example.com - */ - email?: string; - /** - * @description Role of the team member - * @example OWNER - * @enum {string} - */ - role: "MEMBER" | "OWNER"; - /** - * @description Username of the user, internal identifier of Steadybit - * @example 13av2737-b318-4048-a79d-4789d645bc31 - */ - username?: string; - }; - /** @description Optional metric checks used to define success or failure of this step */ - MetricCheckAO: { - a: components["schemas"]["MetricValueAO"]; - b?: components["schemas"]["MetricValueAO"] | components["schemas"]["ScalarValueAO"] | components["schemas"]["VariableValueAO"]; - /** @enum {string} */ - condition: "LT" | "LTE" | "EQ" | "NEQ" | "GT" | "GTE" | "DATA_SERIES_PRESENCE"; - /** Format: uuid */ - id: string; - }; - /** @description Optional metric queries used of this step to filter e.g. monitoring data */ - MetricQueryAO: { - /** Format: uuid */ - id: string; - label: string; - parameters: { - [key: string]: unknown; - }; - }; - MetricValueAO: { - type: "MetricValueAO"; - } & (Omit & { - metric?: { - [key: string]: string; - }; - name?: string | null; - }); - NegationTargetPredicateAO: Record & { - not?: components["schemas"]["TargetPredicateAO"]; - }; - OptionAO: { - attribute?: string; - label?: string; - value?: string; - }; - PagedResponseAOAccessTokensPageItemAO: { - items?: components["schemas"]["AccessTokensPageItemAO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PagedResponseAOAccessTokensPageItemV2AO: { - items?: components["schemas"]["AccessTokensPageItemV2AO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PagedResponseAOExperimentExecutionPageItemAO: { - items?: components["schemas"]["ExperimentExecutionPageItemAO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PagedResponseAOPropertyAssociationAO: { - items?: components["schemas"]["PropertyAssociationAO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PagedResponseAOPropertyDefinitionAO: { - items?: components["schemas"]["PropertyDefinitionAO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PagedResponseAOServiceExperimentAO: { - items?: components["schemas"]["ServiceExperimentAO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PagedResponseAOServiceProfileAO: { - items?: components["schemas"]["ServiceProfileAO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PagedResponseAOServiceSummaryAO: { - items?: components["schemas"]["ServiceSummaryAO"][]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PagedResponseAOString: { - items?: string[]; - /** - * Format: int32 - * @description Next page to query for next page of runs or null if there are none. - * @example 4 - */ - nextPage?: number | null; - /** - * Format: int64 - * @description Total amount of runs matching your query - * @example 241 - */ - totalItems?: number; - }; - PageRequestAO: { - /** Format: int32 */ - page?: number; - /** Format: int32 */ - size?: number; - }; - /** @description Parameters that describe how to fetch metrics for this action. */ - ParameterAO: { - acceptedFileTypes?: string[] | null; - advanced?: boolean; - defaultValue?: string; - deprecated?: boolean; - deprecationMessage?: string; - description?: string; - durationUnits?: string[] | null; - hint?: components["schemas"]["HintAO"]; - label: string; - /** Format: int32 */ - max?: number; - /** Format: int32 */ - min?: number; - name: string; - options?: components["schemas"]["OptionAO"][]; - optionsOnly?: boolean; - /** Format: int32 */ - order?: number; - required?: boolean; - type: string; - }; - /** - * @description A partial update for an experiment schedule. Only non-null fields will be updated. - * @example { - * "enabled": false - * } - */ - PatchExperimentScheduleAO: { - /** - * @description Should the experiment run if another experiment is running? - * @example true - */ - allowParallel?: boolean | null; - /** - * @description Cron expression for the experiment schedule. If provided, startAt will be cleared. - * @example 0 15 10 ? * * - */ - cron?: string | null; - /** - * @description If `false`, the schedule is deactivated and no experiment will be executed. - * @example false - */ - enabled?: boolean | null; - /** - * Format: date-time - * @description Start date for a single execution. If provided, cron will be cleared. - */ - startAt?: string | null; - /** - * @description Optional timezone for a experiment schedule. Can only be used with `cron`. - * @example Europe/Berlin - */ - timezone?: string | null; - /** - * @description Variables that will be used when the experiment will be executed. The variables will override existing environment or experiment variables. Each value is either a constant string, an array of constant strings, or a select expression object. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - variables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - } | null; - }; - /** - * @description A pageable list of pieces of preflight actions. - * @example { - * id: "com.steadybit.extension_preflight.preflightaction.check-configuration", - * version: "0.1.0", - * description: "Check if a execution of a specific experiment in a environment is permitted.", - * targetAttributeIncludes: [ - * "k8s.cluster-name", - * "k8s.namespace" - * ] - * } - */ - PreflightActionAO: { - /** - * @description The description of the preflight action - * @example Check if a execution of a specific experiment in a environment is permitted. - */ - description?: string; - /** - * @description The unique identifier of the preflight action - * @example com.steadybit.extension_preflight.preflightaction.check-configuration - */ - id: string; - /** - * @description The name of the preflight action - * @example Check configuration - */ - name: string; - /** - * @description The list of target attributes that are included in the preflight action - * @example [ - * "k8s.cluster-name", - * "k8s.namespace" - * ] - */ - targetAttributeIncludes?: string[]; - /** - * @description The version of the preflight action - * @example 0.1.0 - */ - version: string; - }; - /** - * @example { - * "id": "ac456d58-8fb2-4df4-86d8-ca81d7562739", - * "version": 1, - * "scope": "TEAM", - * "team": "ADM", - * "name": "Example Preflight Action Integration", - * "preflightActionId": "com.example.preflightaction.MyPreflightAction", - * "inflightInterval": "10s", - * "inflightTimeout": "5s" - * } - */ - PreflightActionIntegrationAO: { - /** - * Format: uuid - * @description The id of the webhook - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - inflightInterval?: string; - inflightTimeout?: string; - /** - * @description The name of the preflightActionIntegration - * @example Preflight PreflightActionIntegration - */ - name: string; - /** - * @description The preflight action id which is used to identify the preflight action - * @example com.example.preflightaction.MyPreflightAction - */ - preflightActionId: string; - /** - * @description The scope of the preflight action integration - * @example TEAM - * @enum {string} - */ - scope: "GLOBAL" | "TEAM"; - /** - * @description The key of the team if the scope is `TEAM` - * @example ADM - */ - team?: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version: number; - }; - /** - * @example { - * "id": "ac456d58-8fb2-4df4-86d8-ca81d7562739", - * "version": 1, - * "scope": "TEAM", - * "team": "ADM", - * "name": "Example Preflight Action Integration", - * "preflightActionId": "com.example.preflightaction.MyPreflightAction", - * "inflightInterval": "10s", - * "inflightTimeout": "5s" - * } - */ - PreflightActionIntegrationUpsertAO: { - /** - * Format: uuid - * @description The id of the webhook or null if a new webhook should be created. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id?: string | null; - inflightInterval?: string; - inflightTimeout?: string; - /** - * @description The name of the preflightActionIntegration - * @example Preflight PreflightActionIntegration - */ - name: string; - /** - * @description The preflight action id which is used to identify the preflight action - * @example com.example.preflightaction.MyPreflightAction - */ - preflightActionId: string; - /** - * @description The scope of the preflight action integration - * @example TEAM - * @enum {string} - */ - scope: "GLOBAL" | "TEAM"; - /** - * @description The key of the team if the scope is `TEAM` - * @example ADM - */ - team?: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** - * @description A pageable list of pieces of perflight actions. - * @example { - * "totalItems": 108, - * "nextOffset": 3, - * "items": [] - * } - */ - PreflightActionSummaryAO: { - items?: components["schemas"]["PreflightActionAO"][]; - /** - * Format: int32 - * @description Next queryable offset to query for next batch of preflight actions - * @example 21 - */ - nextOffset?: number | null; - /** - * Format: int64 - * @description Total amount of preflight actions - * @example 241 - */ - totalItems?: number; - }; - PreflightWebhookAO: { - /** - * @description The events that you want to intercept. Currently only `experiment.execution.preflight` is supported. - * @example [ - * "experiment.execution.preflight" - * ] - */ - events: string[]; - /** - * @description Additional headers to include in the webhook request. - * @example { - * "X-Custom-Header": "CustomValue", - * "X-Another-Header": "AnotherValue" - * } - */ - headers?: { - [key: string]: string; - }; - /** - * Format: uuid - * @description The id of the webhook - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - /** - * @description The name of the webhook - * @example Preflight Webhook - */ - name: string; - /** - * @description The scope of the webhook / integration - * @example TEAM - * @enum {string} - */ - scope: "GLOBAL" | "TEAM"; - /** - * @description If a secret is provided a signature of the body is computed using `HMAC SHA-256` and sent as `X-SB-Signature` http header. You can use this header to verify the message. - * @example secret123!! - */ - secret?: string; - /** - * @description The body size can get very large as we include all target attributes for each target of your experiments. When having experiments with many targets, it might be useful to filter the attributes to only include the ones you are interested in. You can use the wildcard character '*' to match all attributes or a comma-separated-list of attribute-names. If the field is empty, no attributes will be included. - * @example [ - * "k8s.cluster-name", - * "k8s.deployment" - * ] - */ - targetAttributeIncludes: string[]; - /** - * @description The key of the team if the scope is `TEAM` - * @example ADM - */ - team?: string; - /** - * @description The URL of the webhook - * @example https://example.com/webhook - */ - url: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version: number; - }; - /** - * @description Optional preflight webhook response body that can be used to deliver a message of a preflight webhook. This message is used as failure / error / success reason depending on the HTTP API preflight response status code. - * @example { - * "message": "Experiment executions are not allowed for this environment" - * "modifications": [ - * { - * "type": "set_property_value", - * "propertyKey": "approvedBy", - * "value": "Daniel" - * } - * ] - * } - */ - PreflightWebhookResponseAO: { - /** - * @description The message that should be used as a reason for the successful / errored / failed preflight webhook - * @example Experiment executions are not allowed for this environment. - */ - message?: string; - /** - * @description Modifications that should be applied to the execution if the preflight check was successful. - * @example [ - * { - * "type": "set_property_value", - * "propertyKey": "approvedBy", - * "value": "Daniel" - * }, - * { - * "type": "add_value_to_list_property", - * "propertyKey": "observations", - * "value": "This looks interesting!" - * } - * ] - */ - modifications?: (components["schemas"]["AddValueToListProperty"] | components["schemas"]["SetPropertyValue"])[]; - }; - PreflightWebhookUpsertAO: { - /** - * @description The events that you want to intercept. Currently only `experiment.execution.preflight` is supported. - * @example [ - * "experiment.execution.preflight" - * ] - */ - events: string[]; - /** - * @description Additional headers to include in the webhook request. - * @example { - * "X-Custom-Header": "CustomValue", - * "X-Another-Header": "AnotherValue" - * } - */ - headers?: { - [key: string]: string; - }; - /** - * Format: uuid - * @description The id of the webhook or null if a new webhook should be created. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id?: string | null; - /** - * @description The name of the webhook - * @example Preflight Webhook - */ - name: string; - /** - * @description The scope of the webhook / integration - * @example TEAM - * @enum {string} - */ - scope: "GLOBAL" | "TEAM"; - /** - * @description If a secret is provided a signature of the body is computed using `HMAC SHA-256` and sent as `X-SB-Signature` http header. You can use this header to verify the message. - * @example secret123!! - */ - secret?: string; - /** - * @description The body size can get very large as we include all target attributes for each target of your experiments. When having experiments with many targets, it might be useful to filter the attributes to only include the ones you are interested in. You can use the wildcard character '*' to match all attributes or a comma-separated-list of attribute-names. If the field is empty, no attributes will be included. - * @example [ - * "k8s.cluster-name", - * "k8s.deployment" - * ] - */ - targetAttributeIncludes: string[]; - /** - * @description The key of the team if the scope is `TEAM` - * @example ADM - */ - team?: string; - /** - * @description The URL of the webhook - * @example https://example.com/webhook - */ - url: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** - * @description The principal that has performed the logged event - * @example { - * "name": "Jane Doe", - * "role": "ADMIN", - * "email": "example@example.com", - * "username": "1ava2afg-xju33-4c6a-9451-2854584c15be", - * "principalType": "USER" - * } - */ - PrincipalAL: ({ - /** - * @description The principal type that has performed the logged event - * @example USER - * @enum {string} - */ - principalType: "USER" | "ACCESS_TOKEN" | "BATCH_JOB"; - } & (components["schemas"]["AccessTokenPrincipalAL"] | components["schemas"]["BatchPrincipalAL"] | components["schemas"]["UserPrincipalAL"])) | null; - /** - * @description A property association. - * @example { - * "id": "2v1av42-e525-4c00-a13a-1ac32d170724", - * "key": "RESULT_COLOR", - * "editableInExecution": true, - * "required": true, - * "version": 1 - * } - */ - PropertyAssociationAO: { - /** - * @description Always defined to either `EXPERIMENT` for experiment design or run related associations or `SERVICE` for service-associations. Only for the former, an `experimentKey` can be defined and only for the latter, a `serviceId` can be defined - * @default EXPERIMENT - * @example EXPERIMENT - * @enum {string} - */ - associationType: "EXPERIMENT" | "SERVICE"; - /** - * @description Is the property editable in the execution view. Only used when `associationType` is set to `EXPERIMENT`. - * @example true - */ - editableInExecution?: boolean; - /** - * @description The key of the associated experiment. When `associationType` is set to `EXPERIMENT` and `experimentKey` is `null`, it is associated to ALL experiment designs. Can't be changed during updates. - * @example EXP-1 - */ - experimentKey?: string | null; - /** - * Format: uuid - * @description Id of the Property-Association. - */ - id: string; - /** - * @description The key of the property definition - * @example RESULT_COLOR - */ - key: string; - /** - * @description Is the value required? - * @example true - */ - required?: boolean; - /** - * Format: uuid - * @description The serviceId of the associated service. When `associationType` is set to `SERVICE` and `serviceId` is `null`, it is associated to ALL services. Can't be changed during updates. - * @example 3308b47d-5c1f-4f08-a25b-a18fc10f8a56 - */ - serviceId?: string | null; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version: number; - }; - /** - * @description Definition of a property definition that can be associated. - * @example { - * "key": "RESULT_COLOR", - * "label": "Result Color", - * "description": "How would you describe the result of your experiment, thinking in beautiful colors?", - * "dataType": "ENUM", - * "enumValues": [ - * "RED", - * "GREEN", - * "BLUE" - * ], - * "version": 1 - * } - */ - PropertyDefinitionAO: { - /** - * @description The data type of the property - * @example STRING - * @enum {string} - */ - dataType: "STRING" | "STRING_LIST" | "ENUM" | "ENUM_LIST" | "NUMBER" | "NUMBER_LIST" | "MARKDOWN" | "BOOLEAN" | "DATE" | "LINK" | "LINK_LIST"; - /** - * @description The text describing the property. - * @example How would you describe the result of your experiment, thinking in beautiful colors? - */ - description?: string | null; - /** - * @description Valid values if the dataType `ENUM` is used - * @example [ - * "RED", - * "GREEN", - * "BLUE" - * ] - */ - enumValues?: string[]; - /** - * @description The unique key of the property definition - * @example RESULT_COLOR - */ - key: string; - /** - * @description The label shown in the ui for this property - * @example Result color - */ - label: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version: number; - }; - /** - * @description Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the template already exists and should be updated or newly inserted. - * @example { - * "key": "EXAMPLE_CUSTOM_PROPERTY", - * "required": true, - * "editableInExecution": false - * } - */ - PropertyMetadataAO: { - editableInExecution?: boolean; - key: string; - required?: boolean; - }; - QueryLanguagePredicateAO: Record & { - query: string; - }; - RecreateAccessTokenRequestV2AO: { - /** - * Format: date-time - * @description New expiration date for the recreated token. - * @example 2027-01-01T00:00:00Z - */ - expiresAt?: string; - }; - /** @description Filter for time-series report data. */ - ReportFilterAO: { - /** - * Format: date - * @description Start date of the report range (inclusive). - * @example 2026-01-01 - */ - from: string; - /** - * @description The time bucket granularity for report aggregation. - * @example MONTHLY - * @enum {string} - */ - rollup?: "MONTHLY" | "DAILY"; - /** - * Format: date - * @description End date of the report range (inclusive). - * @example 2026-03-01 - */ - to: string; - }; - ScalarValueAO: { - type: "ScalarValueAO"; - } & (Omit & { - /** Format: double */ - value?: number; - }); - SelectExpressionAO: { - attribute: string; - /** Format: int32 */ - count?: number | null; - filter?: string; - /** @enum {string} */ - mode: "fixed" | "percent"; - /** Format: int32 */ - percent?: number | null; - /** - * @description Evaluation scope of a dynamic value, **required for service variables** and rejected for all other variables (environment, experiment, schedule, execution overrides). `service` samples from the service's own targets (its environment narrowed by the service's target query); `environment` samples from the whole environment the service lives in. There is no default — a service variable must state its scope explicitly. - * @example service - * @enum {string|null} - */ - scope?: "service" | "environment" | "service" | "environment" | null; - targetType: string; - type?: string; - }; - /** - * @example { - * "id": "2v1av42-e525-4c00-a13a-1ac32d170724", - * "version": 1, - * "name": "shopping-service", - * "environment": "Global", - * "team": "ADM", - * "logoId": "service-router", - * "logoColor": "blue", - * "query": "aws.account=\"123\" OR aws.account=\"456\"", - * "validations": [ - * { - * "type": "action", - * "parameters": { - * "url": "https://my-service/health", - * "method": "GET" - * }, - * "actionType": "com.steadybit.extension_http.check.periodically" - * } - * ], - * "serviceProfile": "Steadybit Starter", - * "variables": { - * "httpEndpoint": "http://prod.shop.products.internal", - * "targets": { - * "type": "select", - * "targetType": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "attribute": "k8s.deployment", - * "filter": "k8s.namespace=\"shop\"", - * "mode": "fixed", - * "count": 1 - * } - * }, - * "created": "2023-01-01T09:00:00Z", - * "createdBy": { - * "username": "ag1hb7ap-d299-47ab-998f-c2a53b433820", - * "name": "Manuel", - * "pictureUrl": "https://.../picture.png" - * }, - * "edited": "2023-01-01T09:00:00Z", - * "editedBy": { - * "username": "ag1hb7ap-d299-47ab-998f-c2a53b433820", - * "name": "Manuel", - * "pictureUrl": "https://.../picture.png" - * } - * } - */ - ServiceAO: { - /** - * Format: date-time - * @description Timestamp when the service was created - * @example 2023-01-01T09:00:00Z - */ - created: string; - createdBy: components["schemas"]["UserSummaryAO"]; - /** - * Format: date-time - * @description Timestamp when the service was edited the last time - * @example 2023-01-01T09:00:00Z - */ - edited: string; - editedBy: components["schemas"]["UserSummaryAO"]; - /** - * @description The name of the environment to be used - * @example Global - */ - environment: string; - /** - * Format: uuid - * @description The unique id of the service - */ - id?: string; - /** - * @description Color scheme of the logo used to identify the service in the Platform UI - * @default blue - * @example orangeLight - */ - logoColor: string; - /** - * @description Identifier of the logo used to identify the service in the Platform UI - * @default service - * @example service-router - */ - logoId: string; - /** - * @description The name of the service - * @example calculator-service - */ - name: string; - /** - * @description The properties of the service - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this service!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description Query-Language predicate, specifies the targets belonging to this Service - * @example aws.account="123" OR aws.account="456" - */ - query: string; - /** - * @description Name of the service profile that should be used for this service - * @example Steadybit provided - */ - serviceProfile: string; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - /** @description List of validations to be executed against the service */ - validations: components["schemas"]["ExperimentStepActionAO"][]; - /** - * @description Variables owned by the service. Each value is either a constant string, an array of constant strings, or a select expression object. A select-expression value **must set `scope`** (`service` or `environment`) — the request is rejected otherwise. On `POST /api/services` (upsert): omitting this field leaves existing variables untouched, an empty object removes all of them. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal", - * "targetServices": [ - * "gateway", - * "hot-deals", - * "fashion-bestseller" - * ] - * } - */ - variables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * Format: int32 - * @description Version for optimistic locking - * @example 1 - */ - version?: number; - }; - ServiceExperimentAO: { - /** - * @description The type of the association - * @example PROVIDED - * @enum {string} - */ - associationType: "PROVIDED" | "CUSTOM"; - /** - * @description The category of the experiment. - * @example Scalability - */ - category: string; - /** - * @description The key of the experiment. If type is PROVIDED and the experiment has not been created, the experimentKey will be null - * @example EX-754 - */ - experimentKey?: string | null; - /** - * Format: uuid - * @description The id of the experiment template if type is PROVIDED and the experiment has not been created. - * @example 6bea7aec-3572-44cf-9151-c6ada57d08ca - */ - templateId?: string | null; - }; - /** @description A service profile that groups experiment templates by category */ - ServiceProfileAO: { - /** - * Format: date-time - * @description Timestamp when the profile was created - * @example 2023-01-01T09:00:00Z - */ - created: string; - /** - * @description Username of the user that created the profile - * @example admin@example.com - */ - createdBy: string; - /** - * @description Whether this is the default profile. - * @example true - */ - defaultProfile: boolean; - /** - * Format: date-time - * @description Timestamp when the profile was last edited - * @example 2023-01-01T09:00:00Z - */ - edited: string; - /** - * @description Username of the user that last edited the profile - * @example admin@example.com - */ - editedBy: string; - /** - * Format: uuid - * @description The unique id of the profile - */ - id: string; - /** - * @description The name of the profile - * @example Default Resilience Tests - */ - name: string; - /** - * @description Origin of a service profile - * @example CUSTOM - * @enum {string} - */ - origin: "PROVIDED" | "CUSTOM"; - /** @description Template entries in this profile */ - templates: components["schemas"]["ServiceProfileCategoryAO"][]; - /** - * Format: int32 - * @description Version for optimistic locking - * @example 1 - */ - version: number; - }; - /** @description Template entries in this profile */ - ServiceProfileCategoryAO: { - /** - * @description The category name - * @example Scalability - */ - category?: string | null; - /** - * @description The template IDs in this category - * @example [ - * "6bea7aec-3572-44cf-9151-c6ada57d08ca", - * "6bea7aec-3572-44cf-9151-c6ada57d08cb" - * ] - */ - templateIds?: string[]; - }; - /** - * @description The risk for a given service. - * @example { - * "risk": 78, - * "categoryRisks": { - * "Scalability": { - * "total": 92, - * "experiment": 100, - * "advice": 58 - * }, - * "Redundancy": { - * "total": 91, - * "experiment": 100, - * "advice": 51 - * }, - * "Dependency": { - * "total": 50, - * "experiment": 50, - * "advice": 50 - * } - * }, - * "experimentRisks": [ - * { - * "experimentKey": "ADM-8", - * "risk": 100 - * }, - * { - * "experimentKey": "ADM-16", - * "risk": 100 - * } - * ], - * "lastCalculated": "2026-03-31T10:13:38.902372Z" - * } - */ - ServiceRiskAO: { - /** @description Risk per category */ - categoryRisks?: { - [key: string]: components["schemas"]["CategoryRiskAO"]; - }; - /** @description Risk per experiment */ - experimentRisks?: components["schemas"]["ExperimentRiskAO"][]; - /** - * Format: date-time - * @description Timestamp of the last risk calculation - */ - lastCalculated?: string | null; - /** - * Format: int32 - * @description The overall risk for the service - */ - risk?: number; - }; - /** @description Filter for service risk report data, optionally scoped to specific teams, environments, and services. */ - ServiceRiskReportFilterAO: { - /** @description Restrict results to services whose categoryRisks map contains any of the given category keys. */ - categoryKeys?: string[] | null; - /** @description Restrict results to the given environment IDs. If not provided, all environments are included. */ - environmentIds?: string[] | null; - /** - * Format: date - * @description Start date of the report range (inclusive). - * @example 2026-01-01 - */ - from: string; - /** - * @description The time bucket granularity for report aggregation. - * @example MONTHLY - * @enum {string} - */ - rollup?: "MONTHLY" | "DAILY"; - /** @description Restrict results to the given service IDs. If not provided, all services are included. */ - serviceIds?: string[] | null; - /** @description Filter on the service's enum/enum-list custom property values. Map of property key and values; values are OR within a key, AND across keys. */ - serviceProperties?: { - [key: string]: string[]; - } | null; - /** @description Restrict results to the given team IDs. If not provided, all teams are included. */ - teamIds?: string[] | null; - /** - * Format: date - * @description End date of the report range (inclusive). - * @example 2026-03-01 - */ - to: string; - }; - ServiceSummaryAO: { - /** - * @description The name of the environment to be used - * @example Global - */ - environment: string; - /** - * Format: uuid - * @description The unique id of the service - */ - id?: string; - /** - * @description Color scheme of the logo used to identify the service in the Platform UI - * @example blue - */ - logoColor?: string; - /** - * @description Identifier of the logo used to identify the service in the Platform UI - * @example 1 - */ - logoId?: string; - /** - * @description The name of the service - * @example calculator-service - */ - name: string; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - }; - /** - * @description Set a value of an execution property - * @example { - * "type": "set_property_value", - * "propertyKey": "approvedBy", - * "value": "Daniel" - * } - */ - SetPropertyValue: { - type: "SetPropertyValue"; - } & (Omit & { - /** - * @description The key of the property. - * @example approvedBy - */ - propertyKey: string; - /** - * @description The value to be set. Datatype depends on the property definition. Could be a string, a number or a list - * @example Daniel - */ - value: Record; - }); - SlackWebhookAO: { - /** - * @description The name of the slack channel - * @example #steadybit-notifications - */ - channel: string; - /** - * @description The events that are being sent or a list containing a single `*` if all supported event types should be used. - * - * Supported Events: - * - "experiment.execution.requested" - * - "experiment.execution.created" - * - "experiment.execution.preflight" - * - "experiment.execution.completed" - * - "experiment.execution.failed" - * - "experiment.execution.errored" - * - "experiment.execution.canceled" - * - "experiment.execution.step-started" - * - "experiment.execution.step-completed" - * - "experiment.execution.step-failed" - * - "experiment.execution.step-errored" - * - "experiment.execution.step-canceled" - * - "experiment.execution.step-skipped" - * - "killswitch.engaged" - * - "killswitch.disengaged" - * @example [ - * "experiment.execution.created", - * "experiment.execution.completed" - * ] - */ - events?: string[]; - /** - * @description The icon URL of the slack channel, defaults to the Steadybit logo. - * @example https://platform.steadybit.com/assets/logo512.png - */ - iconUrl?: string; - /** - * Format: uuid - * @description The id of the webhook - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - /** - * @description The name of the integration - * @example Slack ACME corporation. - */ - name: string; - /** - * @description The scope of the webhook / integration - * @example TEAM - * @enum {string} - */ - scope: "GLOBAL" | "TEAM"; - /** - * @description The key of the team if the scope is `TEAM` - * @example ADM - */ - team?: string; - /** - * @description The Slack webhook url - * @example https://hooks.slack.com/services/ - */ - url: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version: number; - }; - SlackWebhookUpsertAO: { - /** - * @description The name of the slack channel - * @example #steadybit-notifications - */ - channel: string; - /** - * @description The events that are being sent or a list containing a single `*` if all supported event types should be used. - * - * Supported Events: - * - "experiment.execution.requested" - * - "experiment.execution.created" - * - "experiment.execution.preflight" - * - "experiment.execution.completed" - * - "experiment.execution.failed" - * - "experiment.execution.errored" - * - "experiment.execution.canceled" - * - "experiment.execution.step-started" - * - "experiment.execution.step-completed" - * - "experiment.execution.step-failed" - * - "experiment.execution.step-errored" - * - "experiment.execution.step-canceled" - * - "experiment.execution.step-skipped" - * - "killswitch.engaged" - * - "killswitch.disengaged" - * @example [ - * "experiment.execution.created", - * "experiment.execution.completed" - * ] - */ - events?: string[]; - /** - * @description The icon URL of the slack channel, defaults to the Steadybit logo. - * @example https://platform.steadybit.com/assets/logo512.png - */ - iconUrl?: string; - /** - * Format: uuid - * @description The id of the webhook or null if a new webhook should be created. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id?: string | null; - /** - * @description The name of the integration - * @example Slack ACME corporation. - */ - name: string; - /** - * @description The scope of the webhook / integration - * @example TEAM - * @enum {string} - */ - scope: "GLOBAL" | "TEAM"; - /** - * @description The key of the team if the scope is `TEAM` - * @example ADM - */ - team?: string; - /** - * @description The Slack webhook url - * @example https://hooks.slack.com/services/ - */ - url: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** - * @description Summary of a single advice for the referenced target - * @example { - * "type": "com.steadybit.extension_kubernetes.advice.k8s-cpu-limit", - * "label": "Limit CPU Resources", - * "tags": [ - * "kubernetes", - * "limit", - * "cpu" - * ], - * "status": "Validation needed", - * "summary": "You already took action and configured a CPU limit. Validate your configuration via an experiment." - * } - */ - TargetAdviceAdvicePartAO: { - /** - * @description Human readable label of the advice - * @example Limit CPU Resources - */ - label?: string; - /** - * @description Current status of the advice applied to the referenced target. One of 'Action Needed', 'Validation needed', 'Implemented'. - * @example Validation needed - */ - status?: string; - /** - * @description Summary of the advice to describe the current status and next step. - * @example You already took action and configured a CPU limit. Validate your configuration via an experiment - */ - summary: string; - /** - * @description Tags associated to the advice definition - * @example [ - * "AWS", - * "Kubernetes" - * ] - */ - tags?: string[]; - /** - * @description Identifier of the advice definition that is applied to the target - * @example com.steadybit.extension_kubernetes.advice.k8s-cpu-limit - */ - type?: string; - }; - /** - * @description A pageable list of pieces of advice. - * @example { - * "target": { - * "type": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "reference": "prod-demo/steadybit-demo/gateway", - * "label": "gateway" - * }, - * "advice": { - * "type": "com.steadybit.extension_kubernetes.advice.k8s-cpu-limit", - * "label": "Limit CPU Resources", - * "tags": [ - * "kubernetes", - * "limit", - * "cpu" - * ], - * "status": "Validation needed", - * "summary": "You already took action and configured a CPU limit. Validate your configuration via an experiment." - * }, - * "url": "https://platform.steadybit.com/permalink/advice/eyAiZW52..." - * } - */ - TargetAdviceAO: { - advice?: components["schemas"]["TargetAdviceAdvicePartAO"]; - target?: components["schemas"]["TargetAdviceTargetPartAO"]; - /** - * @description URL to see all details to this advice for this target - * @example https://platform.steadybit.com/permalink/advice/eyAiZW52... - */ - url?: string | null; - }; - /** - * @description A reference to identify the target for a given advice - * @example { - * "type": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "reference": "prod-demo/steadybit-demo/gateway", - * "label": "gateway" - * } - */ - TargetAdviceTargetPartAO: { - /** - * @description Human readable identifier to be displayed for the target - * @example gateway - */ - label: string; - /** - * @description Unique stable identifier of the target for this target type - * @example prod-demo/steadybit-demo/gateway - */ - reference: string; - /** - * @description Target type of the referenced target - * @example com.steadybit.extension_kubernetes.kubernetes-deployment - */ - type: string; - }; - TargetAgentIdPredicateAO: Record & { - /** Format: uuid */ - agentId: string; - }; - TargetAO: { - /** - * @description The ID of the agent this target belongs to - * @example 019504aa-0f60-781b-862d-9763010d5948 - */ - agentId?: string; - /** - * @description The attributes for this target. A key may be associated multiple time to a single target. - * @example [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * } - * ] - */ - attributes?: components["schemas"]["AttributeAO"][]; - /** - * @description The name of the target - * @example fashion-bestseller - */ - name?: string; - /** - * @description The name of the target - * @example com.steadybit.extension_kubernetes.kubernetes-deployment - */ - type?: string; - /** - * Format: int64 - * @description The version of the target, will be increased by every update via the agent. - * @example 0 - */ - version?: number; - }; - TargetAttributeKeyCountPredicateAO: Record & { - key: string; - value: string; - /** @enum {string} */ - valueCountOperator: "EQUAL" | "NOT_EQUAL" | "GREATER_THAN" | "GREATER_THAN_OR_EQUAL" | "LESS_THAN" | "LESS_THAN_OR_EQUAL"; - }; - TargetAttributeKeyPredicateAO: Record & { - key: string; - operator: string; - }; - TargetAttributeKeyPresencePredicateAO: Record & { - key: string; - /** @enum {string} */ - presenceOperator: "PRESENT" | "NOT_PRESENT"; - }; - TargetAttributeKeyValuePredicateAO: Record & { - key: string; - operator: string; - values: string[]; - }; - /** - * @description A target that is expected to be effected by this action. - * @example { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - */ - TargetExecutionAO: { - /** - * @description The agent that processed this target-action command and forwarded it to the proper extension instance. - * @example prod-demo/steadybit-agent/steadybit-agent-0 - */ - agentHostname?: string; - /** - * @description List of artifact identifiers that are associated with this target execution - * @example [ - * "jmeter-report.zip", - * "system-metrics.csv" - * ] - */ - artifacts?: string[]; - /** - * @description A set of attributes that have been discovered for this target. A key may be associated multiple time to a single target. - * @example [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * } - * ] - */ - attributes?: components["schemas"]["AttributeAO"][]; - /** - * Format: uuid - * @description Unique identifier of this target execution - * @example 019aba52-558d-7d44-b793-92839f3c3152 - */ - id?: string; - /** - * @description Identifier of the target that is expected to be effected - * @example docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea - */ - name?: string; - /** - * @description Reason on a per target-level why the experiment failed or errored. If this step didn't failed or errored (`state != 'FAILED' and state != 'ERRORED') the reason is `null`. - * @example Failed to start Stop Container (com.steadybit.extension_container.stop) - */ - reason?: string; - /** - * @description Optional additional reason details on a per target-level why the the experiment failed or errored. - * @example Could not read state of target container: exit status 1 (time="2023-09-29T12:41:32Z" level=error msg="container does not exist" - */ - reasonDetails?: string; - /** - * @description The source (i.e. call to the extension) that caused the step to error or fail. - * @example POST http://11.20.86.255:9093/com.steadybit.extension_container.container_stop/prepare - */ - source?: string; - /** - * @description State of this specific step on a per target-level. - * @example COMPLETED - */ - state?: string; - summary?: components["schemas"]["TargetExecutionSummaryAO"]; - /** - * @description Type of the target that is expected to be effected - * @example container - */ - type?: string; - }; - /** - * @description An attributes (key-value-pair) that is associated to a target - * @example { - * "key": "container.port", - * "value": "51152:2376" - * } - */ - TargetExecutionAOAttributeAO: { - /** - * @description The key of the attribute, may be associated multiple times to the same target - * @example container.engine - */ - key: string; - /** - * @description The value of the attribute - * @example docker - */ - value: string; - }; - /** - * @description A summary for a target execution - * @example { - * "text": "Hello world!", - * "level": "INFO" - * } - */ - TargetExecutionSummaryAO: { - level?: string; - text?: string; - }; - TargetNamePredicateAO: Record & { - name: string; - }; - /** - * @description Query defining the overall superset of targets being effected - * @example [ - * { - * "key": "container.host/name", - * "operator": "EQUALS", - * "values": [ - * "docker-desktop/minikube" - * ] - * } - * ] - */ - TargetPredicateAO: components["schemas"]["NegationTargetPredicateAO"] | components["schemas"]["QueryLanguagePredicateAO"] | components["schemas"]["TargetAgentIdPredicateAO"] | components["schemas"]["TargetAttributeKeyCountPredicateAO"] | components["schemas"]["TargetAttributeKeyPredicateAO"] | components["schemas"]["TargetAttributeKeyPresencePredicateAO"] | components["schemas"]["TargetAttributeKeyValuePredicateAO"] | components["schemas"]["TargetNamePredicateAO"] | components["schemas"]["TargetTypePredicateAO"]; - /** @description A predefined target predicate template that users can apply when configuring an action. */ - TargetPredicateTemplateAO: { - /** @description Description of the template. */ - description?: string; - /** @description Display name of the template. */ - name?: string; - /** @description Query language template. */ - template: string; - }; - TargetSelectorAO: { - targetQuery?: string; - type: string; - }; - TargetStatsRequest: { - predicate?: components["schemas"]["TargetPredicateAO"]; - /** - * @description Alternative to `predicate`. If both `query` and `predicate` will be provided, `query` will override the `predicate`. - * @example (aws.account="123" OR aws.account="456" - */ - query?: string | null; - }; - TargetTypePredicateAO: Record & { - types: string[]; - }; - /** - * @description The team in which the event was triggered - * @example { - * "id": "a2167b29-e73b-4445-8468-4670a0b459b3", - * "key": "ADMIN", - * "name": "Administrators" - * } - */ - TeamAL: { - /** Format: uuid */ - id: string; - key: string; - name: string; - } | null; - /** - * @description A team that is uniquely identified via it's teamKey and has members, allowed environments and actions. - * @example { - * "id": "71ab0180-8abc-4d30-8acb-6aa024e3065f", - * "key": "ADM", - * "name": "Administrators", - * "version": 1, - * "logoId": "1", - * "logoColor": "cyanDark", - * "allowedActions": [ - * "com.steadybit.extension_host.host.stress-cpu" - * ], - * "allowedEnvironments": [ - * "Global" - * ], - * "members": [ - * { - * "username": "auth0|11a9315afc84590069cd53b2", - * "role": "OWNER" - * }, - * { - * "username": "auth0|13b1s51vg184590069cd51ab", - * "role": "MEMBER" - * }, - * { - * "username": "auth0|1va2g15afc84590069cd53c3", - * "role": "MEMBER" - * } - * ] - * } - */ - TeamAO: { - /** - * @description Set of allowed actions that can be used in an experiment of this team - * @example [ - * "com.steadybit.extension_host.host.stress-cpu" - * ] - */ - allowedActions: string[]; - /** - * @description Set of allowed environments, identified via name - * @example [ - * "Global" - * ] - */ - allowedEnvironments: string[]; - /** @description An optional description of a team */ - description?: string; - /** Format: uuid */ - id?: string; - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - /** - * @description Color scheme of the logo used to identify the team in the Platform UI - * @example cyanDark - */ - logoColor?: string; - /** - * @description Identifier of the logo used to identify the team in the Platform UI - * @example 1 - */ - logoId?: string; - /** - * @description How a team or team membership is managed - * @example MANUAL - * @enum {string} - */ - managedBy?: "MANUAL" | "OIDC" | "LDAP"; - /** - * @description Members that are associated to this team - * @example { - * "username": "auth0|11a9315afc84590069cd53b2", - * "role": "OWNER" - * } - */ - members: components["schemas"]["MemberAO"][]; - /** - * @description Name of a team - * @example ADMIN - */ - name: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** - * @description Environment assigned to a team. - * @example { - * "name": "Global" - * } - */ - TeamEnvironmentAO: { - /** - * @description Name of the environment. - * @example Global - */ - name: string; - }; - /** - * @description List of environments that are assigned to this team. - * @example { - * "environments": [ - * { - * "name": "Global" - * }, - * { - * "name": "Shop Production" - * } - * ] - * } - */ - TeamEnvironmentsAO: { - /** - * @description Environments that are assigned to this team - * @example [ - * { - * "name": "Global" - * }, - * { - * "name": "Shop Production" - * } - * ] - */ - environments: components["schemas"]["TeamEnvironmentAO"][]; - }; - /** - * @description Update request to change the environments of a specific team. - * @example { - * "environments": [ - * { - * "name": "Global" - * }, - * { - * "name": "Shop Production" - * } - * ] - * } - */ - TeamEnvironmentsUpdateAO: { - /** - * @description Environments that should be updated to this team - * @example [ - * { - * "name": "Global" - * }, - * { - * "name": "Shop Production" - * } - * ] - */ - environments: components["schemas"]["TeamEnvironmentAO"][]; - }; - /** - * @description List of members that are part of this team. - * @example { - * "members": [ - * { - * "username": "13av2737-b318-4048-a79d-4789d645bc31", - * "role": "OWNER" - * }, - * { - * "username": "google-oauth2|931412422966030837225", - * "role": "MEMBER" - * } - * ] - * } - */ - TeamMembersAO: { - /** - * @description Members that are associated to this team - * @example [ - * { - * "username": "13av2737-b318-4048-a79d-4789d645bc31", - * "name": "jane doe", - * "email": "jane.doe@steadybit.com", - * "role": "OWNER" - * }, - * { - * "username": "google-oauth2|931412422966030837225", - * "name": "john smith", - * "email": "jane.smith@steadybit.com", - * "role": "MEMBER" - * } - * ] - */ - members: components["schemas"]["MemberAO"][]; - }; - /** - * @description Team members that should be removed from a given team. You can specify the user to be removed via the internal identifier `usernames` or via the user's `emails`. If you specify both, both set of users will be removed. - * @example { - * "emails": [ - * "jane.doe@example.com", - * "javier.rodriguez@example.com" - * ], - * "usernames": [ - * "13av2737-b318-4048-a79d-4789d645bc31" - * ] - * } - */ - TeamMembersRemoveAO: { - emails?: string[]; - usernames?: string[]; - }; - /** - * @description Update request to change the members of a specific team. Specify either username, being a Steadybit user id, or the email address of the user. - * @example { - * "members": [ - * { - * "email": "jane.doe@example.com", - * "role": "OWNER" - * }, - * { - * "email": "javier.rodriguez@example.com", - * "role": "MEMBER" - * }, - * { - * "username": "auth0|1va2g15afc84590069cd53c3", - * "role": "MEMBER" - * } - * ] - * } - */ - TeamMembersUpdateAO: { - /** - * @description Members that should be updated to this team - * @example { - * "email": "jane.doe@example.com", - * "role": "OWNER" - * } - */ - members: components["schemas"]["MemberUpdateAO"][]; - }; - /** - * @description List of teams. - * @example { - * "teams": [ - * { - * "id": "714b0180-8abc-4d30-8acb-6aa024e3065f", - * "key": "ADM", - * "name": "Administrators", - * "version": 1, - * "logoId": "1", - * "logoColor": "cyanDark", - * "allowedActions": [ - * "com.steadybit.extension_host.host.stress-cpu" - * ], - * "allowedEnvironments": [ - * "Global" - * ], - * "members": [ - * { - * "username": "auth0|11a9315afc84590069cd53b2", - * "role": "OWNER" - * } - * ] - * } - * ] - * } - */ - TeamSummariesAO: { - teams?: components["schemas"]["TeamAO"][]; - }; - /** - * @description The tenant in which the event was performed. Only relevant in case you are using multiple tenants of the Steadybit platform. - * @example { - * "key": "Demo", - * "name": "Demo Tenant" - * } - */ - TenantAL: { - key: string; - name: string; - }; - /** @description A named time series with date-value pairs. */ - TimeSeriesAO: { - /** @description Name of the series, corresponding to the group label. */ - name?: string; - /** @description Date-value pairs, each serialized as [date, count]. */ - values?: components["schemas"]["TimeSeriesValueAO"][]; - }; - /** - * @description Time-series report data with metadata describing the query context. - * @example { - * "from": "2026-01-01", - * "to": "2026-03-01", - * "rollup": "MONTHLY", - * "groupBy": "NONE", - * "series": [ - * { - * "name": "users", - * "values": [ - * [ - * "2026-01-01", - * 23 - * ], - * [ - * "2026-02-01", - * 42 - * ], - * [ - * "2026-03-01", - * 42 - * ] - * ] - * } - * ] - * } - */ - TimeSeriesReportAO: { - /** - * Format: date - * @description Start date of the requested report range (inclusive). - * @example 2026-01-01 - */ - from?: string; - /** - * @description The grouping dimension applied to the series. - * @enum {string} - */ - groupBy?: "NONE" | "STATE" | "TRIGGER" | "ACTION" | "CREATED_VIA" | "ORIGIN" | "ISSUES_FIXED" | "ISSUES_DISCOVERED" | "RISK_LEVEL" | "CATEGORY"; - /** - * @description The time bucket granularity for report aggregation. - * @enum {string} - */ - rollup?: "MONTHLY" | "DAILY"; - /** @description The time-series data, one entry per group. */ - series?: components["schemas"]["TimeSeriesAO"][]; - /** - * Format: date - * @description End date of the requested report range (inclusive). - * @example 2026-03-01 - */ - to?: string; - }; - /** @description A date-value pair serialized as a two-element array [date, count]. */ - TimeSeriesValueAO: { - /** - * Format: date - * @description Start date of the time bucket. - * @example 2026-01-01 - */ - date?: string; - /** - * Format: int32 - * @description The count for this time bucket. - */ - value?: number; - }; - /** - * @description Update the experiment with the given experiment design. - * @example { - * "name": "Blackhole Hot-deals", - * "team": "ADM", - * "environment": "Global", - * "lanes": [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ], - * "properties": { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - * } - */ - UpdateExperimentAO: { - /** - * @description The name of the environment to be used - * @example Global - */ - environment?: string; - /** - * @description Variables that will be used when the experiment will be executed. Experiment variables will override existing environment variables. Each value is either a constant string, an array of constant strings, or a select expression object (`{"type":"select",...}`). - * @example { - * "httpEndpoint": "http://dev.shop.products.internal", - * "targetServices": [ - * "gateway", - * "hot-deals", - * "fashion-bestseller" - * ], - * "httpEndpointZones": { - * "type": "select", - * "targetType": "com.steadybit.extension_container.container", - * "attribute": "aws.zone", - * "mode": "fixed", - * "count": 2 - * } - * } - */ - experimentVariables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * @description An optional external identifier used for create-or-update semantics. - * @example 1234567 - */ - externalId?: string; - /** - * @deprecated - * @description An optional external reference. Will be removed and is replaced by tags. If used with experiment creation, the value will be added as a tag. - * @example INCIDENT-4711 - */ - externalReference?: string; - /** - * @description The hypothesis that is validated by the experiment - * @example System is able to survive a latency in the network of 1500ms - */ - hypothesis?: string; - /** - * @description The lanes (steps executed in parallel) in the experiment. Each lane consists of multiple steps that are executed sequential per lane. - * @example [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ] - */ - lanes: components["schemas"]["ExperimentLaneAO"][]; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name: string; - /** - * @description The properties of the experiment - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description Team keys with which the experiment is shared with - * @example [OPS, SHOP] - */ - sharedTeams?: string[]; - /** - * @description An optional set of tags you can use to search for. - * @example [ - * "myTag", - * "myOtherTag" - * ] - */ - tags?: string[]; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - }; - /** - * @description Experiment execution data that should be used only for that specific experiment execution and will not update the experiment design. - * @example { - * "propertiesVersion": 1, - * "properties": { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment execution!" - * }, - * "propertiesOrder": [ - * "EXAMPLE_CUSTOM_PROPERTY" - * ] - * } - */ - UpdateExperimentExecutionPropertiesAO: { - /** - * @description The properties of the experiment execution - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment execution!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description The order of the properties for this experiment execution. This may include global and experiment scoped assigned properties. - * @example [ - * "EXAMPLE_CUSTOM_PROPERTY" - * ] - */ - propertiesOrder?: string[]; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - propertiesVersion?: number | null; - }; - /** - * @description Update an experiment based on an experiment template. - * @example { - * "placeholders": [ - * { - * "key": "CLUSTER", - * "value": "demo-cluster" - * }, - * { - * "key": "BOOL", - * "value": true - * }, - * { - * "key": "NUMBER", - * "value": 15 - * }, - * { - * "key": "KEYVALUE", - * "value": [ - * { - * "key": "example-a", - * "value": "abc" - * }, - * { - * "key": "example-b", - * "value": "123" - * } - * ] - * }, - * { - * "key": "LIST", - * "value": [ - * "entry1", - * "entry2", - * "entry3" - * ] - * }, - * { - * "key": "FILE", - * "value": { - * "fileName": "example.txt", - * "data": "SGVsbG8gV29ybGQh" - * } - * } - * ], - * } - */ - UpdateExperimentFromTemplateAO: { - /** @description List of template placeholder values */ - placeholders?: components["schemas"]["ExperimentTemplatePlaceholderValueAO"][]; - }; - /** - * @description Update or insert environment. - * @example { - * "id": "2v1av42-e525-4c00-a13a-1ac32d170724", - * "name": "Global", - * "version": 0, - * "query": "aws.account=\"123\" OR aws.account=\"456\"" - * } - */ - UpsertEnvironmentAO: { - /** - * Format: uuid - * @description Unique identifier of a environment - */ - id?: string; - /** - * @description Name of the environment. - * @example Global - */ - name: string; - predicate?: components["schemas"]["TargetPredicateAO"]; - /** - * @description Alternative to `predicate`. If both `query` and `predicate` will be provided, `query` will override the `predicate`. - * @example aws.account="123" OR aws.account="456" - */ - query?: string | null; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number; - }; - /** - * @description An update or insert for an experiment schedule - * @example { - * "experimentKey": "ADM-8", - * "cron": "30 * * * * ? *" - * } - */ - UpsertExperimentScheduleAO: { - /** - * @description Should the experiment run if another experiment is running? Default is true. - * @example true - */ - allowParallel?: boolean; - /** - * @description Cron expression for the experiment schedule. Can't be used in combination with `startAt`. - * @example 0 15 10 ? * * - */ - cron?: string | null; - /** - * @description If `false`, the schedule is deactivated and no experiment will be executed. Default is true. - * @example false - */ - enabled?: boolean; - /** - * @description The experiment that should be scheduled. - * @example ADM-123 - */ - experimentKey: string; - /** - * @description The unique identifier of the schedule. If not set, a new schedule will be created. - * @example 01951394-727f-76a0-8675-c7519ebd0ff5 - */ - id?: string | null; - /** - * Format: date-time - * @description Start date for a single execution. Can't be used in combination with `cron`. - */ - startAt?: string | null; - /** - * @description Optional timezone for a experiment schedule. Can only be used with `cron`. - * @example Europe/Berlin - */ - timezone?: string | null; - /** - * @description Variables that will be used when the experiment will be executed. The variables will override existing environment or experiment variables. Each value is either a constant string, an array of constant strings, or a select expression object. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - variables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - }; - /** - * @description Insert or update the experiment template in Steadybit. The `id` will be used to identify whether the template already exists and should be updated or newly inserted. - * @example { - * "templateTitle": "HTTP Endpoint remains functional during Kubernetes Rollout Restart", - * "templateDescription": "Test if a given HTTP Endpoint remains funcitonal if a Kubernetes deployment is restarted.", - * "placeholders": [ - * { - * "key": "HTTP_ENDPOINT", - * "name": "HTTP Endpoint", - * "description": "Which HTTP Endpoint should be checked during experiment execution?" - * }, - * { - * "key": "DEPLOYMENT", - * "name": "Kubernetes Deployment", - * "description": "Which Kubernetes deployment do you want to restart?" - * }, - * { - * "key": "CLUSTER", - * "name": "Kubernetes Cluster", - * "description": "In which Kubernetes cluster is the deployment deployed to?" - * }, - * { - * "key": "NAMESPACE", - * "name": "Kubernetes Namespace", - * "description": "In which Kubernetes namespace is the deployment deployed to?" - * } - * ], - * "tags": [ - * "Kubernetes" - * ], - * "lanes": [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "60s", - * "headers": [], - * "method": "GET", - * "successRate": "100", - * "maxConcurrent": 5, - * "followRedirects": false, - * "readTimeout": "5s", - * "connectTimeout": "5s", - * "requestsPerSecond": 1, - * "url": "[[HTTP_ENDPOINT]]", - * "statusCode": "200-299" - * }, - * "actionType": "com.steadybit.extension_http.check.periodically" - * } - * ] - * }, - * { - * "steps": [ - * { - * "type": "wait", - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * }, - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "wait": false - * }, - * "actionType": "com.steadybit.extension_kubernetes.rollout-restart", - * "radius": { - * "targetType": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.cluster-name", - * "operator": "EQUALS", - * "values": [ - * "[[CLUSTER]]" - * ] - * }, - * { - * "key": "k8s.namespace", - * "operator": "EQUALS", - * "values": [ - * "[[NAMESPACE]]" - * ] - * }, - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "[[DEPLOYMENT]]" - * ] - * } - * ] - * }, - * "percentage": 50 - * } - * }, - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "10m" - * }, - * "actionType": "com.steadybit.extension_kubernetes.rollout-status", - * "radius": { - * "targetType": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.cluster-name", - * "operator": "EQUALS", - * "values": [ - * "[[CLUSTER]]" - * ] - * }, - * { - * "key": "k8s.namespace", - * "operator": "EQUALS", - * "values": [ - * "[[NAMESPACE]]" - * ] - * }, - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "[[DEPLOYMENT]]" - * ] - * } - * ] - * }, - * "percentage": 50 - * } - * } - * ] - * } - * ], - * "properties": { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * }, - * "propertiesMetadata": [ - * { - * "key": "EXAMPLE_CUSTOM_PROPERTY", - * "required": true, - * "editableInExecution": false - * } - * ] - * } - */ - UpsertExperimentTemplateAO: { - /** - * @description Name of the experiment created by this template. If omitted, the name needs to be added when the template is used. - * @example Shop survives unavailability of database - */ - experimentName?: string | null; - /** - * @description Should the experiment template be hidden - * @example false - */ - hidden?: boolean; - /** - * @description The hypothesis that is validated by the experiment - * @example System is able to survive a latency in the network of 1500ms - */ - hypothesis?: string; - /** Format: uuid */ - id?: string | null; - /** - * @description The lanes (steps executed in parallel) in the experiment template. Each lane consists of multiple steps that are executed sequential per lane. - * @example [ - * { - * "steps": [ - * { - * "type": "action", - * "ignoreFailure": false, - * "parameters": { - * "duration": "30s" - * }, - * "actionType": "com.steadybit.extension_host.stress-cpu", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "k8s.deployment", - * "operator": "EQUALS", - * "values": [ - * "hot-deals" - * ] - * } - * ] - * }, - * "query": null, - * "percentage": 100 - * } - * } - * ] - * } - * ] - */ - lanes: components["schemas"]["ExperimentLaneAO"][]; - /** @description A list of placeholders used in this experiment template. */ - placeholders?: components["schemas"]["ExperimentTemplatePlaceholderAO"][]; - /** - * @description The properties of the experiment - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this experiment!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description Metadata for properties used in this template. - * @example [ - * { - * "key": "EXAMPLE_CUSTOM_PROPERTY", - * "required": true, - * "editableInExecution": false - * } - * ] - */ - propertiesMetadata?: components["schemas"]["PropertyMetadataAO"][]; - /** @description A list of tags for this experiment template. (Up to 5) */ - tags?: string[]; - /** @description A brief description what the template is doing. */ - templateDescription: string; - /** - * @description The title of the template - * @example Shop survives unavailability of database - */ - templateTitle: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - UpsertHubAO: { - /** - * @description Website address of the the hub - * @example https://hub.steadybit.com/ - */ - hubLink?: string | null; - /** @description Name of the hub */ - hubName: string; - /** Format: uuid */ - id?: string | null; - /** - * @description HTTP address of the the hub's repository - * @example https://github.com/steadybit/reliability-hub-db - */ - repositoryUrl: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** @description Create or update a saved view of the explorer landscape. */ - UpsertLandscapeViewAO: { - colorBy?: components["schemas"]["LandscapeViewColorByAO"]; - /** - * @description Description of the saved view. - * @example All shop workloads grouped by namespace. - */ - description?: string; - /** - * @description Name of the environment the view is scoped to. - * @example Global - */ - environment?: string; - /** - * @description Explorer filter query narrowing the targets shown on the landscape. - * @example k8s.namespace="shop" - */ - filterQuery?: string; - /** @description Ordered list of group-by dimensions the targets are grouped by, each with its own advanced configuration. */ - groupBy?: components["schemas"]["LandscapeViewGroupByAO"][]; - /** - * @description Title of the saved view. - * @example Kubernetes by namespace - */ - name?: string; - /** - * @description Whether reliability advice is shown on the landscape. - * @example false - */ - showAdvice?: boolean; - /** - * @description Attribute key the size of a target is derived from. - * @example k8s.container.cpu.limit - */ - sizeBy?: string; - /** - * @description Key of the team the saved view belongs to. - * @example ADM - */ - team: string; - }; - /** - * @description A property association upsert. - * @example { - * "key": "RESULT_COLOR", - * "editableInExecution": true, - * "required": true - * } - */ - UpsertPropertyAssociationAO: { - /** - * @description Always defined to either `EXPERIMENT` for experiment design or run related associations or `SERVICE` for service-associations. Only for the former, an `experimentKey` can be defined and only for the latter, a `serviceId` can be defined - * @default EXPERIMENT - * @example EXPERIMENT - * @enum {string} - */ - associationType: "EXPERIMENT" | "SERVICE"; - /** - * @description Is the property editable in the execution view. Only used when `associationType` is set to `EXPERIMENT`. - * @example true - */ - editableInExecution?: boolean; - /** - * @description The key of the associated experiment. When `associationType` is set to `EXPERIMENT` and `experimentKey` is `null`, it is associated to ALL experiment designs. Can't be changed during updates. - * @example EXP-1 - */ - experimentKey?: string | null; - /** - * Format: uuid - * @description Id of an existing Property-Association. A new association will be created if no id is provided or no matching association could be found - */ - id?: string | null; - /** - * @description The key of the property definition - * @example RESULT_COLOR - */ - key: string; - /** - * @description Is the value required? - * @example true - */ - required?: boolean; - /** - * Format: uuid - * @description The serviceId of the associated service. When `associationType` is set to `SERVICE` and `serviceId` is `null`, it is associated to ALL services. Can't be changed during updates. - * @example 3308b47d-5c1f-4f08-a25b-a18fc10f8a56 - */ - serviceId?: string | null; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** - * @description A property association upsert. - * @example { - * "key": "RESULT_COLOR", - * "label": "Result Color", - * "description": "How would you describe the result of your experiment, thinking in beautiful colors?", - * "dataType": "ENUM", - * "enumValues": [ - * "RED", - * "GREEN", - * "BLUE" - * ] - * } - */ - UpsertPropertyDefinitionAO: { - /** - * @description The data type of the property - * @example STRING - * @enum {string} - */ - dataType: "STRING" | "STRING_LIST" | "ENUM" | "ENUM_LIST" | "NUMBER" | "NUMBER_LIST" | "MARKDOWN" | "BOOLEAN" | "DATE" | "LINK" | "LINK_LIST"; - /** - * @description The text describing the property. - * @example How would you describe the result of your experiment, thinking in beautiful colors? - */ - description?: string | null; - /** - * @description Valid values if the dataType `ENUM` is used - * @example [ - * "RED", - * "GREEN", - * "BLUE" - * ] - */ - enumValues?: string[]; - /** - * @description The unique key of the property definition - * @example RESULT_COLOR - */ - key: string; - /** - * @description The label shown in the ui for this property - * @example Result color - */ - label: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - UpsertProvidedExperimentRequestAO: { - /** - * @description Update only - the experiment that should be updated - * @example ADM-18 - */ - experimentKey?: string | null; - /** @description List of template placeholder values */ - placeholders?: components["schemas"]["ExperimentTemplatePlaceholderValueAO"][] | null; - /** - * Format: uuid - * @description The templateId that should be used for the provided experiment (needs to be included in the used service profile) - * @example e1c22d74-a48b-4661-ab56-4e90c584c4e0 - */ - templateId: string; - }; - /** - * @example { - * "name": "shopping-service", - * "environment": "Global", - * "team": "ADM", - * "query": "aws.account=\"123\" OR aws.account=\"456\"", - * "validations": [ - * { - * "type": "action", - * "parameters": { - * "url": "https://my-service/health", - * "method": "GET" - * }, - * "actionType": "com.steadybit.extension_http.check.periodically" - * } - * ], - * "serviceProfile": "Steadybit Starter", - * "variables": { - * "httpEndpoint": "http://prod.shop.products.internal", - * "targets": { - * "type": "select", - * "targetType": "com.steadybit.extension_kubernetes.kubernetes-deployment", - * "attribute": "k8s.deployment", - * "filter": "k8s.namespace=\"shop\"", - * "mode": "fixed", - * "count": 1 - * } - * } - * } - */ - UpsertServiceAO: { - /** - * @description The name of the environment to be used - * @example Global - */ - environment: string; - /** - * Format: uuid - * @description The unique id of the service, will be created if not provided - */ - id?: string | null; - /** - * @description Color scheme of the logo used to identify the service in the Platform UI - * @default blue - * @example orangeLight - */ - logoColor: string; - /** - * @description Identifier of the logo used to identify the service in the Platform UI - * @default service - * @example service-router - */ - logoId: string; - /** - * @description The name of the service - * @example calculator-service - */ - name: string; - /** - * @description The properties of the service - * @example { - * "EXAMPLE_CUSTOM_PROPERTY": "I like this service!" - * } - */ - properties?: { - [key: string]: unknown; - }; - /** - * @description Query-Language predicate, specifies the targets belonging to this Service - * @example aws.account="123" OR aws.account="456" - */ - query: string; - /** - * @description Name of the service profile that should be used for this service - * @example Steadybit provided - */ - serviceProfile: string; - /** - * @description The key of the team to be used - * @example ADM - */ - team: string; - /** @description List of validations to be executed against the service */ - validations: components["schemas"]["ExperimentStepActionAO"][]; - /** - * @description Variables owned by the service. Each value is either a constant string, an array of constant strings, or a select expression object. A select-expression value **must set `scope`** (`service` or `environment`) — the request is rejected otherwise. On `POST /api/services` (upsert): omitting this field leaves existing variables untouched, an empty object removes all of them. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal", - * "targetServices": [ - * "gateway", - * "hot-deals", - * "fashion-bestseller" - * ] - * } - */ - variables?: { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** - * @description Request to create or update a service profile - * @example { - * "name": "My Custom Templates", - * "origin": "CUSTOM", - * "templates": { - * "availability": [ - * "550e8400-e29b-41d4-a716-446655440000" - * ], - * "latency": [ - * "550e8400-e29b-41d4-a716-446655440002" - * ] - * } - * } - */ - UpsertServiceProfileAO: { - /** - * Format: uuid - * @description The unique id of the profile. Will be created if not provided. - */ - id?: string | null; - /** - * @description The name of the profile - * @example Default Resilience Tests - */ - name: string; - /** - * @description Origin of a service profile - * @example CUSTOM - * @enum {string} - */ - origin: "PROVIDED" | "CUSTOM"; - /** @description Template entries in this profile */ - templates: components["schemas"]["ServiceProfileCategoryAO"][]; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** - * @description Insert or update the team in Steadybit. The `key` will be used to identify whether the team exists already and should be updated or newly inserted. - * @example { - * "id": "71ab0180-8abc-4d30-8acb-6aa024e3065f", - * "key": "ADM", - * "name": "Administrators", - * "version": 1, - * "logoId": "1", - * "logoColor": "cyanDark", - * "allowedActions": [ - * "com.steadybit.extension_host.host.stress-cpu" - * ], - * "allowedEnvironments": [ - * "Global" - * ], - * "members": [ - * { - * "email": "jane.doe@example.com", - * "role": "OWNER" - * }, - * { - * "email": "javier.rodriguez@example.com", - * "role": "MEMBER" - * }, - * { - * "username": "auth0|1va2g15afc84590069cd53c3", - * "role": "MEMBER" - * } - * ] - * } - */ - UpsertTeamAO: { - /** - * @description Set of allowed actions that can be used in an experiment of this team - * @example [ - * "com.steadybit.extension_host.host.stress-cpu" - * ] - */ - allowedActions: string[]; - /** - * @description Set of allowed environments, identified via name - * @example [ - * "Global" - * ] - */ - allowedEnvironments: string[]; - /** @description An optional description of a team */ - description?: string; - /** Format: uuid */ - id?: string; - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - /** - * @description Color scheme of the logo used to identify the team in the Platform UI - * @example cyanDark - */ - logoColor?: string; - /** - * @description Identifier of the logo used to identify the team in the Platform UI - * @example 1 - */ - logoId?: string; - /** - * @description How a team or team membership is managed - * @example MANUAL - * @enum {string} - */ - managedBy?: "MANUAL" | "OIDC" | "LDAP"; - /** - * @description Members that should be added to this team - * @example { - * "email": "jane.doe@example.com", - * "role": "OWNER" - * } - */ - members?: components["schemas"]["MemberUpdateAO"][]; - /** - * @description Name of a team - * @example ADMIN - */ - name: string; - /** - * Format: int32 - * @description Version for optimistic locking (optional in the API) - * @example 1 - */ - version?: number | null; - }; - /** - * @description A user has performed the logged event e.g. via UI - * @example { - * "name": "Jane Doe", - * "role": "ADMIN", - * "email": "example@example.com", - * "username": "1ava2afg-xju33-4c6a-9451-2854584c15be", - * "principalType": "USER" - * } - */ - UserPrincipalAL: { - /** - * @description E-mail of the user, unique within Steadybit - * @example example@example.com - */ - email: string; - /** - * @description Name of the user - * @example Jane Doe - */ - name: string; - /** - * @description Principal type for user based principal - * @example USER - * @enum {string} - */ - principalType: "USER" | "ACCESS_TOKEN" | "BATCH_JOB"; - /** - * @description Role of the user in the platform - * @example ADMIN - * @enum {string|null} - */ - role?: "ADMIN" | "USER" | "SUPPORT" | null; - /** - * @description Username of the user, internal identifier of Steadybit - * @example 13av2737-b318-4048-a79d-4789d645bc31 - */ - username: string; - }; - /** - * @description The user that canceled the experiment execution, only present if the execution was canceled - * @example { - * "username": "ag1hb7ap-d299-47ab-998f-c2a53b433820", - * "name": "Max Mustermann", - * "email": "max@steadybit.com" - * } - */ - UserSummaryAO: { - email?: string | null; - /** - * @description Name of the user - * @example Jane Doe - */ - name?: string; - pictureUrl?: string | null; - /** - * @description Username of the user, internal identifier of Steadybit - * @example 13av2737-b318-4048-a79d-4789d645bc31 - */ - username: string; - }; - /** @description A variable value: a constant string (≤5000 chars), an array of constant strings, or a select expression object. */ - VariableExpressionAO: string | string[] | components["schemas"]["SelectExpressionAO"]; - VariableValueAO: { - type: "VariableValueAO"; - } & (Omit & { - value: string; - }); - /** - * @description Webhook payload containing more event-specific information. - * @example { - * "event": "killswitch.disengaged", - * "time": "2023-01-01T09:15:00.000000Z", - * "killswitch": { - * "engagedBy": "admin", - * "engaged": "2023-01-01T09:00:00.000000Z", - * "disengagedBy": "admin", - * "disengaged": "2023-01-01T09:15:00.000000Z" - * } - * } - */ - WebhookPayloadAO: { - /** - * @description The event that caused the webhook to be triggered - * @example killswitch.disengaged - */ - event?: string; - execution?: components["schemas"]["WebhookPayloadExecutionAO"]; - /** - * Format: uuid - * @description In case the webhook was triggered by an experiment execution step event, this contains the actual step's id having triggered the webhook - * @example 32009f6e-ff90-47a5-8daa-80f4bb7ae591 - */ - executionStepId?: string; - killswitch?: components["schemas"]["WebhookPayloadKillswitchAO"]; - /** - * Format: date-time - * @description The timestamp at which the event was fired - * @example 2023-01-01T09:15:00Z - */ - time?: string; - }; - /** - * @description The execution of a single experiment. - * @example { - * "id": 1, - * "experimentKey": "ADM-1", - * "teamKey": "ADM", - * "environment": "32009f6e-ff90-47a5-8daa-80f4bb7ae591", - * "name": "My first experiment", - * "created": "2023-01-01T08:00:00.000000Z", - * "createdVia": "UI", - * "experimentVersion": "5", - * "state": "CREATED", - * "steps": [ - * { - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * }, - * { - * "predecessorId": "40b0f797-912d-4256-8887-1553561962a9", - * "ignoreFailure": false, - * "parameters": { - * "cpuLoad": 100, - * "workers": 0, - * "duration": "30s" - * }, - * "actionId": "com.steadybit.extension_container.stress_cpu", - * "actionKind": "ATTACK", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "percentage": 50, - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "container.host/name", - * "operator": "EQUALS", - * "values": [ - * "docker-desktop/minikube" - * ] - * } - * ] - * } - * }, - * "targetExecutions": [ - * { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - * ], - * "totalTargetCount": 1 - * } - * ] - * } - */ - WebhookPayloadExecutionAO: { - canceledBy?: components["schemas"]["UserSummaryAO"]; - /** - * Format: date-time - * @description Timestamp when the experiment execution was created - * @example 2023-01-01T09:00:01Z - */ - created?: string; - createdBy?: components["schemas"]["UserSummaryAO"]; - /** - * @description The creation trigger that caused this experiment execution to be started - * @example UI - * @enum {string} - */ - createdVia?: "API" | "CLI" | "UI" | "SCHEDULE" | "MCP" | "SUITE"; - /** - * Format: date-time - * @description Timestamp when the experiment ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * @description The name of the environment to be used - * @example Global - */ - environment?: string; - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - experimentKey?: string; - /** - * Format: int32 - * @description Experiment design version which can be used to identify changes between experiment runs - * @example 5 - */ - experimentVersion?: number; - /** - * @description The hypothesis that is validated by the experiment - * @example System is able to survive a latency in the network of 1500ms - */ - hypothesis?: string; - /** - * Format: int32 - * @description Unique experiment execution id that identifies this specific experiment execution - * @example 1523 - */ - id?: number; - /** - * @description Name of the experiment to easily identify the experiment - * @example Shop survives unavailability of hot-deals products - */ - name?: string; - overrides?: components["schemas"]["WebhookPayloadExecutionOverridesAO"]; - /** - * @description Reason in case the experiment execution failed or errored - * @example Action error - */ - reason?: string; - /** - * @description Additional detail for the reason in case the experiment execution failed or errored - * @example Couldn't read state of container... - */ - reasonDetails?: string; - /** - * Format: date-time - * @description Timestamp when the experiment execution was requested - * @example 2023-01-01T09:00:00Z - */ - requested?: string; - /** - * Format: date-time - * @description Timestamp when the experiment execution started running, after preparation/preflight - * @example 2023-01-01T09:00:05Z - */ - started?: string; - /** - * @description Current state of the experiment (e.g. CREATED, RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** - * @description The steps that are executed in parallel or sequence in the experiment. - * @example [ - * { - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * }, - * { - * "predecessorId": "40b0f797-912d-4256-8887-1553561962a9", - * "ignoreFailure": false, - * "parameters": { - * "cpuLoad": 100, - * "workers": 0, - * "duration": "30s" - * }, - * "actionId": "com.steadybit.extension_container.stress_cpu", - * "actionKind": "ATTACK", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "percentage": 50, - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "container.host/name", - * "operator": "EQUALS", - * "values": [ - * "docker-desktop/minikube" - * ] - * } - * ] - * } - * }, - * "targetExecutions": [ - * { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - * ], - * "totalTargetCount": 1 - * } - * ] - */ - steps?: components["schemas"]["AbstractWebhookPayloadExecutionStepAO"][]; - /** - * @description Tags of the experiment at the time the execution was requested - * @example [ - * "resilience", - * "shop" - * ] - */ - tags?: string[]; - /** - * @description The key of the team to be used - * @example ADM - */ - teamKey?: string; - /** - * @description The variables resolved for this specific execution, keyed by name. Each entry carries the resolved value(s) and the tier the winning value originated from (ENVIRONMENT, SERVICE, EXPERIMENT, SCHEDULE, EXECUTION). A single-value variable's value is a string, a multi-value variable's value is an array of strings. Empty until the execution starts, as dynamic values are resolved once at run start and then stay stable for the whole run. - * @example { - * "httpEndpoint": { - * "value": "http://shop.products.internal", - * "origin": "EXECUTION" - * } - * } - */ - variables?: { - [key: string]: components["schemas"]["ExperimentExecutionVariableAO"]; - }; - }; - /** - * @deprecated - * @description Overrides that are used for a single experiment execution and not saved into the experiment design - * @example { - * "environment": "Shop-DEV", - * "variables": { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - * } - */ - WebhookPayloadExecutionOverridesAO: { - /** - * @description The environment in which the experiment should be executed instead of the environment specified in the experiment design - * @example Shop-DEV - */ - environment?: string; - /** - * @description A set of environment variables that should only be used for that specific experiment execution. Constant values are passed literally; dynamic (select expression) values are rendered as a human-readable summary, e.g. `select 3 of host.name on com.steadybit.extension_host.host`. - * @example { - * "httpEndpoint": "http://dev.shop.products.internal" - * } - */ - variables?: { - [key: string]: string; - }; - }; - /** - * @description An action-step that is executed as part of an experiment. - * @example { - * "predecessorId": "40b0f797-912d-4256-8887-1553561962a9", - * "ignoreFailure": false, - * "parameters": { - * "cpuLoad": 100, - * "workers": 0, - * "duration": "30s" - * }, - * "actionId": "com.steadybit.extension_container.stress_cpu", - * "actionKind": "ATTACK", - * "radius": { - * "targetType": "com.steadybit.extension_container.container", - * "percentage": 50, - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "container.host/name", - * "operator": "EQUALS", - * "values": [ - * "docker-desktop/minikube" - * ] - * } - * ] - * } - * }, - * "targetExecutions": [ - * { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - * ], - * "totalTargetCount": 1 - * } - */ - WebhookPayloadExecutionStepActionAO: { - /** - * @description Unique identifier of the action that is executed in this step - * @example com.steadybit.extension_container.stress_cpu - */ - actionId?: string; - /** - * @description Kind of the action (e.g. attack, check, loadtest) - * @example ATTACK - * @enum {string} - */ - actionKind?: "ATTACK" | "CHECK" | "LOAD_TEST" | "OTHER" | "BASIC"; - /** - * @description Custom label assigned during experiment design to express the intention of this step - * @example Container 'xyz' can not be reached - */ - customLabel?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: uuid - * @description Unique identifier of this step execution - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - id?: string; - /** - * @description Whether the experiment should fail/error immediately in case this step fails/errors. - * @example false - */ - ignoreFailure?: boolean; - /** - * @description Step-specific parameters of the experiment step configuration - * @example { - * "duration": "10s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - /** - * Format: uuid - * @description Unique identifier of the step execution that precedes this step, null if it is the first step of a lane - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - predecessorId?: string; - radius?: components["schemas"]["WebhookPayloadExecutionStepActionBlastRadiusAO"]; - /** - * @description Reason in case this experiment step execution failed or errored - * @example Couldn't read state of container... - */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step was started - * @example 2023-01-01T09:00:00Z - */ - started?: string; - /** - * @description Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** - * @description List of targets that are expected to be effected by this action. This list may change in case targets aren't available at the specific time of execution - * @example [ - * { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - * ] - */ - targetExecutions?: components["schemas"]["WebhookPayloadExecutionStepActionTargetAO"][]; - /** - * Format: int64 - * @description Amount of targets that are effect int total - * @example 23 - */ - totalTargetCount?: number; - }; - /** - * @description Blast radius that is applied to define the set of targets as well as an optional random subset - * @example { - * "targetType": "com.steadybit.extension_container.container", - * "percentage": 50, - * "predicate": { - * "operator": "AND", - * "predicates": [ - * { - * "key": "container.host/name", - * "operator": "EQUALS", - * "values": [ - * "docker-desktop/minikube" - * ] - * } - * ] - * } - * } - */ - WebhookPayloadExecutionStepActionBlastRadiusAO: { - /** - * Format: int32 - * @description In case a fixed number of as subset of specified targets should be effected - * @example 2 - */ - maximum?: number; - /** - * Format: int32 - * @description In case a percentage subset of the specified targets should be effected - * @example 40 - */ - percentage?: number; - predicate?: components["schemas"]["TargetPredicateAO"]; - /** - * @description Target type that is effected by that action - * @example container - */ - targetType?: string; - }; - /** - * @description A target that is expected to be effected by this action. - * @example { - * "type": "com.steadybit.extension_container.container", - * "name": "docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea", - * "state": "COMPLETED", - * "attributes": [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * }, - * { - * "key": "container.host/name", - * "value": "docker-desktop/minikube" - * }, - * { - * "key": "container.host", - * "value": "docker-desktop" - * } - * ] - * } - */ - WebhookPayloadExecutionStepActionTargetAO: { - /** - * @description A set of attributes that have been discovered for this target. A key may be associated multiple time to a single target. - * @example [ - * { - * "key": "container.port", - * "value": "51152:2376" - * }, - * { - * "key": "container.engine", - * "value": "docker" - * } - * ] - */ - attributes?: components["schemas"]["Attribute"][]; - /** - * @description Identifier of the target that is expected to be effected - * @example docker://1f769d01b9c5cd29bb302ca40157274f38798104208117b3310825ba676883ea - */ - name?: string; - /** - * @description Type of the target that is expected to be effected - * @example container - */ - type?: string; - }; - /** - * @description A service validation step that is executed as part of an experiment. - * @example { - * "stepType": "SERVICE-VALIDATION", - * "id": "40b0f797-912d-4256-8887-1553561962a9", - * "state": "COMPLETED", - * "started": "2025-06-18T08:32:01.850479Z", - * "ended": "2025-06-18T08:32:11.886043Z", - * "predecessorId": null, - * "ignoreFailure": false, - * "parameters": { - * "duration": "60s" - * }, - * "serviceId": "cc06f132-0694-4ffa-aee2-13d8fafa3a8b", - * "validations": [] - * } - */ - WebhookPayloadExecutionStepServiceValidationAO: { - /** - * @description Custom label assigned during experiment design to express the intention of this step - * @example Container 'xyz' can not be reached - */ - customLabel?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: uuid - * @description Unique identifier of this step execution - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - id?: string; - /** - * @description Whether the experiment should fail/error immediately in case this step fails/errors. - * @example false - */ - ignoreFailure?: boolean; - /** - * @description Step-specific parameters of the experiment step configuration - * @example { - * "duration": "10s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - /** - * Format: uuid - * @description Unique identifier of the step execution that precedes this step, null if it is the first step of a lane - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - predecessorId?: string; - /** - * @description Reason in case this experiment step execution failed or errored - * @example Couldn't read state of container... - */ - reason?: string; - /** - * Format: uuid - * @description Unique identifier of the service. - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - serviceId?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step was started - * @example 2023-01-01T09:00:00Z - */ - started?: string; - /** - * @description Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - /** @description List of actions performed as part of this service validation step. */ - validations?: components["schemas"]["WebhookPayloadExecutionStepActionAO"][]; - }; - /** - * @description A wait step that is executed as part of an experiment. - * @example { - * "id": "40b0f797-912d-4256-8887-1553561962a9", - * "predecessorId": null, - * "ignoreFailure": false, - * "parameters": { - * "duration": "10s" - * } - * } - */ - WebhookPayloadExecutionStepWaitAO: { - /** - * @description Custom label assigned during experiment design to express the intention of this step - * @example Container 'xyz' can not be reached - */ - customLabel?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step ended - * @example 2023-01-01T09:00:00Z - */ - ended?: string; - /** - * Format: uuid - * @description Unique identifier of this step execution - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - id?: string; - /** - * @description Whether the experiment should fail/error immediately in case this step fails/errors. - * @example false - */ - ignoreFailure?: boolean; - /** - * @description Step-specific parameters of the experiment step configuration - * @example { - * "duration": "10s" - * } - */ - parameters?: { - [key: string]: unknown; - }; - /** - * Format: uuid - * @description Unique identifier of the step execution that precedes this step, null if it is the first step of a lane - * @example 40b0f797-912d-4256-8887-1553561962a9 - */ - predecessorId?: string; - /** - * @description Reason in case this experiment step execution failed or errored - * @example Couldn't read state of container... - */ - reason?: string; - /** - * Format: date-time - * @description Timestamp when this experiment step was started - * @example 2023-01-01T09:00:00Z - */ - started?: string; - /** - * @description Current state of this step in the experiment (e.g. RUNNING, FAILED, ERRORED, COMPLETED) - * @example RUNNING - */ - state?: string; - }; - /** - * @description Webhook payload performed for a killswitch related event. - * @example { - * "engagedBy": "13av2737-b318-4048-a79d-4789d645bc31", - * "engaged": "2023-01-01T09:00:00.000000Z", - * "disengagedBy": "13av2737-b318-4048-a79d-4789d645bc31", - * "disengaged": "2023-01-01T09:15:00.000000Z" - * } - */ - WebhookPayloadKillswitchAO: { - /** - * Format: date-time - * @description Timestamp when the kill switch was disengaged - * @example 2023-01-01T09:15:00Z - */ - disengaged?: string; - /** - * @description Username of the user who has disengaged the kill switch - * @example 13av2737-b318-4048-a79d-4789d645bc31 - */ - disengagedBy?: string; - disengagedByDetails?: components["schemas"]["UserSummaryAO"]; - /** - * Format: date-time - * @description Timestamp when the kill switch was engaged - * @example 2023-01-01T09:00:00Z - */ - engaged?: string; - /** - * @description Username of the user who has engaged the kill switch - * @example 13av2737-b318-4048-a79d-4789d645bc31 - */ - engagedBy?: string; - engagedByDetails?: components["schemas"]["UserSummaryAO"]; - }; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} -export type $defs = Record; -export interface operations { - getAccessTokens: { - parameters: { - query: { - pageRequest: components["schemas"]["PageRequestAO"]; - team?: string; - type?: "ADMIN" | "TEAM"; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOAccessTokensPageItemAO"]; - "application/yaml": components["schemas"]["PagedResponseAOAccessTokensPageItemAO"]; - }; - }; - }; - }; - createAccessToken: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateAccessTokenRequestAO"]; - }; - }; - responses: { - /** @description Token created, Response body contains the newly generated token.
Make sure to save the generated token as you can't read it again afterwards for security-reasons. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["CreateAccessTokenResponseAO"]; - "application/yaml": components["schemas"]["CreateAccessTokenResponseAO"]; - }; - }; - /** @description Validation error, the access token was not generated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteAccessToken: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Token deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Token not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, the access token was not deleted */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getAccessTokens_1: { - parameters: { - query: { - createdBy?: string; - expired?: boolean; - name?: string; - pageRequest: components["schemas"]["PageRequestAO"]; - teams?: string[]; - type?: "ADMIN" | "TEAM" | "WILDCARD"; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOAccessTokensPageItemV2AO"]; - "application/yaml": components["schemas"]["PagedResponseAOAccessTokensPageItemV2AO"]; - }; - }; - }; - }; - createAccessToken_1: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateAccessTokenRequestV2AO"]; - }; - }; - responses: { - /** @description Token created. Make sure to save the generated token as you can't read it again afterwards for security-reasons. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["CreateAccessTokenResponseV2AO"]; - "application/yaml": components["schemas"]["CreateAccessTokenResponseV2AO"]; - }; - }; - /** @description Validation error, the access token was not generated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteAccessToken_1: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Token deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Token not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - recreateAccessToken: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["RecreateAccessTokenRequestV2AO"]; - }; - }; - responses: { - /** @description Token recreated. Make sure to save the generated token as you can't read it again afterwards for security-reasons. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["CreateAccessTokenResponseV2AO"]; - "application/yaml": components["schemas"]["CreateAccessTokenResponseV2AO"]; - }; - }; - /** @description Token not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - findAllActions: { - parameters: { - query?: { - /** @description The page number to retrieve. Starts from 0. */ - page?: number; - /** @description The number of items to return per page. */ - size?: number; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Actions including their parameters, limited to the given page size. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ActionSummariesAO"]; - "application/yaml": components["schemas"]["ActionSummariesAO"]; - }; - }; - }; - }; - getAction: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Action ID - * @example com.steadybit.extension_container.network_block_dns - */ - actionId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Action found, response body contains the action details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ActionAO"]; - "application/yaml": components["schemas"]["ActionAO"]; - }; - }; - /** @description Action with given `actionId` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getTargetAdviceSummary: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["GetAdviceApiRequestAO"]; - }; - }; - responses: { - /** @description Matching targets with advice information, limited to 20 items. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["AdviceSummaryAO"]; - "application/yaml": components["schemas"]["AdviceSummaryAO"]; - }; - }; - }; - }; - find: { - parameters: { - query?: { - /** - * @description Starting point with the earliest time to be included.
If neither `to` nor `from` is specified, it defaults to a 7 days date range from today. - * @example 2023-01-01T09:00:00Z - */ - from?: string | null; - /** - * @description End point with the latest time to be included.
If neither `to` nor `from` is specified, it defaults to a 7 days date range from today. - * @example 2023-01-31T09:00:00Z - */ - to?: string | null; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of all audit logs */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["AuditLogEntry"][]; - }; - }; - }; - }; - forwardToPlatform: { - parameters: { - query: { - /** - * @deprecated - * @description External reference that identifies the experiment. This is used to identify whether an experiment was already created for that reference or not. Can be e.g. an incident or ticket identifier of pager duty or JIRA - * @example INCIDENT-312 - */ - externalReference?: string; - /** - * @description Tag that identifies the experiment. This is used to identify whether an experiment was already created for these tag or not. Can be e.g. an incident or ticket identifier of pager duty or JIRA - * @example INCIDENT,INCIDENT-312 - */ - tag?: string; - /** - * @description Key of the Steadybit tenant. You can get the key from the Platform URL or by asking the Steadybit team - * @example demo - */ - tenantKey: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Forwarding to create experiment or (a list of) experiments */ - 307: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": Record; - "application/yaml": Record; - }; - }; - }; - }; - getLinkedBadge: { - parameters: { - query: { - /** - * @description Caption that is shown at the badge when no experiment exists in order to create a new experiment - * @example Create experiment - */ - createCaption?: string; - /** - * @deprecated - * @description External reference that identifies the experiment. This is used to identify whether an experiment was already created for that reference or not. Can be e.g. an incident or ticket identifier of pager duty or JIRA - * @example INCIDENT-312 - */ - externalReference?: string; - /** - * @description Optional parameter in case you need to scale the svg image. Defaults to 1 - * @example 2 - */ - scale?: number; - /** - * @description A tag that identifies the experiment. This is used to identify whether an experiment was already created having this tag or not. Can be e.g. an incident or ticket identifier of pager duty or JIRA - * @example INCIDENT,INCIDENT-312 - */ - tag?: string; - /** - * @description Key of the Steadybit tenant (only for SaaS customers). You can get the key from the Platform URL or by asking the Steadybit team - * @example demo - */ - tenantKey: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Returns a badge as a svg-image */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "image/svg+xml": string; - }; - }; - }; - }; - getEnvironments: { - parameters: { - query?: { - /** - * @description If set, only environments matching the search are returned. Matches the environment name or the name or key of a team the environment is assigned to. - * @example shop - */ - search?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all existing environments. Fetch a single environment by `id` to get more details for a team */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["EnvironmentSummariesAO"]; - "application/yaml": components["schemas"]["EnvironmentSummariesAO"]; - }; - }; - }; - }; - upsertEnvironment: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertEnvironmentAO"]; - }; - }; - responses: { - /** @description Environment updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["EnvironmentAO"]; - "application/yaml": components["schemas"]["EnvironmentAO"]; - }; - }; - /** @description Environment created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["EnvironmentAO"]; - "application/yaml": components["schemas"]["EnvironmentAO"]; - }; - }; - /** @description Validation error, environment was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getEnvironment: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a environment - * @example 2v1av42-e525-4c00-a13a-1ac32d170724 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Environment found, response body contains the environment details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["EnvironmentAO"]; - "application/yaml": components["schemas"]["EnvironmentAO"]; - }; - }; - /** @description The given `id` is not a uuid */ - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Environment with given `id` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteEnvironment: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a environment - * @example 2v1av42-e525-4c00-a13a-1ac32d170724 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Environment deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Environment with given `id` was not found and thus not deleted */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getEnvironmentVariables: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a environment - * @example 2v1av42-e525-4c00-a13a-1ac32d170724 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Key-value-Map of environment variables associated to the environment. Each value is either a constant string, an array of constant strings, or a select expression object. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": string; - "application/yaml": string; - }; - }; - /** @description Environment not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - updateEnvironmentVariables: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a environment - * @example 2v1av42-e525-4c00-a13a-1ac32d170724 - */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - }; - }; - responses: { - /** @description Variables updated. New variables added and existing ones updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Environment with given `id` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - setEnvironmentVariables: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a environment - * @example 2v1av42-e525-4c00-a13a-1ac32d170724 - */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - }; - }; - responses: { - /** @description Variables updated. New variables added, existing ones updated, all other removed. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Environment with given `id` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getExperiments: { - parameters: { - query?: { - /** - * @description Filter results by experiments using the specified action. If multiple actions are specified, all of them needs to be used in the experiment - * @example com.steadybit.extension_host.stress-cpu - */ - action?: string[]; - /** - * @description Filter results by one or more external-ids - * @example incident-100 - */ - externalId?: string[]; - /** - * @description Filter results via free text phrases searching for experiment name, key, property values, and 10 last run ids - * @example `ADM-1` or `#212` - */ - freeTextPhrases?: string[]; - /** - * @description Filter results by one or more experiments-keys - * @example ADM-1 - */ - key?: string[]; - /** - * @description Filter results by experiments using an action with the specified kind. If multiple kinds are specified, all of them needs to be used in the experiment - * @example ATTACK - */ - kind?: ("ATTACK" | "CHECK" | "LOAD_TEST" | "OTHER" | "BASIC")[]; - /** - * @description Filter results by name and/or key of the experiment - * @example Outage - */ - name?: string; - /** - * @description Filter results via properties - * @example `Value` or `EnumApiName:EnumValue` - */ - properties?: string[]; - /** - * @description Include only experiments which are runnable by the authorized user? - * @example false - */ - runnable?: boolean; - /** - * @description Filter results by experiments linked to the given Service. If multiple services are specified, the experiment needs to be linked to all of them - * @example Shopping Cart Service - */ - service?: string[]; - /** - * @description Filter results by experiments having the specified tag. If multiple tags are specified, all of them needs to be assigned to the experiment - * @example kubernetes - */ - tag?: string[]; - /** - * @description Filter results by experiments using the specified target-type. If multiple target-types are specified, all of them needs to be used in the experiment - * @example com.steadybit.extension_host.host - */ - targetType?: string[]; - /** - * @description Filter results by one or more team-keys owning an experiment - * @example ADM - */ - team?: string[]; - /** - * @description Filter results by one or more team keys the experiment is shared with - * @example ADM - */ - teamSharedWith?: string[]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains a summary of all existing experiments. Fetch a single experiment using the `experimentKey` to get more details of the experiment. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentSummariesAO"]; - "application/yaml": components["schemas"]["ExperimentSummariesAO"]; - }; - }; - }; - }; - createOrUpdateExperiment: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateExperimentAO"]; - }; - }; - responses: { - /** @description Experiment updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment created */ - 201: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, experiment was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getExperiment: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Experiment found, response body contains all the experiment details */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentAO"]; - "application/yaml": components["schemas"]["ExperimentAO"]; - }; - }; - /** @description Experiment with given `key` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - updateExperiment: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - key: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpdateExperimentAO"]; - }; - }; - responses: { - /** @description Experiment updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, experiment was not updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteExperiment: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Experiment deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment could not be found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getExperimentBadge: { - parameters: { - query: { - /** - * @description Override the default hex-color `9f9f9f` for executions in state `canceled`. - * @example 9f9f9f - */ - colorMappingCanceled?: string; - /** - * @description Override the default hex-color `4c1` for executions in state `completed`. - * @example 4c1 - */ - colorMappingCompleted?: string; - /** - * @description Override the default hex-color `fe7d37` for executions in state `created`. - * @example fe7d37 - */ - colorMappingCreated?: string; - /** - * @description Override the default hex-color `e05d44` for executions in state `errored`. - * @example e05d44 - */ - colorMappingErrored?: string; - /** - * @description Override the default hex-color `e05d44` for executions in state `failed`. - * @example e05d44 - */ - colorMappingFailed?: string; - /** - * @description Override the default hex-color `fe7d37` for executions in state `prepared`. - * @example fe7d37 - */ - colorMappingPrepared?: string; - /** - * @description Override the default hex-color `fe7d37` for executions in state `requested`. - * @example fe7d37 - */ - colorMappingRequested?: string; - /** - * @description Override the default hex-color `fe7d37` for executions in state `running`. - * @example fe7d37 - */ - colorMappingRunning?: string; - /** - * @description Optional parameter in case you need to scale the svg image. Defaults to 1 - * @example 2 - */ - scale?: number; - /** - * @description Key of the Steadybit tenant (only for SaaS customers). You can get the key from the Platform URL or by asking the Steadybit team - * @example demo - */ - tenantKey: string; - }; - header?: never; - path: { - /** - * @description Unique experiment key that identifies the experiment. Combination of `team key` and increasing number - * @example ADM-2 - */ - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Returns a badge as a svg-image */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "image/svg+xml": string; - }; - }; - }; - }; - executeExperiment: { - parameters: { - query?: { - /** - * @description By default an experiment is only executed when no other experiment is running. This can be overriden by starting the new experiment execution although another one is currently running - * @example true - */ - allowParallel?: boolean; - /** - * @description Optional parameter to always store runs on any failure. If false, won´t be stored on validation errors (default behaviour). - * @example false - */ - forcePersist?: boolean; - }; - header?: never; - path: { - /** - * @description Unique experiment key that identifies the experiment to be started. Combination of `team key` and increasing number - * @example ADM-2 - */ - key: string; - }; - cookie?: never; - }; - requestBody?: { - content: { - "application/json": components["schemas"]["ExecuteExperimentRequestAO"]; - }; - }; - responses: { - /** @description Experiment execution was started. The newly created object can be accessed via the HTTP header `location` */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExecuteExperimentResponseAO"]; - "application/yaml": components["schemas"]["ExecuteExperimentResponseAO"]; - }; - }; - /** @description Experiment execution couldn't be started. */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExecuteExperimentResponseAO"]; - "application/yaml": components["schemas"]["ExecuteExperimentResponseAO"]; - }; - }; - }; - }; - getExperimentExecutions_3: { - parameters: { - query?: { - /** @description Filter results by one or more states */ - state?: ("REQUESTED" | "CREATED" | "PREPARED" | "RUNNING" | "FAILED" | "CANCELED" | "COMPLETED" | "ERRORED")[]; - }; - header?: never; - path: { - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains a summary of all experiment executions of the single experiment. Fetch a single experiment execution using the `id` of an execution and the `/experiments/executions/{id}`-API to get more details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentExecutionSummariesAO"]; - "application/yaml": components["schemas"]["ExperimentExecutionSummariesAO"]; - }; - }; - }; - }; - saveAndRun: { - parameters: { - query?: { - /** - * @description Should this experiment also be executed when there is already at least one experiment running? - * @example false - */ - allowParallel?: boolean; - /** - * @description Optional parameter to always store runs on any failure. If false, won´t be stored on validation errors (default behaviour). - * @example false - */ - forcePersist?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateAndRunExperimentAO"]; - }; - }; - responses: { - /** @description Experiment started */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExecuteExperimentResponseAO"]; - "application/yaml": components["schemas"]["ExecuteExperimentResponseAO"]; - }; - }; - /** @description Validation error, experiment was neither saved nor executed */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExecuteExperimentResponseAO"]; - "application/yaml": components["schemas"]["ExecuteExperimentResponseAO"]; - }; - }; - }; - }; - getExperimentExecutions_1: { - parameters: { - query?: { - /** - * @description Filter results by name and/or key of the experiment - * @example Outage - */ - name?: string; - /** - * @description Filter results by one or more states - * @example RUNNING - */ - state?: ("REQUESTED" | "CREATED" | "PREPARED" | "RUNNING" | "FAILED" | "CANCELED" | "COMPLETED" | "ERRORED")[]; - /** - * @description Filter results by one or more team-keys - * @example ADM - */ - team?: string[]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains a summary of all experiment executions. Fetch a single experiment execution using the `id` of an execution and the `/experiments/executions/{id}`-API to get more details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentExecutionSummariesAO"]; - "application/yaml": components["schemas"]["ExperimentExecutionSummariesAO"]; - }; - }; - }; - }; - getExperimentExecutions_2: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ExperimentExecutionsRequestAO"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOExperimentExecutionPageItemAO"]; - "application/yaml": components["schemas"]["PagedResponseAOExperimentExecutionPageItemAO"]; - }; - }; - }; - }; - getExperimentExecution: { - parameters: { - query?: { - /** - * @description Additional fields to be returned for the experiment execution - * @example steps - */ - fields?: string; - }; - header?: never; - path: { - /** - * @description Unique experiment execution id that identifies a single experiment execution - * @example 123 - */ - id: number; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all information of the experiment execution of the single experiment. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentExecutionAO"]; - "application/yaml": components["schemas"]["ExperimentExecutionAO"]; - }; - }; - }; - }; - getArtifact: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description The id of the artifact, usually the filename - * @example logfile.txt - */ - artifactId: string; - /** - * @description Unique experiment execution id that identifies a single experiment execution - * @example 123 - */ - id: number; - /** - * @description The id of the target execution where the artifact is attached to - * @example 019abec0-3b14-7235-81fd-8007e720dfd0 - */ - targetExecutionId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - cancelExperimentExecution: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique experiment execution id that identifies a single experiment execution - * @example 123 - */ - id: number; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Experiment execution was already canceled, errored or completed. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Request to cancel the experiment was accepted and will be performed by communicating to the experiment's agents. */ - 202: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment execution not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - updateExecutionProperties: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique experiment execution id that identifies a single experiment execution - * @example 123 - */ - id: number; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpdateExperimentExecutionPropertiesAO"]; - }; - }; - responses: { - /** @description Properties updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment execution not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, properties were not updated. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - addExecutionPropertyValue: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique experiment execution id that identifies a single experiment execution - * @example 123 - */ - id: number; - /** - * @description The key of the property - * @example approvedBy - */ - key: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": Record; - }; - }; - responses: { - /** @description Properties updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment execution not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, properties were not updated. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - setExecutionPropertyValue: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique experiment execution id that identifies a single experiment execution - * @example 123 - */ - id: number; - /** - * @description The key of the property - * @example approvedBy - */ - key: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": Record; - }; - }; - responses: { - /** @description Properties updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment execution not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, properties were not updated. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - upsertSchedule: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertExperimentScheduleAO"]; - }; - }; - responses: { - /** @description Schedule updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentScheduleAO"]; - "application/yaml": components["schemas"]["ExperimentScheduleAO"]; - }; - }; - /** @description Schedule created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentScheduleAO"]; - "application/yaml": components["schemas"]["ExperimentScheduleAO"]; - }; - }; - /** @description Validation error, schedule was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getSchedules: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Experiment schedule found. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentScheduleAO"]; - "application/yaml": components["schemas"]["ExperimentScheduleAO"]; - }; - }; - /** @description Experiment Schedule couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - removeExperimentScheduleById: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Experiment schedule id. - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Experiment schedule deleted. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment Schedule couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - patchSchedule: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Experiment schedule id. - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["PatchExperimentScheduleAO"]; - }; - }; - responses: { - /** @description Schedule updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentScheduleAO"]; - "application/yaml": components["schemas"]["ExperimentScheduleAO"]; - }; - }; - /** @description Experiment Schedule couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, schedule was not updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getAllSchedulesV2: { - parameters: { - query?: { - /** - * @description Filter results by one or more experiment-keys - * @example ADM-5 - */ - experiment?: string[]; - /** - * @description Filter results by one or more team-keys - * @example ADM - */ - team?: string[]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description All experiment schedule configurations */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentScheduleAO"][]; - "application/yaml": components["schemas"]["ExperimentScheduleAO"][]; - }; - }; - }; - }; - getExperimentTemplates: { - parameters: { - query?: { - /** @description Filter results by one or more action, like `com.steadybit.extension_host.stress-cpu` */ - action?: string[]; - /** @description Filter results by one or more free text phrases searching in the template title and template description */ - freeTextPhrases?: string[]; - /** - * @description Include hidden templates (requires an admin token) - * @example false - */ - includeHidden?: boolean; - /** - * @description Include templates referencing actions/target-types/property-definitions that are not available - * @example false - */ - includeNonAvailable?: boolean; - /** @description Filter results by one or more tags */ - tag?: string[]; - /** @description Filter results by one or more target type, like `com.steadybit.extension_container.container` */ - targetType?: string[]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all existing templates. Fetch a single template by `id` to get more details for a template */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentTemplateSummariesAO"]; - "application/yaml": components["schemas"]["ExperimentTemplateSummariesAO"]; - }; - }; - }; - }; - upsertExperimentTemplate: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertExperimentTemplateAO"]; - }; - }; - responses: { - /** @description Experiment Template updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentTemplateAO"]; - "application/yaml": components["schemas"]["ExperimentTemplateAO"]; - }; - }; - /** @description Experiment Template created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentTemplateAO"]; - "application/yaml": components["schemas"]["ExperimentTemplateAO"]; - }; - }; - /** @description Validation error, experiment template was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getExperimentTemplate: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of an experiment template - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Experiment Template found, response body contains the team details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExperimentTemplateAO"]; - "application/yaml": components["schemas"]["ExperimentTemplateAO"]; - }; - }; - /** @description Experiment Template with given `id` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteExperimentTemplate: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of an experiment template - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Experiment Template deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment Template with given `id` was not found and thus not deleted */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - createExperimentByTemplate: { - parameters: { - query?: { - /** - * @description If `true`, all properties will be reset to properties specified in the template either with their fixed values in the template or via template placeholder. If `false`, existing properties will stay untouched, only new properties will be added. Only relevant for experiment updates via `externalId`. - * @example true - */ - resetProperties?: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of an experiment template - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateExperimentFromTemplateAO"]; - }; - }; - responses: { - /** @description Experiment updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment created */ - 201: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Template specified by experimentTemplateId could not be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, experiment was not created */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - saveAndRunFromTemplate: { - parameters: { - query?: { - /** - * @description Should this experiment also be executed when there is already another experiment running? - * @example false - */ - allowParallel?: boolean; - /** - * @description Optional parameter to always store runs on any failure. If false, won´t be stored on validation errors (default behaviour). - * @example false - */ - forcePersist?: boolean; - /** - * @description If `true`, all properties will be reset to properties specified in the template either with their fixed values in the template or via template placeholder. If `false`, existing properties will stay untouched, only new properties will be added. Only relevant for experiment updates via `externalId`. - * @example true - */ - resetProperties?: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of an experiment template - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CreateAndRunExperimentFromTemplateAO"]; - }; - }; - responses: { - /** @description Experiment started */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExecuteExperimentResponseAO"]; - "application/yaml": components["schemas"]["ExecuteExperimentResponseAO"]; - }; - }; - /** @description Validation error, experiment was neither saved nor executed */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ExecuteExperimentResponseAO"]; - "application/yaml": components["schemas"]["ExecuteExperimentResponseAO"]; - }; - }; - }; - }; - updateExperimentByTemplate: { - parameters: { - query?: { - /** - * @description If `true`, all properties will be reset to properties specified in the template either with their fixed values in the template or via template placeholder. If `false`, existing properties will stay untouched, only new properties will be added. - * @example true - */ - resetProperties?: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of an experiment template - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - /** - * @description The key of the experiment that should be updated - * @example ADM-18 - */ - key: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpdateExperimentFromTemplateAO"]; - }; - }; - responses: { - /** @description Experiment updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Template specified by experimentTemplateId or experiment specified by key could not be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, experiment was not created */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - importFromHub: { - parameters: { - query?: { - /** - * @description Do you want to overwrite a template that already exists? If set to `false` and any of the templates already exist, the API will return HTTP status 409 and none of the templates are imported. - * @example true - */ - overwrite?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ExperimentTemplatesImportAO"]; - }; - }; - responses: { - /** @description Experiment Templates imported */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description If any of the templates to be imported already exist in the platform and overwrite is set to `false` */ - 409: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getLandscapeViews: { - parameters: { - query: { - /** - * @description Key of the team whose saved views should be returned. - * @example ADM - */ - team: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all saved landscape views of the team. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListResponseLandscapeViewAO"]; - "application/yaml": components["schemas"]["ListResponseLandscapeViewAO"]; - }; - }; - /** @description The given team could not be found. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - createLandscapeView: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertLandscapeViewAO"]; - }; - }; - responses: { - /** @description Saved view created, response body contains its details. */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["LandscapeViewAO"]; - "application/yaml": components["schemas"]["LandscapeViewAO"]; - }; - }; - /** @description Validation error, the saved view was not created. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getLandscapeView: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a saved view. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Saved view found, response body contains its details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["LandscapeViewAO"]; - "application/yaml": components["schemas"]["LandscapeViewAO"]; - }; - }; - /** @description The given `id` is not a uuid. */ - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Saved view with given `id` was not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - updateLandscapeView: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a saved view. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertLandscapeViewAO"]; - }; - }; - responses: { - /** @description Saved view updated, response body contains its details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["LandscapeViewAO"]; - "application/yaml": components["schemas"]["LandscapeViewAO"]; - }; - }; - /** @description Saved view with given `id` was not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, the saved view was not updated. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteLandscapeView: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a saved view. - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Saved view deleted. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Saved view with given `id` was not found and thus not deleted. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - health: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": Record; - "application/yaml": Record; - }; - }; - }; - }; - liveness: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": Record; - "application/yaml": Record; - }; - }; - }; - }; - readiness: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": Record; - "application/yaml": Record; - }; - }; - }; - }; - getHubs: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all hubs. Fetch a single hub by `id` to get more details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HubSummariesAO"]; - "application/yaml": components["schemas"]["HubSummariesAO"]; - }; - }; - }; - }; - upsertHub: { - parameters: { - query?: { - /** @description Whether to synchronize the hub or not. */ - synchronize?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertHubAO"]; - }; - }; - responses: { - /** @description Hub updated. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HubAO"]; - "application/yaml": components["schemas"]["HubAO"]; - }; - }; - /** @description Hub created. */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HubAO"]; - "application/yaml": components["schemas"]["HubAO"]; - }; - }; - /** @description Validation error, hub was not created / updated. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getHubById: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a hub - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Hub found, response body contains the hub. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HubAO"]; - "application/yaml": components["schemas"]["HubAO"]; - }; - }; - /** @description Hub with the given `id` was not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteHub: { - parameters: { - query?: { - /** @description Whether imported templates of the hub should be deleted as well. */ - deleteImportedTemplates?: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of a hub. - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Hub deleted. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Hub with given `id` was not found and thus not deleted. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - resyncHub: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a hub - * @example d7e65100-1d20-4980-be87-c351704910b8 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Hub re-synchronized. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HubAO"]; - "application/yaml": components["schemas"]["HubAO"]; - }; - }; - /** @description Hub with given `id` was not found and thus not re-synchronized. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Rate limit exceeded. */ - 429: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - connectionCheck: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["HubConnectionCheckAO"]; - }; - }; - responses: { - /** @description Hub successful resolved. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HubConnectionCheckResponseAO"]; - "application/yaml": components["schemas"]["HubConnectionCheckResponseAO"]; - }; - }; - /** @description Hub could not be resolved. */ - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getPreflightWebhooks: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all existing preflight webhooks. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListResponsePreflightWebhookAO"]; - "application/yaml": components["schemas"]["ListResponsePreflightWebhookAO"]; - }; - }; - }; - }; - upsertPreflightWebhook: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["PreflightWebhookUpsertAO"]; - }; - }; - responses: { - /** @description Preflight webhook updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightWebhookAO"]; - "application/yaml": components["schemas"]["PreflightWebhookAO"]; - }; - }; - /** @description Preflight webhook created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightWebhookAO"]; - "application/yaml": components["schemas"]["PreflightWebhookAO"]; - }; - }; - /** @description Validation error, preflight webhook was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getPreflightActionIntegrations: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all existing preflight preflight action integrations. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListResponsePreflightActionIntegrationAO"]; - "application/yaml": components["schemas"]["ListResponsePreflightActionIntegrationAO"]; - }; - }; - }; - }; - upsertPreflightActionIntegration: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["PreflightActionIntegrationUpsertAO"]; - }; - }; - responses: { - /** @description Preflight action integration updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightActionIntegrationAO"]; - "application/yaml": components["schemas"]["PreflightActionIntegrationAO"]; - }; - }; - /** @description Preflight action integration created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightActionIntegrationAO"]; - "application/yaml": components["schemas"]["PreflightActionIntegrationAO"]; - }; - }; - /** @description Validation error, preflight action integration was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getPreflightActionIntegration: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description ID of the preflight action integration - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Preflight preflight action integrations found, response body contains the details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightActionIntegrationAO"]; - "application/yaml": components["schemas"]["PreflightActionIntegrationAO"]; - }; - }; - /** @description Preflight preflight action integrations with given `id` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deletePreflightActionIntegration: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a preflight action integration - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Preflight action integration deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightActionIntegrationAO"]; - "application/yaml": components["schemas"]["PreflightActionIntegrationAO"]; - }; - }; - /** @description Preflight action integration with given `id` was not found and thus not deleted */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getPreflightWebhook: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description ID of the preflight webhook - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Preflight webhooks found, response body contains the details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightWebhookAO"]; - "application/yaml": components["schemas"]["PreflightWebhookAO"]; - }; - }; - /** @description Preflight webhooks with given `id` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deletePreflightWebhook: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a preflight webhook - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Preflight webhook deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightWebhookAO"]; - "application/yaml": components["schemas"]["PreflightWebhookAO"]; - }; - }; - /** @description Preflight webhook with given `id` was not found and thus not deleted */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getSlackIntegrations: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all existing integrations. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListResponseSlackWebhookAO"]; - "application/yaml": components["schemas"]["ListResponseSlackWebhookAO"]; - }; - }; - }; - }; - upsertSlackIntegration: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["SlackWebhookUpsertAO"]; - }; - }; - responses: { - /** @description Slack integration updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SlackWebhookAO"]; - "application/yaml": components["schemas"]["SlackWebhookAO"]; - }; - }; - /** @description Slack integration created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SlackWebhookAO"]; - "application/yaml": components["schemas"]["SlackWebhookAO"]; - }; - }; - /** @description Validation error, slack integration was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getSlackIntegration: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description ID of the integration - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Integration found, response body contains the details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SlackWebhookAO"]; - "application/yaml": components["schemas"]["SlackWebhookAO"]; - }; - }; - /** @description Integration with given `id` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteSlackIntegration: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a slack integration - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Slack integration deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SlackWebhookAO"]; - "application/yaml": components["schemas"]["SlackWebhookAO"]; - }; - }; - /** @description Slack integration with given `id` was not found and thus not deleted */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getCustomWebhooks: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all existing custom webhooks. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ListResponseCustomWebhookAO"]; - "application/yaml": components["schemas"]["ListResponseCustomWebhookAO"]; - }; - }; - }; - }; - upsertCustomWebhook: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["CustomWebhookUpsertAO"]; - }; - }; - responses: { - /** @description Custom webhook updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["CustomWebhookAO"]; - "application/yaml": components["schemas"]["CustomWebhookAO"]; - }; - }; - /** @description Custom webhook created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["CustomWebhookAO"]; - "application/yaml": components["schemas"]["CustomWebhookAO"]; - }; - }; - /** @description Validation error, custom webhook was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getCustomWebhook: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description ID of the custom webhook - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Custom webhooks found, response body contains the details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["CustomWebhookAO"]; - "application/yaml": components["schemas"]["CustomWebhookAO"]; - }; - }; - /** @description Custom webhooks with given `id` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteCustomWebhook: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a custom webhook - * @example ac456d58-8fb2-4df4-86d8-ca81d7562739 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Custom webhook deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["CustomWebhookAO"]; - "application/yaml": components["schemas"]["CustomWebhookAO"]; - }; - }; - /** @description Custom webhook with given `id` was not found and thus not deleted */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getKillswitch: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Kill switch status in the response body */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["KillswitchAO"]; - "application/yaml": components["schemas"]["KillswitchAO"]; - }; - }; - }; - }; - engageKillswitch: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Kill switch was activated / engaged */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - disengageKillswitch: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Kill switch was deactivated / disengaged */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getLicenseSummary: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description License summary. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["GetLicenseSummaryAO"]; - "application/yaml": components["schemas"]["GetLicenseSummaryAO"]; - }; - }; - }; - }; - getReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description License report zip. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getPreflightActionSummary: { - parameters: { - query: { - offset: number; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Matching targets with preflightAction information, limited to 20 items. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PreflightActionSummaryAO"]; - "application/yaml": components["schemas"]["PreflightActionSummaryAO"]; - }; - }; - }; - }; - getAssociations: { - parameters: { - query?: { - /** - * @description Filter association based on association type (`EXPERIMENT` for experiment-related associations and `SERVICE` for service-related associations, no matter whether globally or individually) - * @example EXPERIMENT - */ - associationTypeAO?: "EXPERIMENT" | "SERVICE"; - /** - * @description Filter association that are explicitly assigned to the given experimentKey. (There might still be associations for ALL Experiment Designs) - * @example ADM-15 - */ - experimentKey?: string; - /** - * @description Filter association based on a single property definition key - * @example RESULT_COLOR - */ - key?: string; - /** - * @description The number of the page, responses are limited to 50 elements per page. - * @example 0 - */ - page?: number; - /** - * @description Filter association that are explicitly assigned to the given serviceId. (There might still be associations for ALL Services) - * @example c1975ae7-02f6-4a4a-9762-14559b330b8c - */ - serviceId?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of associations. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOPropertyAssociationAO"]; - "application/yaml": components["schemas"]["PagedResponseAOPropertyAssociationAO"]; - }; - }; - }; - }; - upsertPropertyAssociation: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertPropertyAssociationAO"]; - }; - }; - responses: { - /** @description Property association updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PropertyAssociationAO"]; - "application/yaml": components["schemas"]["PropertyAssociationAO"]; - }; - }; - /** @description Property association created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PropertyAssociationAO"]; - "application/yaml": components["schemas"]["PropertyAssociationAO"]; - }; - }; - /** @description Version not matching */ - 409: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, property association was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getPropertyDefinition_1: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Property association found. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PropertyAssociationAO"]; - "application/yaml": components["schemas"]["PropertyAssociationAO"]; - }; - }; - /** @description Property association couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deletePropertyAssociation: { - parameters: { - query?: { - /** - * @description Associations can only be deleted, if no experiment design or experiment schedule is still using the value. Setting this parameter to `true` will delete those values. - * @example false - */ - deleteValues?: boolean; - }; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Property association deleted. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Property association couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getPropertyDefinitions: { - parameters: { - query: { - /** - * @description The number of the page, responses are limited to 50 elements per page. - * @example 0 - */ - page: components["schemas"]["PageRequestAO"]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOPropertyDefinitionAO"]; - "application/yaml": components["schemas"]["PagedResponseAOPropertyDefinitionAO"]; - }; - }; - }; - }; - upsertPropertyDefinition: { - parameters: { - query?: { - /** - * @description You can remove enum-values for a ENUM or ENUM_LIST property if they are still in use in experiment designs. Setting this parameter to `true` will delete those values in experiment designs. - * @example false - */ - deleteValues?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertPropertyDefinitionAO"]; - }; - }; - responses: { - /** @description Property definition updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PropertyDefinitionAO"]; - "application/yaml": components["schemas"]["PropertyDefinitionAO"]; - }; - }; - /** @description Property definition created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PropertyDefinitionAO"]; - "application/yaml": components["schemas"]["PropertyDefinitionAO"]; - }; - }; - /** @description Version not matching */ - 409: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, property definition was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getPropertyDefinition: { - parameters: { - query?: never; - header?: never; - path: { - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Property definition found. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PropertyDefinitionAO"]; - "application/yaml": components["schemas"]["PropertyDefinitionAO"]; - }; - }; - /** @description Property definition couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deletePropertyDefinition: { - parameters: { - query?: { - /** - * @description Definitions can only be deleted, if no associations are still refering to this property. Setting the value to `true` will delete all associations and all current values in experiment designs and schedules. Existing executions won't get touched. - * @example false - */ - deleteAssociations?: boolean; - }; - header?: never; - path: { - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Property definition deleted. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Property definition couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getEnvironmentCounts: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ReportFilterAO"]; - }; - }; - responses: { - /** @description Environment count time series. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeSeriesReportAO"]; - "application/yaml": components["schemas"]["TimeSeriesReportAO"]; - }; - }; - }; - }; - getExperimentCreations: { - parameters: { - query?: { - /** - * @description Grouping dimension for the results. - * @example CREATED_VIA - */ - groupBy?: "NONE" | "CREATED_VIA" | "ORIGIN"; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ExperimentReportFilterAO"]; - }; - }; - responses: { - /** @description Creation count time series. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeSeriesReportAO"]; - "application/yaml": components["schemas"]["TimeSeriesReportAO"]; - }; - }; - }; - }; - getExperimentExecutions: { - parameters: { - query?: { - /** - * @description Grouping dimension for the results. - * @example STATE - */ - groupBy?: "NONE" | "STATE" | "TRIGGER" | "ACTION" | "ISSUES_FIXED" | "ISSUES_DISCOVERED"; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ExperimentExecutionReportFilterAO"]; - }; - }; - responses: { - /** @description Execution count time series. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeSeriesReportAO"]; - "application/yaml": components["schemas"]["TimeSeriesReportAO"]; - }; - }; - }; - }; - getAverageRisk: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ServiceRiskReportFilterAO"]; - }; - }; - responses: { - /** @description Average risk time series. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeSeriesReportAO"]; - "application/yaml": components["schemas"]["TimeSeriesReportAO"]; - }; - }; - }; - }; - getRiskByCategory: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ServiceRiskReportFilterAO"]; - }; - }; - responses: { - /** @description Risk by category time series. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeSeriesReportAO"]; - "application/yaml": components["schemas"]["TimeSeriesReportAO"]; - }; - }; - }; - }; - getRiskDistribution: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ServiceRiskReportFilterAO"]; - }; - }; - responses: { - /** @description Risk level distribution time series. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeSeriesReportAO"]; - "application/yaml": components["schemas"]["TimeSeriesReportAO"]; - }; - }; - }; - }; - getTeamCounts: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ReportFilterAO"]; - }; - }; - responses: { - /** @description Team count time series. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeSeriesReportAO"]; - "application/yaml": components["schemas"]["TimeSeriesReportAO"]; - }; - }; - }; - }; - getUserCounts: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ReportFilterAO"]; - }; - }; - responses: { - /** @description User count time series. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TimeSeriesReportAO"]; - "application/yaml": components["schemas"]["TimeSeriesReportAO"]; - }; - }; - }; - }; - getServiceList: { - parameters: { - query: { - /** @description Filter results by one or more environment name, like 'Global' */ - environmentName?: string[]; - /** @description Filter results by one or more experiment keys being linked to a service, like 'ADM-123' */ - experimentKey?: string[]; - page: components["schemas"]["PageRequestAO"]; - /** @description Filter results by one or more team key, like 'ADM' */ - teamKey?: string[]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body containing all existing services matching the query parameters. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOServiceSummaryAO"]; - "application/yaml": components["schemas"]["PagedResponseAOServiceSummaryAO"]; - }; - }; - }; - }; - upsertService: { - parameters: { - query?: { - /** - * @description When the service profile of a service gets updated, provided experiments whose template ids are not part of the new service profile are not allowed. Setting the value to `true` will delete those experiments. - * @example false - */ - deleteExperiments?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertServiceAO"]; - }; - }; - responses: { - /** @description Service updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServiceAO"]; - "application/yaml": components["schemas"]["ServiceAO"]; - }; - }; - /** @description Service created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServiceAO"]; - "application/yaml": components["schemas"]["ServiceAO"]; - }; - }; - /** @description Version not matching */ - 409: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, Service was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getService: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Service found. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServiceAO"]; - "application/yaml": components["schemas"]["ServiceAO"]; - }; - }; - /** @description Service couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteService: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Service deleted. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Service couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getServiceExperiments: { - parameters: { - query: { - /** @description Filter results by one or more categories */ - category?: string[]; - /** @description Include custom experiments with missing categories */ - categoryMissing?: boolean; - page: components["schemas"]["PageRequestAO"]; - /** @description Filter results by type (PROVIDED,CUSTOM) */ - type?: ("PROVIDED" | "CUSTOM")[]; - }; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Service-Associations found. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOServiceExperimentAO"]; - "application/yaml": components["schemas"]["PagedResponseAOServiceExperimentAO"]; - }; - }; - }; - }; - linkCustomExperiment: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["LinkCustomExperimentRequestAO"]; - }; - }; - responses: { - /** @description Experiment linked to service. */ - 201: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - unlinkCustomExperiment: { - parameters: { - query: { - experimentKey: string; - }; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Experiment unlinked from service. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - upsertProvidedExperiment: { - parameters: { - query?: { - /** - * @description If `true`, all properties will be reset to properties specified in the template either with their fixed values in the template or via template placeholder. If `false`, existing properties will stay untouched, only new properties will be added. Only relevant for experiment updates. - * @example true - */ - resetProperties?: boolean; - }; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertProvidedExperimentRequestAO"]; - }; - }; - responses: { - /** @description Experiment updated. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Experiment created. */ - 201: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Service or Template not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getRisk: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Risk score found. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServiceRiskAO"]; - "application/yaml": components["schemas"]["ServiceRiskAO"]; - }; - }; - /** @description Service or risk not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getServiceVariables: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Unique identifier of a service */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Key-value map of variables owned by the service. Each value is either a constant string, an array of constant strings, or a select expression object. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": string; - "application/yaml": string; - }; - }; - /** @description Service not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - setServiceVariables: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Unique identifier of a service */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - }; - }; - responses: { - /** @description Variables updated. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Service not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - mergeServiceVariables: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Unique identifier of a service */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - [key: string]: components["schemas"]["VariableExpressionAO"]; - }; - }; - }; - responses: { - /** @description Variables updated. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Service not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getProfiles: { - parameters: { - query: { - /** @description Filter results by defaultProfile flag */ - defaultProfile?: boolean; - /** @description Filter results by name (partial match) */ - name?: string; - /** @description Filter results by origin (PROVIDED, CUSTOM) */ - origin?: string[]; - page: components["schemas"]["PageRequestAO"]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body containing all existing service profiles matching the query parameters. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOServiceProfileAO"]; - "application/yaml": components["schemas"]["PagedResponseAOServiceProfileAO"]; - }; - }; - }; - }; - upsertProfile: { - parameters: { - query?: { - /** - * @description When templates are removed from a service profile, provided experiments using those templates will be affected. Setting the value to `true` will delete those experiments. - * @example false - */ - deleteExperiments?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertServiceProfileAO"]; - }; - }; - responses: { - /** @description Service profile updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServiceProfileAO"]; - "application/yaml": components["schemas"]["ServiceProfileAO"]; - }; - }; - /** @description Service profile created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServiceProfileAO"]; - "application/yaml": components["schemas"]["ServiceProfileAO"]; - }; - }; - /** @description Version not matching */ - 409: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, Service profile was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getProfile: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Service profile found. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServiceProfileAO"]; - "application/yaml": components["schemas"]["ServiceProfileAO"]; - }; - }; - /** @description Service profile couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteProfile: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Service profile deleted. */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Service profile couldn't be found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Cannot delete PROVIDED profiles. */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getTargetsStats: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - [key: string]: number; - }; - "application/yaml": { - [key: string]: number; - }; - }; - }; - }; - }; - getTargetsStats_1: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TargetStatsRequest"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - [key: string]: number; - }; - "application/yaml": { - [key: string]: number; - }; - }; - }; - }; - }; - getTargets: { - parameters: { - query: { - /** @description Optional, list of requested target attribute keys. If not specified, all attributes will be returned. Multiple values allowed. Example: `k8s.deployment` */ - attribute?: string[]; - /** - * @description Optional, the cursor to use to fetch the next page - * @example eyJhZ2VudElkIjoiMDE5ZDE4ZjYtZTgwYy03NDdlLThkYWItNmE5MTBkM2JhZWQyIiwibmFtZSI6ImRlbW8tZGV2ZWxvcC9pbmdyZXNzLW5naW54L2luZ3Jlc3MtbmdpbngtY29udHJvbGxlciIsInR5cGUiOiJjb20uc3RlYWR5Yml0LmV4dGVuc2lvbl9rdWJlcm5ldGVzLmt1YmVybmV0ZXMtZGVwbG95bWVudCJ9 - */ - cursor?: string; - /** - * @description The name of the environment - * @example Global - */ - environment: string; - /** - * @description Optional, additional target selection query - * @example (k8s.cluster-name="demo-develop" AND k8s.namespace="steadybit-demo" AND k8s.deployment="fashion-bestseller") - */ - query?: string; - /** @description Optional, the number of items to return per page. default is 100, maximum is 1000. */ - size?: number; - /** - * @description Optional, the type of the target - * @example com.steadybit.extension_kubernetes.kubernetes-deployment - */ - targetType?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of targets. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["CursorSliceResponseAOTargetAO"]; - "application/yaml": components["schemas"]["CursorSliceResponseAOTargetAO"]; - }; - }; - /** @description Environment not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getTargetAttributeKeys: { - parameters: { - query: { - /** - * @description If the action specifies a extended target selector and you want to fetch all attribute keys for a given action. Required if targetType is not set - * @example com.steadybit.extension_kubernetes.delete-pod - */ - actionId?: string; - /** - * @description The name of the environment - * @example Global - */ - environment: string; - /** @description The page number to retrieve. Starts from 0. default is 0 */ - page?: number; - /** @description The number of items to return per page. default is 100, maximum is 100. */ - size?: number; - /** - * @description The type of the target, required if actionId is not set - * @example com.steadybit.extension_kubernetes.kubernetes-deployment - */ - targetType?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of attribute keys. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOString"]; - "application/yaml": components["schemas"]["PagedResponseAOString"]; - }; - }; - /** @description Environment not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getTargetAttributeValues: { - parameters: { - query: { - /** - * @description If the action specifies a extended target selector and you want to fetch all attribute keys for a given action. Required if targetType is not set - * @example com.steadybit.extension_kubernetes.delete-pod - */ - actionId?: string; - /** - * @description The key of of the attribute - * @example k8s.namespace - */ - attributeKey: string; - /** - * @description The name of the environment - * @example Global - */ - environment: string; - /** @description The page number to retrieve. Starts from 0. default is 0 */ - page?: number; - /** @description The number of items to return per page. default is 100, maximum is 100. */ - size?: number; - /** - * @description The type of the target, required if actionId is not set - * @example com.steadybit.extension_kubernetes.kubernetes-deployment - */ - targetType?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of attribute values. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PagedResponseAOString"]; - "application/yaml": components["schemas"]["PagedResponseAOString"]; - }; - }; - /** @description Environment not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getTeams: { - parameters: { - query?: { - /** - * @description If set and used with an `accessToken` associated to one or multiple teams, only the team associated to the token are returned. Otherwise, all teams are listed. - * @example true - */ - onlyAccessible?: boolean; - /** - * @description If set, only teams matching the search are returned. Matches the team name or key, the name or email of a team member, or the name of an allowed environment. - * @example shop - */ - search?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Response body contains all existing teams. Fetch a single team by `key` to get more details for a team */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamSummariesAO"]; - "application/yaml": components["schemas"]["TeamSummariesAO"]; - }; - }; - }; - }; - upsertTeam: { - parameters: { - query?: { - /** - * @description By default, Steadybit checks whether the allowed actions exists and are reported by an agent. For convenience, this can be deactivated to decouple team creation and agent-installation - * @example false - */ - validateActions?: boolean; - /** - * @description By default, Steadybit will skip members, which are not yet know. If set to true, Steadybit will validate the given members and show a 422 response. - * @example false - */ - validateMembers?: boolean; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UpsertTeamAO"]; - }; - }; - responses: { - /** @description Team updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamAO"]; - "application/yaml": components["schemas"]["TeamAO"]; - }; - }; - /** @description Team created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamAO"]; - "application/yaml": components["schemas"]["TeamAO"]; - }; - }; - /** @description Validation error, team was not created / updated */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getTeam: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Team found, response body contains the team details. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamAO"]; - "application/yaml": components["schemas"]["TeamAO"]; - }; - }; - /** @description Team with given `key` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - deleteTeam: { - parameters: { - query: { - /** - * @description Safety-Parameter - purge team including all experiments and executions. - * @example true - */ - purgeIncludingExperiments: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Team deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamAO"]; - "application/yaml": components["schemas"]["TeamAO"]; - }; - }; - /** @description Team has running experiments or insufficient permisssions */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamAO"]; - "application/yaml": components["schemas"]["TeamAO"]; - }; - }; - /** @description Team with given `key` was not found and thus not deleted */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getTeamEnvironments: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Team found, response body contains all assigned environments */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamEnvironmentsAO"]; - "application/yaml": components["schemas"]["TeamEnvironmentsAO"]; - }; - }; - /** @description Team with given `key` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - setTeamEnvironments: { - parameters: { - query?: { - /** - * @description By default, Steadybit will skip environments, which are not yet know. If set to true, Steadybit will validate the given environments and show a 422 response. - * @example false - */ - validateEnvironments?: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - /** @description Update request to change the environments of a specific team. */ - requestBody: { - content: { - "application/json": components["schemas"]["TeamEnvironmentsAO"]; - }; - }; - responses: { - /** @description Team environments updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamEnvironmentsAO"]; - "application/yaml": components["schemas"]["TeamEnvironmentsAO"]; - }; - }; - /** @description Validation error, no update was performed */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - addTeamEnvironments: { - parameters: { - query?: { - /** - * @description By default, Steadybit will skip environments, which are not yet know. If set to true, Steadybit will validate the given environments and show a 422 response. - * @example false - */ - validateEnvironments?: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TeamEnvironmentsUpdateAO"]; - }; - }; - responses: { - /** @description Team environments added */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamEnvironmentsAO"]; - "application/yaml": components["schemas"]["TeamEnvironmentsAO"]; - }; - }; - /** @description Validation error, no team environment were added */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - removeTeamEnvironments: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TeamEnvironmentsUpdateAO"]; - }; - }; - responses: { - /** @description Team environments removed */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamEnvironmentsAO"]; - "application/yaml": components["schemas"]["TeamEnvironmentsAO"]; - }; - }; - /** @description Validation error, no team environments were removed */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - getTeamMembers: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Team found, response body contains all the team members */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamMembersAO"]; - "application/yaml": components["schemas"]["TeamMembersAO"]; - }; - }; - /** @description Team with given `key` was not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - setTeamMembers: { - parameters: { - query?: { - /** - * @description By default, Steadybit will skip members, which are not yet know. If set to true, Steadybit will validate the given members and show a 422 response. - * @example false - */ - validateMembers?: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - /** @description Update request to change the members of a specific team. Specify either username, being a Steadybit user id, or the email address of the user. */ - requestBody: { - content: { - "application/json": components["schemas"]["TeamMembersUpdateAO"]; - }; - }; - responses: { - /** @description Team members updated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamMembersAO"]; - "application/yaml": components["schemas"]["TeamMembersAO"]; - }; - }; - /** @description Validation error, no update was performed */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - addTeamMembers: { - parameters: { - query?: { - /** - * @description By default, Steadybit will skip members, which are not yet know. If set to true, Steadybit will validate the given members and show a 422 response. - * @example false - */ - validateMembers?: boolean; - }; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TeamMembersUpdateAO"]; - }; - }; - responses: { - /** @description Team members added */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamMembersAO"]; - "application/yaml": components["schemas"]["TeamMembersAO"]; - }; - }; - /** @description Validation error, no team member were added */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - removeTeamMembers: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Unique identifier of a team - * @example ADM - */ - key: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TeamMembersRemoveAO"]; - }; - }; - responses: { - /** @description Team members removed */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TeamMembersAO"]; - "application/yaml": components["schemas"]["TeamMembersAO"]; - }; - }; - /** @description Validation error, no team members were removed */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - inviteUser: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["InviteUsersRequestAO"]; - }; - }; - responses: { - /** @description Users invited */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Insufficient permissions to invite a new user to this tenant. */ - 403: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Validation error, no or not all users were invited */ - 422: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; -} diff --git a/src/api/http.test.ts b/src/api/http.test.ts deleted file mode 100644 index 454c320..0000000 --- a/src/api/http.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { afterAll, beforeAll, describe, expect, it } from 'vitest'; -import { delay, http, HttpResponse } from 'msw'; -import { server } from '../mocks/server.ts'; -import { executeApiCall, options } from './http.ts'; - -describe('http', () => { - beforeAll(() => { - options.defaultWaitTime = 10; - options.rateLimitBudget = 5000; - }); - - afterAll(() => { - options.defaultWaitTime = 1000; - options.rateLimitBudget = 120000; - }); - - describe('too many requests', () => { - it('should not handle codes besides Too Many Requests', async () => { - await expect(() => - executeApiCall({ - method: 'GET', - path: `/api/status`, - queryParameters: { - code: '500', - body: 'Internal Server Error', - }, - }) - ).rejects.toThrow('responded with unexpected status code: 500 - Internal Server Error'); - }); - - it('should retry on too many requests response', async () => { - const response = await executeApiCall({ - method: 'GET', - path: `/api/status`, - queryParameters: { - code: '429', - times: '3', - }, - }); - expect(response.status).toEqual(200); - }); - - it('should keep retrying past the old four-attempt cap', async () => { - const response = await executeApiCall({ - method: 'GET', - path: `/api/status`, - queryParameters: { - code: '429', - times: '20', - }, - }); - expect(response.status).toEqual(200); - }); - - // The budget counts time spent waiting on 429s, not elapsed time. Measuring elapsed - // time meant the pacing before a request could spend the whole budget, leaving none - // for the rate limit the pacing exists to survive. - it('should not let a slow response consume the budget', async () => { - const budget = options.rateLimitBudget; - options.rateLimitBudget = 100; - let attempts = 0; - server.use( - http.get('http://example.com/api/slow-429', async () => { - attempts++; - // Each response takes far longer than the whole budget. - await delay(300); - return attempts <= 2 ? new HttpResponse(null, { status: 429 }) : HttpResponse.text('recovered'); - }) - ); - - try { - const response = await executeApiCall({ method: 'GET', path: '/api/slow-429' }); - expect(await response.text()).toEqual('recovered'); - expect(attempts).toEqual(3); - } finally { - options.rateLimitBudget = budget; - } - }); - - it('should surface the rate limit once the budget is spent', async () => { - const budget = options.rateLimitBudget; - options.rateLimitBudget = 25; - try { - await expect(() => - executeApiCall({ - method: 'GET', - path: `/api/status`, - queryParameters: { - code: '429', - times: '10000', - }, - }) - ).rejects.toThrow('responded with unexpected status code: 429'); - } finally { - options.rateLimitBudget = budget; - } - }); - - it('should wait for reset time', async () => { - const start = new Date(); - const response = await executeApiCall({ - method: 'GET', - path: `/api/status`, - queryParameters: { - code: '429', - times: '1', - reset: '2', - }, - }); - const duration = new Date().getTime() - start.getTime(); - expect(response.status).toEqual(200); - expect(duration).toBeGreaterThan(2000); - }); - }); - - // A dump makes hundreds of requests, so a single flaky DNS lookup or reset connection - // must not end the command. - describe('transport failures', () => { - it('should retry an idempotent request that fails in transit', async () => { - let attempts = 0; - server.use( - http.get('http://example.com/api/flaky', () => { - attempts++; - return attempts === 1 ? HttpResponse.error() : HttpResponse.text('recovered'); - }) - ); - - const response = await executeApiCall({ method: 'GET', path: '/api/flaky' }); - - expect(await response.text()).toEqual('recovered'); - expect(attempts).toEqual(2); - }); - - it('should not repeat a POST, which may already have started a run', async () => { - let attempts = 0; - server.use( - http.post('http://example.com/api/flaky', () => { - attempts++; - return HttpResponse.error(); - }) - ); - - await expect(() => executeApiCall({ method: 'POST', path: '/api/flaky' })).rejects.toThrow( - 'Failed to call Steadybit API at POST' - ); - expect(attempts).toEqual(1); - }); - - it('should give up on an idempotent request once the attempts are used', async () => { - let attempts = 0; - server.use( - http.get('http://example.com/api/always-broken', () => { - attempts++; - return HttpResponse.error(); - }) - ); - - await expect(() => executeApiCall({ method: 'GET', path: '/api/always-broken' })).rejects.toThrow( - 'Failed to call Steadybit API at GET' - ); - expect(attempts).toEqual(options.maxRetries + 2); - }); - }); - - // Absolute URLs arrive from platform responses, most notably the Location header of a - // started run, and every request carries the API access token. - describe('absolute urls', () => { - it('should send a foreign origin to the configured platform instead', async () => { - // The message carries the URL that was actually requested, so it shows both that - // the path survived and that nothing was sent to the other host. - await expect(() => - executeApiCall({ - method: 'GET', - path: 'https://attacker.example/api/status?code=500&body=served%20by%20the%20mock', - }) - ).rejects.toThrow( - 'Steadybit API at GET http://example.com/api/status?code=500&body=served%20by%20the%20mock responded with unexpected status code: 500 - served by the mock' - ); - }); - - it('should accept an absolute url on the configured origin', async () => { - const response = await executeApiCall({ - method: 'GET', - path: 'http://example.com/api/status', - }); - - expect(response.status).toEqual(200); - }); - - it('should keep query parameters when the path is absolute', async () => { - await expect(() => - executeApiCall({ - method: 'GET', - path: 'http://example.com/api/status', - queryParameters: { - code: '500', - body: 'Internal Server Error', - }, - }) - ).rejects.toThrow('responded with unexpected status code: 500 - Internal Server Error'); - }); - }); -}); diff --git a/src/api/http.ts b/src/api/http.ts deleted file mode 100644 index a208004..0000000 --- a/src/api/http.ts +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { setTimeout as sleep } from 'node:timers/promises'; -import { getHeaders, type QueryParameters, toUrl } from './common.ts'; -import { ApiError } from './error.ts'; -import { errorMessage } from '../errors.ts'; -import { rateLimiter } from './rateLimit.ts'; -import { ensurePlatformAccessConfigurationIsAvailable } from '../config/requirePlatformAccess.ts'; - -const TOO_MANY_REQUESTS = 429; - -export const options = { - maxRetries: 2, - defaultWaitTime: 1000, - rateLimitBudget: 120000, -}; - -export interface ApiCallArguments { - path: string; - method: string; - queryParameters?: QueryParameters; - body?: unknown; - timeout?: number; // defaults to 30000 -} - -export function enableRequestLogging() { - process.env.REQUEST_LOGGING_ENABLED = 'true'; -} - -async function doFetch( - url: string, - method: string, - headers: Record, - body: undefined | string, - signal: AbortSignal -) { - if (process.env.REQUEST_LOGGING_ENABLED === 'true') { - console.log(`> HTTP ${method} ${url}`); - for (const [key, value] of Object.entries(headers)) { - console.log(`> ${key}: ${maskSensitiveHeader(key, value)}`); - } - console.log(`> `); - if (body) { - console.log(body); - } - console.log(''); - } - - const response = await fetch(url, { - method, - headers, - body, - signal, - redirect: 'error', - }); - - if (process.env.REQUEST_LOGGING_ENABLED === 'true') { - console.log(`< HTTP ${response.status} ${response.statusText}`); - for (const [key, value] of response.headers) { - console.log(`< ${key}: ${maskSensitiveHeader(key, value)}`); - } - console.log(`< `); - try { - const text = await response.clone().text(); - if (text) { - console.log(text); - } - } catch { - // ignore - } - console.log(''); - } - return response; -} - -export async function executeApiCall({ - method, - path, - queryParameters, - body, - timeout = 30000, -}: ApiCallArguments): Promise { - await ensurePlatformAccessConfigurationIsAvailable(); - const url = await toUrl(path, queryParameters); - const headers = await getHeaders(); - - const response = await doWithRetry(method, async () => { - await rateLimiter.acquire(); - // The deadline stays attached to the response, so it bounds reading the body as - // well and not just the wait for the status line. Clearing it once the headers - // arrive would leave a stalled body download running forever. - const signal = AbortSignal.timeout(timeout); - try { - // Compared with undefined rather than tested for truth: 0, false, null and "" are - // all bodies a caller can mean, such as a run property being set to zero. - return await doFetch(url, method, headers, body !== undefined ? JSON.stringify(body) : undefined, signal); - } catch (e) { - throw new Error(`Failed to call Steadybit API at ${method} ${url}: ${describeFetchError(e)}`, { - cause: e, - }); - } - }); - - if (!response.ok) { - let body = ''; - try { - body = await response.text(); - } catch { - // ignore - } - throw new ApiError( - `Steadybit API at ${method} ${url} responded with unexpected status code: ${response.status} - ${body || ''}`, - response, - body - ); - } - - return response; -} - -// Repeating a request that may already have been applied is only safe for methods -// defined to be idempotent: a POST that failed in transit might still have started an -// experiment run, so it is reported rather than retried. -const IDEMPOTENT_METHODS = new Set(['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE']); - -async function doWithRetry(method: string, fn: () => Promise): Promise { - // A rate limit says the request was rejected, not applied, so waiting it out is safe - // whatever the method. What it needs is time, not a number of tries: an attempt count - // multiplied by the reset interval gave up after roughly eighteen seconds, which a - // fan-out like `experiment dump` exceeds routinely because every rejected request - // retries into the same window. A transport failure is still capped by attempts, since - // repeating it is the part that carries risk. - // - // The budget counts time actually spent waiting on 429s, not elapsed time. A deadline - // taken at the start would have been spent by the pacing in rateLimiter.acquire(), - // which under a large dump can hold a request back for minutes — leaving no budget for - // the rate limit the pacing exists to survive. - const maxTransportAttempts = options.maxRetries + 2; - const mayRepeat = IDEMPOTENT_METHODS.has(method.toUpperCase()); - let rateLimitWait = 0; - let transportAttempt = 1; - - for (;;) { - let response: Response; - try { - response = await fn(); - } catch (e) { - // Transport failures used to end the command outright, which meant one flaky DNS - // lookup out of the hundreds a dump makes discarded the whole run. - if (!mayRepeat || transportAttempt >= maxTransportAttempts) { - throw e; - } - await sleep(withJitter(options.defaultWaitTime * transportAttempt)); - transportAttempt++; - continue; - } - - if (response.status !== TOO_MANY_REQUESTS) { - return response; - } - const resetHeader = response.headers.get('RateLimit-Reset') || response.headers.get('Retry-After'); - const retryInMillis = (resetHeader && Number.parseInt(resetHeader) * 1000) || options.defaultWaitTime; - if (rateLimitWait + retryInMillis > options.rateLimitBudget) { - return response; - } - await sleep(retryInMillis); - rateLimitWait += retryInMillis; - } -} - -// Without this, requests that failed together — a dump shares one DNS resolver and one -// connection pool — would come back together and fail together again. -function withJitter(millis: number): number { - return millis / 2 + Math.random() * (millis / 2); -} - -const SENSITIVE_HEADERS = new Set(['authorization', 'cookie', 'set-cookie', 'proxy-authorization']); - -function maskSensitiveHeader(name: string, value: string): string { - return SENSITIVE_HEADERS.has(name.toLowerCase()) ? '' : value; -} - -// The global fetch reports every transport failure as "fetch failed" and carries the -// actual reason (DNS, TLS, ECONNREFUSED) on the cause chain. -function describeFetchError(e: unknown): string { - const message = errorMessage(e); - const causeMessage = (e as { cause?: Error })?.cause?.message; - return causeMessage && causeMessage !== message ? `${message}: ${causeMessage}` : message; -} diff --git a/src/api/paging.test.ts b/src/api/paging.test.ts deleted file mode 100644 index dd85508..0000000 --- a/src/api/paging.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it } from 'vitest'; -import { respondTo } from '../mocks/recorder.ts'; -import { fetchAllPages } from './paging.ts'; - -describe('paging', () => { - it('follows nextPage until the last page', async () => { - const requests = respondTo('get', '/api/things', ({ url }) => { - const page = Number(url.searchParams.get('page')); - return { json: { items: [`item-${page}`], nextPage: page < 2 ? page + 1 : null } }; - }); - - const items = await fetchAllPages('/api/things', { team: ['A', 'B'] }); - - expect(items).toEqual(['item-0', 'item-1', 'item-2']); - expect(requests.map(r => r.url.searchParams.get('page'))).toEqual(['0', '1', '2']); - expect(requests.every(r => r.url.searchParams.get('size') === '100')).toBe(true); - expect(requests[0].url.searchParams.getAll('team')).toEqual(['A', 'B']); - }); - - it('stops when the platform omits nextPage', async () => { - const requests = respondTo('get', '/api/things', () => ({ json: { items: ['only'] } })); - - await expect(fetchAllPages('/api/things')).resolves.toEqual(['only']); - expect(requests).toHaveLength(1); - }); -}); diff --git a/src/api/paging.ts b/src/api/paging.ts deleted file mode 100644 index 217b3a0..0000000 --- a/src/api/paging.ts +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { QueryParameters } from './common.ts'; -import { executeApiCall } from './http.ts'; - -interface Page { - items?: T[]; - nextPage?: number | null; -} - -// Paged endpoints return at most 100 items and point to the next page, so a listing -// that stopped at the first response would silently leave out the rest. -export async function fetchAllPages(path: string, queryParameters: QueryParameters = {}): Promise { - const items: T[] = []; - let page: number | null | undefined = 0; - while (page !== undefined && page !== null) { - const response = await executeApiCall({ - method: 'GET', - path, - queryParameters: { ...queryParameters, page: String(page), size: '100' }, - }); - const body = (await response.json()) as Page; - items.push(...(body.items ?? [])); - page = body.nextPage; - } - return items; -} diff --git a/src/api/rateLimit.test.ts b/src/api/rateLimit.test.ts deleted file mode 100644 index 66f6823..0000000 --- a/src/api/rateLimit.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it } from 'vitest'; -import { type Clock, RateLimiter, bucketFromEnvironment, defaultBucket } from './rateLimit.ts'; - -// Wall-clock assertions made these tests fail whenever the machine was busy. This clock -// advances only when the limiter asks to sleep, so the numbers below are exact. -function fakeClock(): Clock & { elapsed(): number } { - let millis = 0; - return { - now: () => millis, - sleep: async requested => { - millis += requested; - }, - elapsed: () => millis, - }; -} - -describe('RateLimiter', () => { - it('should let the whole burst through without pacing', async () => { - const clock = fakeClock(); - const limiter = new RateLimiter({ burst: 20, refillTokens: 1, refillIntervalMillis: 10000 }, clock); - - for (let i = 0; i < 20; i++) { - await limiter.acquire(); - } - - expect(clock.elapsed()).toEqual(0); - }); - - it('should pace at the refill rate once the burst is spent', async () => { - const clock = fakeClock(); - const limiter = new RateLimiter({ burst: 4, refillTokens: 1, refillIntervalMillis: 100 }, clock); - - for (let i = 0; i < 7; i++) { - await limiter.acquire(); - } - - // Four free, then one per 100ms interval for the remaining three. - expect(clock.elapsed()).toEqual(300); - }); - - it('should bound concurrent callers, not just sequential ones', async () => { - const clock = fakeClock(); - const limiter = new RateLimiter({ burst: 3, refillTokens: 1, refillIntervalMillis: 200 }, clock); - - const admittedAt: number[] = []; - await Promise.all(Array.from({ length: 6 }, () => limiter.acquire().then(() => admittedAt.push(clock.now())))); - - expect(admittedAt).toEqual([0, 0, 0, 200, 400, 600]); - }); - - it('should refill over time rather than all at once', async () => { - const clock = fakeClock(); - const limiter = new RateLimiter({ burst: 2, refillTokens: 2, refillIntervalMillis: 200 }, clock); - await limiter.acquire(); - await limiter.acquire(); - - // A full interval restores the burst, and no more than the burst. - await clock.sleep(200); - await limiter.acquire(); - await limiter.acquire(); - expect(clock.elapsed()).toEqual(200); - - await limiter.acquire(); - expect(clock.elapsed()).toBeGreaterThan(200); - }); - - it('should not accumulate more than the burst while idle', async () => { - const clock = fakeClock(); - const limiter = new RateLimiter({ burst: 2, refillTokens: 2, refillIntervalMillis: 100 }, clock); - - await clock.sleep(10_000); // idle far longer than it takes to refill - - await limiter.acquire(); - await limiter.acquire(); - expect(clock.elapsed()).toEqual(10_000); - - await limiter.acquire(); - expect(clock.elapsed()).toBeGreaterThan(10_000); - }); - - describe('millisFor', () => { - it('should charge nothing for a run inside the burst', () => { - expect(new RateLimiter().millisFor(100)).toEqual(0); - }); - - it('should charge the refill rate beyond the burst', () => { - // 200 requests is 100 beyond the burst, which is four refills of 15s. - expect(new RateLimiter().millisFor(200)).toEqual(60000); - }); - }); - - describe('bucketFromEnvironment', () => { - it('should use the documented allowance when nothing is set', () => { - expect(bucketFromEnvironment({})).toEqual(defaultBucket); - }); - - it('should take overrides, with the interval given in seconds', () => { - expect( - bucketFromEnvironment({ - STEADYBIT_RATE_LIMIT_BURST: '10', - STEADYBIT_RATE_LIMIT_REFILL: '5', - STEADYBIT_RATE_LIMIT_INTERVAL: '30', - }) - ).toEqual({ burst: 10, refillTokens: 5, refillIntervalMillis: 30000 }); - }); - - it('should take a value with surrounding whitespace', () => { - expect(bucketFromEnvironment({ STEADYBIT_RATE_LIMIT_BURST: ' 10 ' }).burst).toEqual(10); - }); - - // `Number` would have read these as 1000, 16 and 2.5. None is how a request count - // gets written on purpose, and accepting them changes the pacing silently. - it.each(['0', '-1', 'abc', 'NaN', '1e3', '0x10', '2.5', '10,5', '+5'])( - 'should fall back and complain about %s', - value => { - const complaints: string[] = []; - const original = console.error; - console.error = (m: string) => complaints.push(m); - try { - expect(bucketFromEnvironment({ STEADYBIT_RATE_LIMIT_BURST: value }).burst).toEqual(defaultBucket.burst); - } finally { - console.error = original; - } - expect(complaints).toHaveLength(1); - expect(complaints[0]).toContain('STEADYBIT_RATE_LIMIT_BURST'); - } - ); - }); - - it('should default to the allowance the platform documents', () => { - // A burst of 100, refilling by 25 every 15s. - expect(defaultBucket).toEqual({ burst: 100, refillTokens: 25, refillIntervalMillis: 15000 }); - }); -}); diff --git a/src/api/rateLimit.ts b/src/api/rateLimit.ts deleted file mode 100644 index d486e0c..0000000 --- a/src/api/rateLimit.ts +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { setTimeout as sleep } from 'node:timers/promises'; - -// The platform limits requests with a token bucket: a burst of 100 is allowed, and the -// allowance then refills by 25 every 15 seconds. A fan-out like `experiment dump` issues -// far more than that, and because every rejected request retries into the window it just -// exhausted, the retries make the pressure worse rather than better. -// -// Pacing to the documented allowance from the first request keeps 429s rare instead of -// routine. Nothing is learned from response headers: `RateLimit-Limit: 100;w=15` reports -// the burst but not the refill rate, and taking it at face value paces four times too -// fast. Where the limit is exceeded anyway, the Retry-After handling in http.ts adapts. -export const defaultBucket = { - burst: 100, - refillTokens: 25, - refillIntervalMillis: 15000, -}; - -export type BucketOptions = typeof defaultBucket; - -// Injected so that tests can drive the bucket deterministically. Asserting on wall-clock -// windows made them fail whenever the machine was busy. -export interface Clock { - now(): number; - sleep(millis: number): Promise; -} - -const systemClock: Clock = { - now: () => Date.now(), - sleep: millis => sleep(millis), -}; - -// A deployment may be configured with a different allowance, and the platform does not -// advertise one that could be read instead: the ratelimit-* headers appear only on the -// 429 itself, by which point remaining is zero. -export function bucketFromEnvironment(env: NodeJS.ProcessEnv = process.env): BucketOptions { - return { - burst: positiveInteger(env.STEADYBIT_RATE_LIMIT_BURST, 'STEADYBIT_RATE_LIMIT_BURST', defaultBucket.burst), - refillTokens: positiveInteger( - env.STEADYBIT_RATE_LIMIT_REFILL, - 'STEADYBIT_RATE_LIMIT_REFILL', - defaultBucket.refillTokens - ), - refillIntervalMillis: - positiveInteger( - env.STEADYBIT_RATE_LIMIT_INTERVAL, - 'STEADYBIT_RATE_LIMIT_INTERVAL', - defaultBucket.refillIntervalMillis / 1000 - ) * 1000, - }; -} - -// Plain decimal digits only. `Number` would also have taken '1e3', '0x10' and '2.5', -// which are not how anyone means to write a request count, and reading '0x10' as 16 -// would quietly change how hard the CLI hits the platform. -const POSITIVE_INTEGER = /^\d+$/; - -function positiveInteger(value: string | undefined, name: string, fallback: number): number { - if (value === undefined || value.trim() === '') { - return fallback; - } - const trimmed = value.trim(); - if (!POSITIVE_INTEGER.test(trimmed) || Number(trimmed) <= 0) { - // Warned about rather than ignored: a typo here silently changes how hard the CLI - // hits the platform, which is the last thing that should fail quietly. - console.error(`Ignoring ${name}: '${value}' is not a positive whole number. Using ${fallback}.`); - return fallback; - } - return Number(trimmed); -} - -export class RateLimiter { - private tokens: number; - private lastRefill: number; - private gate: Promise = Promise.resolve(); - - constructor( - private readonly bucket: BucketOptions = defaultBucket, - private readonly clock: Clock = systemClock - ) { - this.tokens = bucket.burst; - this.lastRefill = clock.now(); - } - - // How long `count` requests take once the burst is spent, which is what makes the - // scale of a large dump visible before it starts rather than an hour into it. - millisFor(count: number): number { - const beyondBurst = Math.max(0, count - this.bucket.burst); - return (beyondBurst / this.bucket.refillTokens) * this.bucket.refillIntervalMillis; - } - - // Serialised, so that concurrent callers cannot all spend the same token. - acquire(): Promise { - const admitted = this.gate.then(() => this.reserve()); - this.gate = admitted.catch(() => undefined); - return admitted; - } - - private async reserve(): Promise { - for (;;) { - this.refill(); - if (this.tokens >= 1) { - this.tokens--; - return; - } - await this.clock.sleep(this.millisUntilNextToken()); - } - } - - private refill(): void { - const now = this.clock.now(); - this.tokens = Math.min(this.bucket.burst, this.tokens + (now - this.lastRefill) * this.tokensPerMilli()); - this.lastRefill = now; - } - - private tokensPerMilli(): number { - return this.bucket.refillTokens / this.bucket.refillIntervalMillis; - } - - private millisUntilNextToken(): number { - return Math.max(1, Math.ceil((1 - this.tokens) / this.tokensPerMilli())); - } -} - -let shared: RateLimiter | undefined; - -// Built on first use, not at import. The parent `steadybit` process pulls this module in -// through http.ts just to dispatch to a subcommand and never makes a request, so eager -// construction had it read the environment — and complain about a bad value — twice. -function sharedLimiter(): RateLimiter { - return (shared ??= new RateLimiter(bucketFromEnvironment())); -} - -export const rateLimiter = { - acquire: () => sharedLimiter().acquire(), - millisFor: (count: number) => sharedLimiter().millisFor(count), -}; diff --git a/src/api/schemas.ts b/src/api/schemas.ts deleted file mode 100644 index 9e162d2..0000000 --- a/src/api/schemas.ts +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { components } from './generated/platform-api.ts'; - -// The platform's request and response bodies, generated from its OpenAPI spec. Commands -// type what they send and receive against these, so that a breaking change in the spec -// fails the type check rather than a customer's pipeline. -export type Schemas = components['schemas']; diff --git a/src/cli/help.ts b/src/cli/help.ts deleted file mode 100644 index 59f9e13..0000000 --- a/src/cli/help.ts +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { Command } from 'commander'; - -// Every command shows at least one example under --help. An option list says what can -// be passed, not which combination does the thing a pipeline author came for. -export function withExamples(command: Command, examples: string[]): Command { - const lines = examples.map(example => ` $ ${example}`).join('\n'); - return command.addHelpText('after', `\nExamples:\n${lines}\n`); -} diff --git a/src/cli/options.test.ts b/src/cli/options.test.ts deleted file mode 100644 index e5f8390..0000000 --- a/src/cli/options.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it } from 'vitest'; -import { collectKeyValue, parseDecimal } from './options.ts'; - -describe('options', () => { - it('collects repeated KEY=VALUE pairs, splitting on the first =', () => { - const first = collectKeyValue('url=http://shop?a=b'); - expect(collectKeyValue('CLUSTER=prod', first)).toEqual({ url: 'http://shop?a=b', CLUSTER: 'prod' }); - }); - - it('accepts an empty value but not an empty key', () => { - expect(collectKeyValue('EMPTY=')).toEqual({ EMPTY: '' }); - expect(() => collectKeyValue('=value')).toThrow("'=value' is not in the form KEY=VALUE."); - expect(() => collectKeyValue('novalue')).toThrow("'novalue' is not in the form KEY=VALUE."); - }); - - it('parses decimals regardless of the previous value', () => { - expect(parseDecimal('010')).toBe(10); - expect(() => parseDecimal('ten')).toThrow("'ten' is not a number."); - }); -}); diff --git a/src/cli/options.ts b/src/cli/options.ts deleted file mode 100644 index e1e0b52..0000000 --- a/src/cli/options.ts +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { InvalidArgumentError } from 'commander'; - -// Not `parseInt` directly: commander invokes an argument parser as (value, previous), -// so passing it wholesale turns the previous value into the radix. Repeating an option -// then parses the second value in the base of the first, silently and wrongly. -export function parseDecimal(value: string): number { - const parsed = Number.parseInt(value, 10); - if (Number.isNaN(parsed)) { - throw new InvalidArgumentError(`'${value}' is not a number.`); - } - return parsed; -} - -// For repeatable `--flag KEY=VALUE` options. Only the first `=` separates, so a value -// may itself contain one, as a URL with a query string does. -export function collectKeyValue(value: string, previous: Record = {}): Record { - const separator = value.indexOf('='); - if (separator <= 0) { - throw new InvalidArgumentError(`'${value}' is not in the form KEY=VALUE.`); - } - return { ...previous, [value.slice(0, separator)]: value.slice(separator + 1) }; -} diff --git a/src/cli/requirements.ts b/src/cli/requirements.ts deleted file mode 100644 index 3a383ba..0000000 --- a/src/cli/requirements.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { ensurePlatformAccessConfigurationIsAvailable } from '../config/requirePlatformAccess.ts'; - -export type ActionFn = (...args: any[]) => void | Promise; - -export function requirePlatformAccess(fn: ActionFn): ActionFn { - return async (...args: any[]) => { - await ensurePlatformAccessConfigurationIsAvailable(); - return fn(...args); - }; -} diff --git a/src/cli/steadybit-advice.ts b/src/cli/steadybit-advice.ts deleted file mode 100644 index 9b0b6c9..0000000 --- a/src/cli/steadybit-advice.ts +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2024 Steadybit GmbH - -import { Command, Option } from 'commander'; -import { requirePlatformAccess } from './requirements.ts'; -import { validateAdviceStatus } from '../advice/validateStatus.ts'; -import { withExamples } from './help.ts'; - -const program = new Command(); - -const validateStatus = program - .command('validate-status') - .description('Validates the status of one or multiple advice for a given environment and an optional query.') - .addOption(new Option('-e, --environment ', 'The environment name.').makeOptionMandatory(true)) - .addOption(new Option('-s, --status ', 'The expected status of the advice.').default('Implemented')) - .addOption(new Option('-q, --query ', '(optional) A target query to filter advice by targets.')) - .action(requirePlatformAccess(validateAdviceStatus)); -withExamples(validateStatus, [ - 'steadybit advice validate-status -e Global', - 'steadybit advice validate-status -e Global -q "k8s.cluster-name=dev-demo and k8s.namespace=steadybit-demo"', -]); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit-config-profile.ts b/src/cli/steadybit-config-profile.ts deleted file mode 100644 index a54152c..0000000 --- a/src/cli/steadybit-config-profile.ts +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH -import { Command, Option } from 'commander'; -import { remove } from '../config/profile/remove.ts'; -import { select } from '../config/profile/select.ts'; -import { list } from '../config/profile/list.ts'; -import { add } from '../config/profile/add.ts'; -import { defaultBaseUrl } from '../config/index.ts'; -import { withExamples } from './help.ts'; - -const program = new Command(); - -withExamples( - program - .command('add') - .description('Configure a new profile (interactively or via options).') - .addOption(new Option('-n, --name ', 'Name of the profile')) - .addOption(new Option('-b, --baseUrl ', 'Base URL to be used').default(defaultBaseUrl)) - .addOption(new Option('-t, --token ', 'Team API token')) - .action(add), - ['steadybit config profile add', 'steadybit config profile add -n prod -t "$STEADYBIT_TOKEN"'] -); -withExamples(program.command('list').description('List all configured profiles.').action(list), [ - 'steadybit config profile list', -]); -withExamples(program.command('ls').description('Alias for list.').action(list), ['steadybit config profile ls']); -withExamples(program.command('remove').description('Interactively remove an existing profile.').action(remove), [ - 'steadybit config profile remove', -]); -withExamples( - program.command('select').description('Interactively change the currently active profile.').action(select), - ['steadybit config profile select'] -); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit-config.ts b/src/cli/steadybit-config.ts deleted file mode 100644 index a9f97a4..0000000 --- a/src/cli/steadybit-config.ts +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { Command } from 'commander'; - -import { show } from '../config/show.ts'; -import { withExamples } from './help.ts'; - -const program = new Command(); - -program.command('profile', 'Configure authentication profiles.'); - -withExamples( - program.command('show').description('Show the active CLI configuration. Warning: Prints secrets!').action(show), - ['steadybit config show'] -); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit-execution.ts b/src/cli/steadybit-execution.ts deleted file mode 100644 index ce7fd9d..0000000 --- a/src/cli/steadybit-execution.ts +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { Command, Option } from 'commander'; -import { requirePlatformAccess } from './requirements.ts'; -import { withExamples } from './help.ts'; -import { parseDecimal } from './options.ts'; -import { getExecution } from '../execution/get.ts'; -import { cancel } from '../execution/cancel.ts'; -import { addProperty, setProperty } from '../execution/property.ts'; -import { downloadArtifacts, listArtifacts } from '../execution/artifacts.ts'; - -const program = new Command(); - -function idOption() { - return new Option('-i, --id ', 'The experiment run id.').makeOptionMandatory(true).argParser(parseDecimal); -} - -withExamples( - program - .command('get') - .description( - 'Get an experiment run, including its steps and target executions. Output is written to file or stdout.' - ) - .addOption(idOption()) - .addOption(new Option('-f, --file ', 'The path to write the experiment run to.')) - .addOption( - new Option( - '-t, --type ', - 'The output format ("json" or "yaml"). (default: "json" if the file ends in ".json", "yaml" otherwise.)' - ) - ) - .action(requirePlatformAccess(getExecution)), - ['steadybit execution get -i 1234', 'steadybit execution get -i 1234 -t json | jq .state'] -); - -withExamples( - program - .command('cancel') - .description('Cancel a running experiment run. The run stops as soon as its agents have been told.') - .addOption(idOption()) - .action(requirePlatformAccess(cancel)), - ['steadybit execution cancel -i 1234'] -); - -const property = program.command('property').description('Change the properties of an experiment run.'); - -withExamples( - property - .command('set') - .description( - 'Set the value of a property of an experiment run. Only properties editable in a run can be changed. Several --value set a list property.' - ) - .addOption(idOption()) - .addOption(new Option('-k, --key ', 'The property key.').makeOptionMandatory(true)) - .addOption(new Option('--value ', 'The value to set.').makeOptionMandatory(true)) - .addOption(new Option('--json', 'Parse each value as JSON, to send a number or an object.').default(false)) - .action(requirePlatformAccess(setProperty)), - [ - 'steadybit execution property set -i 1234 -k approvedBy --value "Jane Doe"', - 'steadybit execution property set -i 1234 -k tickets --value SHOP-1 SHOP-2', - 'steadybit execution property set -i 1234 -k score --value 7 --json', - ] -); - -withExamples( - property - .command('add') - .description('Add a value to a list property of an experiment run.') - .addOption(idOption()) - .addOption(new Option('-k, --key ', 'The property key.').makeOptionMandatory(true)) - .addOption(new Option('--value ', 'The value to add.').makeOptionMandatory(true).argParser(v => [v])) - .addOption(new Option('--json', 'Parse the value as JSON, to send a number or an object.').default(false)) - .action(requirePlatformAccess(addProperty)), - ['steadybit execution property add -i 1234 -k tickets --value SHOP-3'] -); - -const artifact = program.command('artifact').description('List and download the artifacts of an experiment run.'); - -withExamples( - artifact - .command('list') - .description('List the artifacts that the actions of an experiment run attached.') - .addOption(idOption()) - .action(requirePlatformAccess(listArtifacts)), - ['steadybit execution artifact list -i 1234'] -); - -withExamples( - artifact - .command('download') - .description( - 'Download the artifacts of an experiment run into //. Without filters, all of them are downloaded.' - ) - .addOption(idOption()) - .addOption(new Option('-a, --artifact ', 'Only download artifacts with this id, usually the file name.')) - .addOption(new Option('--target-execution ', 'Only download artifacts of this target execution.')) - .addOption(new Option('-d, --directory ', 'The directory to download into.').default('.')) - .addOption( - new Option( - '-o, --output ', - 'Write the artifact to this file instead. Requires exactly one match.' - ).conflicts('directory') - ) - .action(requirePlatformAccess(downloadArtifacts)), - [ - 'steadybit execution artifact download -i 1234 -d ./artifacts', - 'steadybit execution artifact download -i 1234 -a jmeter-report.zip -o report.zip', - ] -); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit-experiment.ts b/src/cli/steadybit-experiment.ts deleted file mode 100644 index aaf878b..0000000 --- a/src/cli/steadybit-experiment.ts +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { Command, Option } from 'commander'; -import { executeExperiments } from '../experiment/exec.ts'; -import { getExperiment } from '../experiment/get.ts'; -import { dump } from '../experiment/dump.ts'; -import { applyExperiments } from '../experiment/apply.ts'; -import { deleteExperiment } from '../experiment/delete.ts'; -import { requirePlatformAccess } from './requirements.ts'; -import { withExamples } from './help.ts'; -import { collectKeyValue, parseDecimal } from './options.ts'; - -const program = new Command(); - -// Shared by `run` and `apply`, which both render an experiment from a template when -// --template is given instead of reading it from a file. -function addTemplateOptions(command: Command): Command { - return command - .addOption( - new Option('--template ', 'Create the experiment from the experiment template with this id.').conflicts( - 'file' - ) - ) - .addOption(new Option('--team ', 'With --template: the key of the team owning the experiment.')) - .addOption(new Option('--environment ', 'With --template: the environment the experiment runs in.')) - .addOption( - new Option( - '--external-id ', - 'With --template: an identifier of your own. Using the same one again updates the experiment it created before.' - ) - ) - .addOption( - new Option('-p, --placeholder ', 'With --template: a placeholder value. Repeat for more.').argParser( - collectKeyValue - ) - ) - .addOption( - new Option( - '--placeholders ', - 'With --template: a YAML/JSON file mapping placeholder keys to values. -p overrides entries.' - ) - ) - .addOption( - new Option( - '--variable ', - 'With --template: an experiment variable to add to the experiment. Repeat for more.' - ).argParser(collectKeyValue) - ) - .addOption( - new Option( - '--no-reset-properties', - 'With --template: keep the properties of an existing experiment instead of resetting them to the template.' - ) - ); -} - -const run = program - .command('run') - .alias('exec') - .description('Executes an experiment run. If a file is specified the experiment is saved before execution.') - .addOption(new Option('-k, --key ', 'The experiment key.').conflicts('file')) - .addOption( - new Option( - '-f, --file ', - 'The path to the experiment file or a directory containing multiple files.' - ).conflicts('key') - ) - .addOption( - new Option( - '-R, --recursive', - 'Process the directory used in -f, --file recursively. Useful when you want to manage related experiments organized within the same directory.' - ).default(false) - ) - .addOption(new Option('--no-wait', 'Do not wait for experiment run to finish.')) - .addOption( - new Option( - '--yes', - 'Skip the prompt asking for experiment run confirmation. Not necessary when no TTY is attached.' - ).default(false) - ) - .addOption( - new Option( - '--allowParallel', - 'Skip the prompt warning about another experiment running and allow always parallel execution.' - ).default(false) - ) - .addOption( - new Option( - '--retries ', - 'Number of retries when the experiment fails validation (e.g., missing targets). 0 means no retry.' - ) - .default(0) - .argParser(parseDecimal) - ) - .addOption( - new Option('--retryInterval ', 'Interval in seconds between retries.').default(10).argParser(parseDecimal) - ) - .addOption( - new Option( - '--execution-variable ', - 'With --template: a variable for this run only, overriding experiment and environment variables. Repeat for more.' - ).argParser(collectKeyValue) - ); -addTemplateOptions(run).action(requirePlatformAccess(executeExperiments)); -withExamples(run, [ - 'steadybit experiment run -k ADM-1', - 'steadybit experiment run -f experiment.yml --no-wait', - 'steadybit experiment run -f ./experiments -R --yes', - 'steadybit experiment run --template d7e65100-1d20-4980-be87-c351704910b8 --team ADM --environment Global -p CLUSTER=prod', -]); - -const get = program - .command('get') - .description('Get an experiment from Steadybit. Output is written to file or stdout.') - .addOption(new Option('-k, --key ', 'The experiment key.').makeOptionMandatory(true)) - .addOption(new Option('-f, --file ', 'The path to the experiment file.')) - .addOption( - new Option( - '-t, --type ', - 'The output format of the experiment ("json" or "yaml"). (default: if a file with ".json"-suffix is given: "json", "yaml" otherwise.)' // intentionally documented here and not using .default(flags, description) as otherwise the file-extension-logic isn't working - ) - ) - .action(requirePlatformAccess(getExperiment)); -withExamples(get, ['steadybit experiment get -k ADM-1', 'steadybit experiment get -k ADM-1 -f experiment.json']); - -const apply = program - .command('apply') - .description( - 'Upload an experiment to Steadybit. If a key is provided, an update is performed. Otherwise, the externalId from the file is used to create or update the experiment. With --template, the experiment is created from an experiment template instead of a file.' - ) - .addOption(new Option('-k, --key ', 'The experiment key.')) - .addOption( - new Option( - '-f, --file ', - 'The path to the experiment file or a directory containing multiple files' - ).conflicts('template') - ) - .addOption( - new Option( - '-R, --recursive', - 'Process the directory used in -f, --file recursively. Useful when you want to manage related experiments organized within the same directory.' - ).default(false) - ); -addTemplateOptions(apply).action(requirePlatformAccess(applyExperiments)); -withExamples(apply, [ - 'steadybit experiment apply -f experiment.yml', - 'steadybit experiment apply -f ./experiments -R', - 'steadybit experiment apply --template d7e65100-1d20-4980-be87-c351704910b8 --team ADM --external-id shop-latency -p CLUSTER=prod', - 'steadybit experiment apply --template d7e65100-1d20-4980-be87-c351704910b8 -k ADM-12 --placeholders values.yml', -]); - -const del = program - .command('delete') - .description('Delete an experiment from Steadybit.') - .addOption(new Option('-k, --key ', 'The experiment key.').makeOptionMandatory(true)) - .action(requirePlatformAccess(deleteExperiment)); -withExamples(del, ['steadybit experiment delete -k ADM-1']); - -const dumpCommand = program - .command('dump') - .description('Dump all experiments and executions from all teams in Steadybit.') - .addOption(new Option('-d, --directory ', 'The path to dump all the experiments to').default('.')) - .addOption(new Option('-t, --type ', 'The output format of the experiment ("json" or "yaml").').default('yaml')) - .addOption( - new Option('--team ', 'Only dump the given teams, by team key. Defaults to every accessible team.') - ) - .action(requirePlatformAccess(dump)); -withExamples(dumpCommand, [ - 'steadybit experiment dump -d ./dump', - 'steadybit experiment dump -d ./dump -t json --team ADM WEBHOOK', -]); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit-schedule.ts b/src/cli/steadybit-schedule.ts deleted file mode 100644 index a603666..0000000 --- a/src/cli/steadybit-schedule.ts +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { Command, Option } from 'commander'; -import { requirePlatformAccess } from './requirements.ts'; -import { withExamples } from './help.ts'; -import { collectKeyValue } from './options.ts'; -import { - applySchedules, - createSchedule, - deleteSchedule, - disableSchedule, - enableSchedule, - getSchedule, - listSchedules, - updateSchedule, -} from '../schedule/commands.ts'; - -const program = new Command(); - -function idOption() { - return new Option('-i, --id ', 'The experiment schedule id.').makeOptionMandatory(true); -} - -// Shared by `create` and `update`, which take the same fields as flags. -function addScheduleFields(command: Command): Command { - return command - .addOption( - new Option( - '--cron ', - 'Run repeatedly on this Quartz cron expression (seconds first), e.g. "0 0 9 ? * MON-FRI".' - ) - ) - .addOption(new Option('--start-at ', 'Run once at this ISO 8601 time, e.g. 2026-10-01T09:00:00Z.')) - .addOption(new Option('--timezone ', 'The timezone of the cron expression, e.g. Europe/Berlin.')) - .addOption(new Option('--allow-parallel', 'Run even when another experiment is running.')) - .addOption(new Option('--no-allow-parallel', 'Skip the run when another experiment is running.')) - .addOption( - new Option( - '--variable ', - 'A variable for the scheduled runs, overriding experiment and environment variables. Repeat for more.' - ).argParser(collectKeyValue) - ); -} - -withExamples( - program - .command('list') - .description('List experiment schedules.') - .addOption(new Option('--team ', 'Only list schedules of these teams, by team key.')) - .addOption(new Option('--experiment ', 'Only list schedules of these experiments, by experiment key.')) - .action(requirePlatformAccess(listSchedules)), - ['steadybit schedule list', 'steadybit schedule list --team ADM --experiment ADM-1 ADM-2'] -); - -withExamples( - program - .command('get') - .description('Get an experiment schedule. Output is written to file or stdout.') - .addOption(idOption()) - .addOption(new Option('-f, --file ', 'The path to write the schedule to.')) - .addOption( - new Option( - '-t, --type ', - 'The output format ("json" or "yaml"). (default: "json" if the file ends in ".json", "yaml" otherwise.)' - ) - ) - .action(requirePlatformAccess(getSchedule)), - ['steadybit schedule get -i 01951394-727f-76a0-8675-c7519ebd0ff5 -f schedule.yml'] -); - -withExamples( - program - .command('apply') - .description( - 'Create or update experiment schedules from files. A file without an id creates a schedule, and the new id is written back to it.' - ) - .addOption( - new Option( - '-f, --file ', - 'The path to the schedule file or a directory containing multiple files.' - ).makeOptionMandatory(true) - ) - .addOption(new Option('-R, --recursive', 'Process the directory used in -f, --file recursively.').default(false)) - .action(requirePlatformAccess(applySchedules)), - ['steadybit schedule apply -f schedule.yml', 'steadybit schedule apply -f ./schedules -R'] -); - -withExamples( - addScheduleFields( - program - .command('create') - .description('Schedule an experiment, either repeatedly with --cron or once with --start-at.') - .addOption( - new Option('-k, --experiment ', 'The key of the experiment to schedule.').makeOptionMandatory(true) - ) - .addOption(new Option('--disabled', 'Create the schedule disabled.')) - ).action(requirePlatformAccess(createSchedule)), - [ - 'steadybit schedule create -k ADM-1 --cron "0 0 9 ? * MON-FRI" --timezone Europe/Berlin', - 'steadybit schedule create -k ADM-1 --start-at 2026-10-01T09:00:00Z --no-allow-parallel', - ] -); - -withExamples( - addScheduleFields( - program - .command('update') - .description('Change an experiment schedule. Only the given fields are changed.') - .addOption(idOption()) - ).action(requirePlatformAccess(updateSchedule)), - ['steadybit schedule update -i 01951394-727f-76a0-8675-c7519ebd0ff5 --cron "0 30 8 ? * *"'] -); - -withExamples( - program - .command('enable') - .description('Enable an experiment schedule.') - .addOption(idOption()) - .action(requirePlatformAccess(enableSchedule)), - ['steadybit schedule enable -i 01951394-727f-76a0-8675-c7519ebd0ff5'] -); - -withExamples( - program - .command('disable') - .description('Disable an experiment schedule without deleting it.') - .addOption(idOption()) - .action(requirePlatformAccess(disableSchedule)), - ['steadybit schedule disable -i 01951394-727f-76a0-8675-c7519ebd0ff5'] -); - -withExamples( - program - .command('delete') - .description('Delete an experiment schedule.') - .addOption(idOption()) - .action(requirePlatformAccess(deleteSchedule)), - ['steadybit schedule delete -i 01951394-727f-76a0-8675-c7519ebd0ff5'] -); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit-service-profile.ts b/src/cli/steadybit-service-profile.ts deleted file mode 100644 index 03adf37..0000000 --- a/src/cli/steadybit-service-profile.ts +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { Command, Option } from 'commander'; -import { requirePlatformAccess } from './requirements.ts'; -import { withExamples } from './help.ts'; -import { - applyServiceProfiles, - deleteServiceProfile, - getServiceProfile, - listServiceProfiles, -} from '../serviceProfile/commands.ts'; - -const program = new Command(); - -const PROFILE_ID = '019eacd7-fb2c-733a-bed5-99a935323db5'; - -function idOption() { - return new Option('-i, --id ', 'The service profile id.').makeOptionMandatory(true); -} - -withExamples( - program - .command('list') - .description('List service profiles.') - .addOption(new Option('--name ', 'Only list profiles whose name contains this.')) - .addOption(new Option('--origin ', 'Only list "provided" or "custom" profiles.')) - .addOption(new Option('--default', 'Only list the default profile.')) - .action(requirePlatformAccess(listServiceProfiles)), - ['steadybit service-profile list', 'steadybit service-profile list --origin custom'] -); - -withExamples( - program - .command('get') - .description('Get a service profile. Output is written to file or stdout.') - .addOption(idOption()) - .addOption(new Option('-f, --file ', 'The path to write the service profile to.')) - .addOption( - new Option( - '-t, --type ', - 'The output format ("json" or "yaml"). (default: "json" if the file ends in ".json", "yaml" otherwise.)' - ) - ) - .action(requirePlatformAccess(getServiceProfile)), - [`steadybit service-profile get -i ${PROFILE_ID} -f profile.yml`] -); - -withExamples( - program - .command('apply') - .description( - 'Create or update service profiles from files. A file without an id creates a profile, and the new id is written back to it.' - ) - .addOption( - new Option( - '-f, --file ', - 'The path to the service profile file or a directory containing multiple files.' - ).makeOptionMandatory(true) - ) - .addOption(new Option('-R, --recursive', 'Process the directory used in -f, --file recursively.').default(false)) - .addOption( - new Option( - '--delete-experiments', - 'Delete the provided experiments of services that use templates removed from the profile.' - ).default(false) - ) - .action(requirePlatformAccess(applyServiceProfiles)), - ['steadybit service-profile apply -f profile.yml'] -); - -withExamples( - program - .command('delete') - .description('Delete a custom service profile.') - .addOption(idOption()) - .action(requirePlatformAccess(deleteServiceProfile)), - [`steadybit service-profile delete -i ${PROFILE_ID}`] -); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit-service.ts b/src/cli/steadybit-service.ts deleted file mode 100644 index 8cf007e..0000000 --- a/src/cli/steadybit-service.ts +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { Command, Option } from 'commander'; -import { requirePlatformAccess } from './requirements.ts'; -import { withExamples } from './help.ts'; -import { collectKeyValue, parseDecimal } from './options.ts'; -import { - applyServices, - deleteService, - getService, - getServiceVariables, - linkExperiment, - listServiceExperiments, - listServices, - provideExperiment, - setServiceVariables, - showServiceRisk, - unlinkExperiment, -} from '../service/commands.ts'; - -const program = new Command(); - -const SERVICE_ID = '019cd80d-a4c9-775b-bdf8-2672a280ce7c'; - -function idOption() { - return new Option('-i, --id ', 'The service id.').makeOptionMandatory(true); -} - -function typeOption() { - return new Option( - '-t, --type ', - 'The output format ("json" or "yaml"). (default: "json" if the file ends in ".json", "yaml" otherwise.)' - ); -} - -withExamples( - program - .command('list') - .description('List services. Filters of the same kind match any of the given values.') - .addOption(new Option('--team ', 'Only list services of these teams, by team key.')) - .addOption(new Option('--environment ', 'Only list services in these environments.')) - .addOption(new Option('--experiment ', 'Only list services these experiments are linked to.')) - .action(requirePlatformAccess(listServices)), - ['steadybit service list', 'steadybit service list --team ADM --environment Global'] -); - -withExamples( - program - .command('get') - .description('Get a service. Output is written to file or stdout.') - .addOption(idOption()) - .addOption(new Option('-f, --file ', 'The path to write the service to.')) - .addOption(typeOption()) - .action(requirePlatformAccess(getService)), - [`steadybit service get -i ${SERVICE_ID} -f service.yml`] -); - -withExamples( - program - .command('apply') - .description( - 'Create or update services from files. A file without an id creates a service, and the new id is written back to it.' - ) - .addOption( - new Option( - '-f, --file ', - 'The path to the service file or a directory containing multiple files.' - ).makeOptionMandatory(true) - ) - .addOption(new Option('-R, --recursive', 'Process the directory used in -f, --file recursively.').default(false)) - .addOption( - new Option( - '--delete-experiments', - 'When the service profile changes, delete provided experiments whose templates the new profile does not contain. Without it, such a change is refused.' - ).default(false) - ) - .action(requirePlatformAccess(applyServices)), - ['steadybit service apply -f service.yml', 'steadybit service apply -f ./services -R'] -); - -withExamples( - program - .command('delete') - .description('Delete a service.') - .addOption(idOption()) - .action(requirePlatformAccess(deleteService)), - [`steadybit service delete -i ${SERVICE_ID}`] -); - -withExamples( - program - .command('risk') - .description('Show the risk score of a service, overall, per category and per experiment.') - .addOption(idOption()) - .addOption(new Option('-t, --type ', 'Print the raw risk as "json" or "yaml" instead of tables.')) - .addOption( - new Option( - '--fail-above ', - 'Exit with a non-zero status when the overall risk is above this score.' - ).argParser(parseDecimal) - ) - .action(requirePlatformAccess(showServiceRisk)), - [`steadybit service risk -i ${SERVICE_ID}`, `steadybit service risk -i ${SERVICE_ID} --fail-above 50`] -); - -const experiment = program.command('experiment').description('Manage the experiments of a service.'); - -withExamples( - experiment - .command('list') - .description( - 'List the experiments of a service: those provided by its service profile, created or not, and custom ones linked to it.' - ) - .addOption(idOption()) - .addOption(new Option('--category ', 'Only list experiments in these categories.')) - .addOption(new Option('--type ', 'Only list "provided" or "custom" experiments.')) - .action(requirePlatformAccess(listServiceExperiments)), - [ - `steadybit service experiment list -i ${SERVICE_ID}`, - `steadybit service experiment list -i ${SERVICE_ID} --type custom`, - ] -); - -withExamples( - experiment - .command('provide') - .description("Create or update a provided experiment of a service from one of its service profile's templates.") - .addOption(idOption()) - .addOption( - new Option('--template ', 'The template, which must be part of the service profile.').makeOptionMandatory( - true - ) - ) - .addOption( - new Option('-k, --experiment ', 'Update this existing provided experiment instead of creating one.') - ) - .addOption( - new Option('-p, --placeholder ', 'A placeholder value. Repeat for more.').argParser(collectKeyValue) - ) - .addOption( - new Option('--placeholders ', 'A YAML/JSON file mapping placeholder keys to values. -p overrides entries.') - ) - .addOption( - new Option( - '--no-reset-properties', - 'Keep the properties of an existing experiment instead of resetting them to the template.' - ) - ) - .action(requirePlatformAccess(provideExperiment)), - [ - `steadybit service experiment provide -i ${SERVICE_ID} --template d7e65100-1d20-4980-be87-c351704910b8 -p REPLICAS=3`, - ] -); - -withExamples( - experiment - .command('link') - .description('Link an existing experiment to a service as a custom experiment.') - .addOption(idOption()) - .addOption(new Option('-k, --experiment ', 'The experiment to link.').makeOptionMandatory(true)) - .addOption(new Option('--category ', 'The category to link it in.').makeOptionMandatory(true)) - .action(requirePlatformAccess(linkExperiment)), - [`steadybit service experiment link -i ${SERVICE_ID} -k ADM-1 --category Redundancy`] -); - -withExamples( - experiment - .command('unlink') - .description('Remove a custom experiment from a service. The experiment itself is kept.') - .addOption(idOption()) - .addOption(new Option('-k, --experiment ', 'The experiment to unlink.').makeOptionMandatory(true)) - .action(requirePlatformAccess(unlinkExperiment)), - [`steadybit service experiment unlink -i ${SERVICE_ID} -k ADM-1`] -); - -const variable = program.command('variable').description('Manage the variables of a service.'); - -withExamples( - variable - .command('get') - .description('Print the variables of a service.') - .addOption(idOption()) - .addOption(new Option('-t, --type ', 'The output format ("json" or "yaml").').default('yaml')) - .action(requirePlatformAccess(getServiceVariables)), - [`steadybit service variable get -i ${SERVICE_ID}`] -); - -withExamples( - variable - .command('set') - .description( - 'Set variables of a service, keeping the others. With --replace, the given variables become the only ones.' - ) - .argument('[KEY=VALUE...]', 'Variables to set.') - .addOption(idOption()) - .addOption( - new Option( - '-f, --file ', - 'A YAML/JSON file mapping variable names to values, which may be lists or select expressions.' - ) - ) - .addOption(new Option('--replace', 'Remove every variable not given.').default(false)) - .action(requirePlatformAccess(setServiceVariables)), - [ - `steadybit service variable set -i ${SERVICE_ID} endpoint=http://shop.internal region=eu`, - `steadybit service variable set -i ${SERVICE_ID} -f variables.yml --replace`, - ] -); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit-template.ts b/src/cli/steadybit-template.ts deleted file mode 100644 index 71fddc0..0000000 --- a/src/cli/steadybit-template.ts +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { Command, Option } from 'commander'; -import { requirePlatformAccess } from './requirements.ts'; -import { withExamples } from './help.ts'; -import { getTemplate, listTemplates } from '../template/commands.ts'; - -const program = new Command(); - -withExamples( - program - .command('list') - .description('List experiment templates. Filters of the same kind match any of the given values.') - .addOption(new Option('--tag ', 'Only list templates with one of these tags.')) - .addOption(new Option('--target-type ', 'Only list templates targeting one of these target types.')) - .addOption(new Option('--action ', 'Only list templates using one of these actions.')) - .addOption(new Option('--search ', 'Only list templates whose title or description match.')) - .action(requirePlatformAccess(listTemplates)), - [ - 'steadybit template list', - 'steadybit template list --search kubernetes --action com.steadybit.extension_host.stress-cpu', - ] -); - -withExamples( - program - .command('get') - .description('Get an experiment template. Output is written to file or stdout.') - .addOption(new Option('-i, --id ', 'The experiment template id.').makeOptionMandatory(true)) - .addOption(new Option('-f, --file ', 'The path to write the template to.')) - .addOption( - new Option( - '-t, --type ', - 'The output format ("json" or "yaml"). (default: "json" if the file ends in ".json", "yaml" otherwise.)' - ) - ) - .addOption( - new Option( - '--placeholders', - 'Only output the template placeholders, as a file to fill in and pass to --placeholders.' - ) - ) - .action(requirePlatformAccess(getTemplate)), - [ - 'steadybit template get -i d7e65100-1d20-4980-be87-c351704910b8', - 'steadybit template get -i d7e65100-1d20-4980-be87-c351704910b8 --placeholders -f values.yml', - ] -); - -program.parseAsync(process.argv); diff --git a/src/cli/steadybit.ts b/src/cli/steadybit.ts deleted file mode 100644 index 4c0c762..0000000 --- a/src/cli/steadybit.ts +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env node -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { Command, Option } from 'commander'; -import colors from '../colors.ts'; -import { satisfies } from 'semver'; -import { enableRequestLogging } from '../api/http.ts'; -import { packageJson } from '../packageJson.ts'; -import { withExamples } from './help.ts'; - -const requiredNodejsVersion = packageJson.engines.node; -const actualNodejsVersion = process.version; - -if (!satisfies(actualNodejsVersion, requiredNodejsVersion)) { - const help = ` -Node.js version ${actualNodejsVersion} is not supported. The Steadybit CLI -requires a Node.js version that satisfies the following version range: - - ${colors.bold(requiredNodejsVersion)} - -We recommend to install Node.js via a version manager. For example, -using the Node Version Manager (NVM): - - ${colors.bold('https://github.com/nvm-sh/nvm#readme')} -`; - console.error(colors.red(help.trim())); - process.exit(1); -} - -const program = new Command() - .version(packageJson.version) - .addOption(new Option('-v, --verbose', 'Enable verbose logging').default(false)) - .hook('preSubcommand', thisCommand => { - if (thisCommand.opts().verbose) { - enableRequestLogging(); - } - }) - .command('advice', 'Show/verify advice status.') - .command('config', 'Show/modify the CLI configuration and authentication profiles.') - .command('execution', 'Inspect, cancel and annotate experiment runs, and download their artifacts.') - .command('experiment', 'Check and run experiments.') - .command('schedule', 'Schedule experiments.') - .command('service', 'Manage services, their experiments, variables and risk.') - .command('service-profile', 'Manage the service profiles that provide experiments to services.') - .command('template', 'Find experiment templates to create experiments from.'); - -withExamples(program, [ - 'steadybit experiment run -f experiment.yml', - 'steadybit schedule list --team ADM', - 'steadybit experiment --help', -]); - -program.parseAsync(process.argv); diff --git a/src/colors.ts b/src/colors.ts deleted file mode 100644 index 3cdaaea..0000000 --- a/src/colors.ts +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { createColors } from 'picocolors'; - -// picocolors enables colouring whenever CI is set, and unconditionally on win32, even -// when stdout is a pipe. The CLI's output is routinely parsed by GitOps pipelines, so -// colouring is gated on stdout actually being a terminal instead, which is what the -// previously used `colors` package did. -export const colorsSupported = !process.env.NO_COLOR && Boolean(process.env.FORCE_COLOR || process.stdout.isTTY); - -export default createColors(colorsSupported); diff --git a/src/concurrency.test.ts b/src/concurrency.test.ts deleted file mode 100644 index 03acbad..0000000 --- a/src/concurrency.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it } from 'vitest'; -import { mapWithConcurrency } from './concurrency.ts'; - -function trackingMapper(limitObserver: { inFlight: number; maxInFlight: number }) { - return async (item: number) => { - limitObserver.inFlight++; - limitObserver.maxInFlight = Math.max(limitObserver.maxInFlight, limitObserver.inFlight); - await new Promise(resolve => setImmediate(resolve)); - limitObserver.inFlight--; - return item * 2; - }; -} - -describe('mapWithConcurrency', () => { - it('should never exceed the given limit', async () => { - const observer = { inFlight: 0, maxInFlight: 0 }; - - await mapWithConcurrency( - Array.from({ length: 100 }, (_, i) => i), - 4, - trackingMapper(observer) - ); - - expect(observer.maxInFlight).toBe(4); - }); - - it('should return results in input order', async () => { - const items = [5, 1, 4, 2, 3]; - - const results = await mapWithConcurrency(items, 2, async item => { - await new Promise(resolve => setTimeout(resolve, item)); - return item * 10; - }); - - expect(results).toEqual([50, 10, 40, 20, 30]); - }); - - it('should handle fewer items than the limit', async () => { - const observer = { inFlight: 0, maxInFlight: 0 }; - - const results = await mapWithConcurrency([1, 2], 16, trackingMapper(observer)); - - expect(results).toEqual([2, 4]); - expect(observer.maxInFlight).toBe(2); - }); - - it('should handle an empty input', async () => { - expect(await mapWithConcurrency([], 4, async () => 1)).toEqual([]); - }); - - it('should reject when the mapper fails', async () => { - await expect( - mapWithConcurrency([1, 2, 3], 2, async item => { - if (item === 2) { - throw new Error('boom'); - } - return item; - }) - ).rejects.toThrow('boom'); - }); -}); diff --git a/src/concurrency.ts b/src/concurrency.ts deleted file mode 100644 index cfb42e0..0000000 --- a/src/concurrency.ts +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -// Applies mapper to every item while keeping at most `limit` calls in flight, returning -// the results in input order. Rejects like Promise.all does, on the first failure. -export async function mapWithConcurrency( - items: readonly T[], - limit: number, - mapper: (item: T) => Promise -): Promise { - const results = new Array(items.length); - let nextIndex = 0; - - async function worker(): Promise { - for (let index = nextIndex++; index < items.length; index = nextIndex++) { - results[index] = await mapper(items[index]); - } - } - - const workerCount = Math.min(Math.max(1, limit), items.length); - await Promise.all(Array.from({ length: workerCount }, worker)); - return results; -} diff --git a/src/config/index.ts b/src/config/index.ts deleted file mode 100644 index b5f711b..0000000 --- a/src/config/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH -import { getActiveProfile } from './profile/service.ts'; -import type { Configuration } from './types.ts'; - -export const defaultBaseUrl = 'https://platform.steadybit.com'; - -export async function getConfiguration(): Promise { - let apiAccessToken: string | undefined; - let baseUrl = defaultBaseUrl; - - const profile = await getActiveProfile(); - if (profile) { - apiAccessToken = profile.apiAccessToken; - baseUrl = profile.baseUrl ?? baseUrl; - } - - // Environment arguments take precedence over the global system configuration. - apiAccessToken = process.env.STEADYBIT_TOKEN ?? apiAccessToken; - baseUrl = process.env.STEADYBIT_URL ?? baseUrl; - - // A typically error case is that the baseUrl carries a trailing slash. This is fine - // in our persisted config files, but we don't want our CLI to internally work with those. - if (baseUrl.endsWith('/')) { - baseUrl = baseUrl.substring(0, baseUrl.length - 1); - } - - return { - apiAccessToken, - baseUrl, - }; -} diff --git a/src/config/profile/add.test.ts b/src/config/profile/add.test.ts deleted file mode 100644 index 6cbec68..0000000 --- a/src/config/profile/add.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import os from 'node:os'; -import path from 'node:path'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { wrapPrompt } from '@inquirer/testing/vitest'; -import { answerPrompt, waitForPrompt } from '../../mocks/prompts.ts'; -import { add } from './add.ts'; -import type { Profile } from './types.ts'; - -vi.mock('@inquirer/input', async importOriginal => { - const actual = await importOriginal(); - return { ...actual, default: wrapPrompt(actual.default) }; -}); -vi.mock('@inquirer/password', async importOriginal => { - const actual = await importOriginal(); - return { ...actual, default: wrapPrompt(actual.default) }; -}); - -// The flow writes a real profile store, so HOME points at a scratch directory. -const fakeHome = await fs.mkdtemp(path.join(os.tmpdir(), 'steadybit-add-test-')); -process.env.HOME = fakeHome; -if (!os.homedir().startsWith(fakeHome)) { - throw new Error(`refusing to run: home directory is ${os.homedir()}, not the scratch directory`); -} - -async function storedProfiles(): Promise { - return JSON.parse(await fs.readFile(path.join(fakeHome, '.steadybit', 'profiles.json'), 'utf8')); -} - -describe('config profile add', () => { - beforeEach(() => { - // Otherwise the flow wipes the test runner's output. - vi.spyOn(console, 'clear').mockImplementation(() => undefined); - vi.spyOn(console, 'log').mockImplementation(() => undefined); - }); - - it('should ask for a name, a base url and a token, and store them', async () => { - const done = add({} as never); - - await answerPrompt('Profile name:', 'from-the-prompt'); - await answerPrompt('Base URL of the Steadybit server:', 'https://platform.example.com'); - await answerPrompt('API access token:', 's3cr3t'); - await done; - - expect(await storedProfiles()).toContainEqual({ - name: 'from-the-prompt', - baseUrl: 'https://platform.example.com', - apiAccessToken: 's3cr3t', - }); - }); - - it('should fall back to the public platform when the base url is left empty', async () => { - const done = add({} as never); - - await answerPrompt('Profile name:', 'defaulted'); - await answerPrompt('Base URL of the Steadybit server:', ''); // accept the offered default - await answerPrompt('API access token:', 'tok'); - await done; - - expect((await storedProfiles()).find(p => p.name === 'defaulted')?.baseUrl).toEqual( - 'https://platform.steadybit.com' - ); - }); - - it('should refuse a blank name and ask again', async () => { - const done = add({} as never); - - await answerPrompt('Profile name:', ' '); - await waitForPrompt('You must provide a valid value'); - - await answerPrompt('Profile name:', 'eventually-valid', { replace: true }); - await answerPrompt('Base URL of the Steadybit server:', ''); - await answerPrompt('API access token:', 'tok'); - await done; - - expect((await storedProfiles()).map(p => p.name)).toContain('eventually-valid'); - }); - - it('should refuse a base url that is not http', async () => { - const done = add({} as never); - - await answerPrompt('Profile name:', 'bad-url'); - await answerPrompt('Base URL of the Steadybit server:', 'ftp://files.example.com'); - await waitForPrompt('Unsupported protocol ftp:'); - - await answerPrompt('Base URL of the Steadybit server:', 'https://platform.example.com', { replace: true }); - await answerPrompt('API access token:', 'tok'); - await done; - - expect((await storedProfiles()).map(p => p.name)).toContain('bad-url'); - }); - - it('should skip the questions entirely when name and token are given', async () => { - await add({ name: 'non-interactive', token: 'tok', baseUrl: 'https://given.example.com' }); - - expect(await storedProfiles()).toContainEqual({ - name: 'non-interactive', - baseUrl: 'https://given.example.com', - apiAccessToken: 'tok', - }); - }); -}); diff --git a/src/config/profile/add.ts b/src/config/profile/add.ts deleted file mode 100644 index 5fae343..0000000 --- a/src/config/profile/add.ts +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH -import colors from '../../colors.ts'; -import input from '@inquirer/input'; -import password from '@inquirer/password'; - -import { cancelable } from '../../prompt/cancellation.ts'; -import { validateNotBlank, validateHttpUrl } from '../../prompt/validation.ts'; -import { addProfile } from './service.ts'; -import { defaultBaseUrl } from '../index.ts'; -import type { Profile } from './types.ts'; - -const startHelp = ` -Configuration profiles enable you to use the CLI without repeatedly providing -passwords or having to remember environment variables. Configuration profiles -are stored in ~/.steadybit -`.trim(); - -const finishHelp = ` -${colors.green('Done!')} You can now start using the CLI. For example, you could start -to run your first experiment via: - - ${colors.bold('steadybit experiment run -k ')} -`.trim(); - -interface Options { - name: string; - baseUrl?: string; - token: string; -} - -export async function add(options: Options): Promise { - const profile: Profile = - options?.name && options?.token - ? { name: options.name, baseUrl: options.baseUrl, apiAccessToken: options.token } - : await ask(); - await addProfile(profile); - - console.log(); - console.log(finishHelp); -} - -async function ask(): Promise { - console.clear(); - console.log(startHelp); - console.log(); - - const name = await cancelable( - input({ - message: 'Profile name:', - validate: validateNotBlank, - }) - ); - - const baseUrl = await cancelable( - input({ - message: 'Base URL of the Steadybit server:', - default: defaultBaseUrl, - validate: validateHttpUrl, - }) - ); - - console.log(` -The CLI will need an API access token of ${colors.bold('type team')} to communicate with -the Steadybit servers. You can generate one through the following URL: - - ${baseUrl.replace(/\/$/, '')}/settings/api-tokens -`); - - const apiAccessToken = await cancelable( - password({ - message: 'API access token:', - validate: validateNotBlank, - }) - ); - - return { name, baseUrl, apiAccessToken }; -} diff --git a/src/config/profile/list.ts b/src/config/profile/list.ts deleted file mode 100644 index c8bdf65..0000000 --- a/src/config/profile/list.ts +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import colors from '../../colors.ts'; - -import { getActiveProfile, getProfiles } from './service.ts'; - -export async function list(): Promise { - const profiles = await getProfiles(); - const activeProfile = await getActiveProfile(); - - profiles - .slice() - .sort((a, b) => a.name.localeCompare(b.name)) - .forEach(p => { - const isActive = p.name === activeProfile?.name; - - if (isActive) { - console.log('* %s', colors.green(p.name)); - } else { - console.log(' %s', p.name); - } - }); -} diff --git a/src/config/profile/remove.ts b/src/config/profile/remove.ts deleted file mode 100644 index 4704f92..0000000 --- a/src/config/profile/remove.ts +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { promptProfileSelection } from './select.ts'; -import { removeProfile } from './service.ts'; - -export async function remove(): Promise { - const profileName = await promptProfileSelection('Choose profile to delete:'); - await removeProfile(profileName); -} diff --git a/src/config/profile/select.test.ts b/src/config/profile/select.test.ts deleted file mode 100644 index 63e99a5..0000000 --- a/src/config/profile/select.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import os from 'node:os'; -import path from 'node:path'; -import { beforeAll, describe, expect, it, vi } from 'vitest'; -import { wrapPrompt } from '@inquirer/testing/vitest'; -import { answerPrompt, waitForPrompt } from '../../mocks/prompts.ts'; -import { addProfile } from './service.ts'; -import { select } from './select.ts'; - -vi.mock('@inquirer/select', async importOriginal => { - const actual = await importOriginal(); - return { ...actual, default: wrapPrompt(actual.default) }; -}); - -const fakeHome = await fs.mkdtemp(path.join(os.tmpdir(), 'steadybit-select-test-')); -process.env.HOME = fakeHome; -if (!os.homedir().startsWith(fakeHome)) { - throw new Error(`refusing to run: home directory is ${os.homedir()}, not the scratch directory`); -} - -const activeProfileFile = path.join(fakeHome, '.steadybit', 'activeProfile'); - -describe('config profile select', () => { - beforeAll(async () => { - await addProfile({ name: 'alpha', apiAccessToken: 'a' }); - await addProfile({ name: 'beta', apiAccessToken: 'b' }); - }); - - it('should offer every configured profile', async () => { - const done = select(); - - await waitForPrompt('Choose the new active profile:'); - await waitForPrompt('alpha'); - await waitForPrompt('beta'); - - await answerPrompt('Choose the new active profile:', ''); - await done; - }); - - it('should make the chosen profile the active one', async () => { - const done = select(); - - await waitForPrompt('Choose the new active profile:'); - // Down to the second entry, then accept. - await answerPrompt('Choose the new active profile:', '\x1b[B'); - await done; - - expect(await fs.readFile(activeProfileFile, 'utf8')).toEqual('beta'); - }); -}); diff --git a/src/config/profile/select.ts b/src/config/profile/select.ts deleted file mode 100644 index 2b6109f..0000000 --- a/src/config/profile/select.ts +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import selectPrompt from '@inquirer/select'; -import { cancelable } from '../../prompt/cancellation.ts'; - -import { setActiveProfile, getProfiles } from './service.ts'; -import { abortExecution } from '../../errors.ts'; - -export async function select(): Promise { - const activeProfileName = await promptProfileSelection('Choose the new active profile:'); - await setActiveProfile(activeProfileName); -} - -export async function promptProfileSelection(message: string): Promise { - const profiles = await getProfiles(); - if (profiles.length === 0) { - throw abortExecution('No profiles configured.'); - } - - return await cancelable( - selectPrompt({ - message, - choices: profiles.map(p => p.name), - }) - ); -} diff --git a/src/config/profile/service.test.ts b/src/config/profile/service.test.ts deleted file mode 100644 index a671458..0000000 --- a/src/config/profile/service.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import os from 'node:os'; -import path from 'node:path'; -import { beforeAll, describe, expect, it, vi } from 'vitest'; - -import { addProfile, getActiveProfile, getProfiles, setActiveProfile } from './service.ts'; - -// These tests write profile files, so HOME is redirected to a scratch directory. The -// guard refuses to run the suite if that ever stops working. A plain import is enough -// because service.ts resolves the config directory per call rather than at module load. -const fakeHome = await fs.mkdtemp(path.join(os.tmpdir(), 'steadybit-service-test-')); -process.env.HOME = fakeHome; -if (!os.homedir().startsWith(fakeHome)) { - throw new Error(`refusing to run: home directory is ${os.homedir()}, not the scratch directory`); -} - -const profilesFile = path.join(fakeHome, '.steadybit', 'profiles.json'); - -describe('profile service', () => { - beforeAll(async () => { - await addProfile({ name: 'first', baseUrl: 'https://one.example.com', apiAccessToken: 'a' }); - await addProfile({ name: 'second', baseUrl: 'https://two.example.com', apiAccessToken: 'b' }); - }); - - it('should read the profiles file only once across repeated calls', async () => { - const readSpy = vi.spyOn(fs, 'readFile'); - - await getProfiles(); - await getProfiles(); - await getProfiles(); - - const profileReads = readSpy.mock.calls.filter(([file]) => String(file) === profilesFile); - expect(profileReads).toHaveLength(1); - readSpy.mockRestore(); - }); - - it('should resolve the active profile', async () => { - await setActiveProfile('second'); - - expect((await getActiveProfile())?.name).toBe('second'); - }); - - it('should not serve a stale active profile after it changes', async () => { - await setActiveProfile('second'); - expect((await getActiveProfile())?.name).toBe('second'); - - await setActiveProfile('first'); - - expect((await getActiveProfile())?.name).toBe('first'); - }); - - it('should not serve stale profiles after one is added', async () => { - expect((await getProfiles()).map(p => p.name)).not.toContain('third'); - - await addProfile({ name: 'third', baseUrl: 'https://three.example.com', apiAccessToken: 'c' }); - - expect((await getProfiles()).map(p => p.name)).toContain('third'); - }); - - // The config directory used to be computed at module load, which meant nothing could - // point the CLI at a different home once this module had been imported. - it('should follow a home directory that changes after import', async () => { - const otherHome = await fs.mkdtemp(path.join(os.tmpdir(), 'steadybit-other-home-')); - const previous = process.env.HOME; - process.env.HOME = otherHome; - - try { - await setActiveProfile('written-elsewhere'); - expect(await fs.readFile(path.join(otherHome, '.steadybit', 'activeProfile'), 'utf8')).toEqual( - 'written-elsewhere' - ); - } finally { - process.env.HOME = previous; - } - }); - - // The directory memo followed HOME while the read memo did not, so a changed home - // produced correct directories with the previous home's contents in them. - it('should read from a home directory that changes after import', async () => { - const otherHome = await fs.mkdtemp(path.join(os.tmpdir(), 'steadybit-other-home-')); - await fs.mkdir(path.join(otherHome, '.steadybit'), { recursive: true }); - await fs.writeFile( - path.join(otherHome, '.steadybit', 'profiles.json'), - JSON.stringify([{ name: 'only-over-here', apiAccessToken: 'z' }]) - ); - - const previous = process.env.HOME; - process.env.HOME = otherHome; - try { - expect((await getProfiles()).map(p => p.name)).toEqual(['only-over-here']); - } finally { - process.env.HOME = previous; - } - - // ...and the original home is still answered correctly afterwards. - expect((await getProfiles()).map(p => p.name)).toContain('first'); - }); -}); diff --git a/src/config/profile/service.ts b/src/config/profile/service.ts deleted file mode 100644 index 346c578..0000000 --- a/src/config/profile/service.ts +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { homedir } from 'node:os'; -import fs from 'node:fs/promises'; -import path from 'node:path'; - -import { abortExecution, errorMessage } from '../../errors.ts'; -import type { Profile } from './types.ts'; - -// Resolved per call rather than at module load. HOME is what decides where the profile -// store lives, and a test — or anything else setting it after this module is imported — -// would otherwise be talking to the developer's real ~/.steadybit. -const configDir = () => path.join(homedir(), '.steadybit'); -const profilesFile = () => path.join(configDir(), 'profiles.json'); -const activeProfileFile = () => path.join(configDir(), 'activeProfile'); - -// The profile files are read on every API call, three times per call via -// getConfiguration(). Doing the work once turns thousands of redundant syscalls into a -// handful during commands like `experiment dump`. -// -// Remembered against the config directory rather than outright, because that directory -// follows HOME: a single cached value would answer for whichever home happened to be -// current first, and for the directory-creation entry that meant leaving a later -// directory unmade while every write into it failed. Failures are not remembered, so an -// unreadable or unwritable home stays retryable, and the writers below forget the entry -// for the home they wrote to. -function oncePerConfigDirectory(work: () => Promise): (() => Promise) & { forget: () => void } { - const done = new Map>(); - const runOnce = () => { - const directory = configDir(); - let result = done.get(directory); - if (!result) { - result = work().catch(e => { - done.delete(directory); - throw e; - }); - done.set(directory, result); - } - return result; - }; - runOnce.forget = () => { - done.delete(configDir()); - }; - return runOnce; -} - -const ensureConfigDirectoryExists = oncePerConfigDirectory(async () => { - await fs.mkdir(configDir(), { recursive: true }); -}); - -export async function addProfile(profile: Profile): Promise { - const profiles = await getProfiles(); - - const updatedProfiles = profiles.filter(p => p.name !== profile.name).concat(profile); - - await writeProfiles(updatedProfiles); -} - -export async function removeProfile(profileName: string): Promise { - const profiles = await getProfiles(); - - const updatedProfiles = profiles.filter(p => p.name !== profileName); - - await writeProfiles(updatedProfiles); -} - -const readProfiles = oncePerConfigDirectory(async (): Promise => { - await ensureConfigDirectoryExists(); - - let fileContent: string; - try { - fileContent = await fs.readFile(profilesFile(), { encoding: 'utf8' }); - } catch (e) { - if ((e as any)?.code === 'ENOENT') { - return []; - } - - throw abortExecution("Failed to read file '%s': %s", profilesFile(), errorMessage(e)); - } - - try { - return JSON.parse(fileContent); - } catch (e) { - throw abortExecution("Failed to parse file '%s' as JSON: %s", profilesFile(), errorMessage(e)); - } -}); - -export function getProfiles(): Promise { - return readProfiles(); -} - -async function writeProfiles(profiles: Profile[]): Promise { - await ensureConfigDirectoryExists(); - - try { - await fs.writeFile(profilesFile(), JSON.stringify(profiles, undefined, 2)); - } catch (e) { - throw abortExecution("Failed to write to file '%s': %s", profilesFile(), errorMessage(e)); - } - readProfiles.forget(); -} - -const readActiveProfileName = oncePerConfigDirectory(async (): Promise => { - await ensureConfigDirectoryExists(); - - try { - // Users opening and saving the file might end up adding a trailing new line character. - return (await fs.readFile(activeProfileFile(), { encoding: 'utf8' })).trim(); - } catch (e) { - if ((e as any)?.code !== 'ENOENT') { - throw abortExecution("Failed to read file '%s': %s", activeProfileFile(), errorMessage(e)); - } - return undefined; - } -}); - -export async function getActiveProfile(): Promise { - const activeProfileName = await readActiveProfileName(); - const profiles = await getProfiles(); - const activeProfile: Profile | undefined = profiles.find(p => p.name === activeProfileName) ?? profiles[0]; - return activeProfile; -} - -export async function setActiveProfile(profileName: string): Promise { - await ensureConfigDirectoryExists(); - - try { - await fs.writeFile(activeProfileFile(), profileName); - } catch (e) { - throw abortExecution("Failed to write to file '%s': %s", activeProfileFile(), errorMessage(e)); - } - readActiveProfileName.forget(); -} diff --git a/src/config/profile/types.ts b/src/config/profile/types.ts deleted file mode 100644 index 0cdc4c4..0000000 --- a/src/config/profile/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -export interface Profile { - name: string; - apiAccessToken: string; - baseUrl?: string; -} diff --git a/src/config/requirePlatformAccess.ts b/src/config/requirePlatformAccess.ts deleted file mode 100644 index 35d8adc..0000000 --- a/src/config/requirePlatformAccess.ts +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import colors from '../colors.ts'; - -import { abortExecutionWithOpts } from '../errors.ts'; -import { getConfiguration } from './index.ts'; - -const platformAccessConfigurationMissingHelp = ` -No API access token configuration was found for Steadybit platform access. -You can configure API access tokens through configuration profiles or -environment variables (${colors.bold('STEADYBIT_TOKEN')}). We recommend configuration profiles -for local CLI usage. You can add a configuration profile via - - ${colors.bold('steadybit config profile add')} -`.trim(); - -export async function ensurePlatformAccessConfigurationIsAvailable() { - const config = await getConfiguration(); - - if (!config.apiAccessToken) { - throw abortExecutionWithOpts({ colorize: false }, platformAccessConfigurationMissingHelp); - } -} diff --git a/src/config/show.ts b/src/config/show.ts deleted file mode 100644 index d15129d..0000000 --- a/src/config/show.ts +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { dump } from '../yaml.ts'; - -import { getConfiguration } from './index.ts'; - -export async function show(): Promise { - const configuration = await getConfiguration(); - console.log(dump(configuration)); -} diff --git a/src/config/types.ts b/src/config/types.ts deleted file mode 100644 index 37e3471..0000000 --- a/src/config/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -export interface Configuration { - apiAccessToken: string | undefined; - baseUrl: string; -} diff --git a/src/errors.test.ts b/src/errors.test.ts deleted file mode 100644 index fee35a7..0000000 --- a/src/errors.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it } from 'vitest'; -import { executeApiCall } from './api/http.ts'; -import { getExecutionErrorBody } from './errors.ts'; - -interface ExecutionProblem { - type: string; -} - -describe('errors', () => { - describe('getExecutionErrorBody', () => { - // executeApiCall consumes the response body to build its error message, so the body - // has to be carried on the error itself for this to return anything at all. - it('should expose the problem body of a failed API call', async () => { - const error = await executeApiCall({ method: 'GET', path: '/api/problem' }).catch(e => e); - - expect(getExecutionErrorBody(error)).toEqual({ - type: 'https://steadybit.com/problems/another-experiment-running-exception', - }); - }); - - it('should return undefined when there is no body to parse', async () => { - expect(getExecutionErrorBody(new Error('boom'))).toBeUndefined(); - }); - }); -}); diff --git a/src/errors.ts b/src/errors.ts deleted file mode 100644 index 86b4ae7..0000000 --- a/src/errors.ts +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { ApiError } from './api/error.ts'; -import colors from './colors.ts'; -import { format } from 'node:util'; - -export interface AbortExecutionOptions { - colorize?: boolean; -} - -// Reaching for `(e as Error)?.message` at each catch site had already let two different -// fallback strings drift apart, so the idiom lives here instead. -export function errorMessage(e: unknown): string { - return (e as Error)?.message || 'Unknown error'; -} - -export function abortExecution(msg: string, ...args: unknown[]): Error { - return abortExecutionWithOpts(undefined, msg, ...args); -} - -export function abortExecutionWithOpts( - { colorize = true }: AbortExecutionOptions = {}, - msg: string, - ...args: unknown[] -): Error { - // Make unit-testing easier by only aborting the process outside of the test runner, - // which sets NODE_ENV to 'test'. - if (process.env.NODE_ENV !== 'test') { - if (colorize) { - msg = colors.red(msg); - } - console.error(msg, ...args); - process.exit(1); - } - - return new Error(format(msg, ...args)); -} - -export function abortExecutionWithError(error: unknown, msg: string, ...args: unknown[]): Error { - let message = errorMessage(error); - - const errorBody = getExecutionErrorBody(error); - if (errorBody) { - message = `${message}: ${JSON.stringify(errorBody, undefined, 2)}`; - } - - return abortExecution(`${msg}: %s`, ...args, message); -} - -export function getExecutionErrorBody(error: unknown): T | undefined { - return error instanceof ApiError ? error.problemBody() : undefined; -} diff --git a/src/execution/api.ts b/src/execution/api.ts deleted file mode 100644 index 85e4bf4..0000000 --- a/src/execution/api.ts +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { Schemas } from '../api/schemas.ts'; -import { ApiError } from '../api/error.ts'; -import { executeApiCall } from '../api/http.ts'; -import { abortExecution, abortExecutionWithError } from '../errors.ts'; - -export type Execution = Schemas['ExperimentExecutionAO']; - -function notFoundOr(e: unknown, id: number, msg: string): Error { - if (e instanceof ApiError && e.status === 404) { - return abortExecution('Experiment run %s not found.', id); - } - return abortExecutionWithError(e, msg, id); -} - -// Steps, and with them the target executions and their artifacts, are only included -// when asked for. Without them a run looks the same as one that attached nothing. -export async function fetchExecution(id: number): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: `/api/experiments/executions/${id}`, - queryParameters: { fields: 'steps' }, - }); - return (await response.json()) as Execution; - } catch (e) { - throw notFoundOr(e, id, 'Failed to get experiment run %s'); - } -} - -// The platform accepts a cancel with 202 and hands it on to the agents, so the run is -// still stopping when this returns. A 200 means there was nothing left to cancel. -export async function cancelExecution(id: number): Promise<'accepted' | 'already-ended'> { - try { - const response = await executeApiCall({ method: 'POST', path: `/api/experiments/executions/${id}/cancel` }); - return response.status === 202 ? 'accepted' : 'already-ended'; - } catch (e) { - throw notFoundOr(e, id, 'Failed to cancel experiment run %s'); - } -} - -export type PropertyOperation = 'set' | 'add'; - -export async function changeExecutionProperty( - id: number, - key: string, - operation: PropertyOperation, - value: unknown -): Promise { - try { - await executeApiCall({ - method: 'POST', - path: `/api/experiments/executions/${id}/properties/${encodeURIComponent(key)}/${operation}`, - body: value, - }); - } catch (e) { - throw notFoundOr(e, id, `Failed to ${operation} property ${key} of experiment run %s`); - } -} - -export async function downloadArtifact(id: number, targetExecutionId: string, artifactId: string): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: `/api/experiments/executions/${id}/artifacts/${encodeURIComponent(targetExecutionId)}/${encodeURIComponent(artifactId)}`, - // Artifacts are reports and log archives, which can take far longer than an API - // response to arrive. - timeout: 300000, - }); - return Buffer.from(await response.arrayBuffer()); - } catch (e) { - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Artifact %s of experiment run %s not found.', artifactId, id); - } - throw abortExecutionWithError(e, `Failed to download artifact ${artifactId} of experiment run %s`, id); - } -} diff --git a/src/execution/artifacts.ts b/src/execution/artifacts.ts deleted file mode 100644 index c848d32..0000000 --- a/src/execution/artifacts.ts +++ /dev/null @@ -1,114 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import path from 'node:path'; -import { createTable } from '../table.ts'; -import type { Schemas } from '../api/schemas.ts'; -import { abortExecution } from '../errors.ts'; -import { downloadArtifact, type Execution, fetchExecution } from './api.ts'; - -export interface Artifact { - step: string; - target: string; - targetExecutionId: string; - artifactId: string; -} - -type TargetExecution = Schemas['TargetExecutionAO']; - -// Artifacts hang off the target executions of action steps, and of the actions a -// service validation step runs on the step's behalf. The platform offers no listing of -// its own, so they are collected from the run. -export function collectArtifacts(execution: Execution): Artifact[] { - const artifacts: Artifact[] = []; - const addFrom = (step: string, targetExecutions: TargetExecution[] | undefined) => { - for (const target of targetExecutions ?? []) { - for (const artifactId of target.artifacts ?? []) { - artifacts.push({ step, target: target.name ?? '', targetExecutionId: target.id ?? '', artifactId }); - } - } - }; - - for (const step of execution.steps ?? []) { - const label = step.customLabel || ('actionId' in step && step.actionId) || step.stepType; - if ('targetExecutions' in step) { - addFrom(label, step.targetExecutions); - } - if ('validations' in step) { - for (const validation of step.validations ?? []) { - addFrom(validation.customLabel || validation.actionId || label, validation.targetExecutions); - } - } - } - return artifacts; -} - -export interface ListOptions { - id: number; -} - -export async function listArtifacts(options: ListOptions) { - const artifacts = collectArtifacts(await fetchExecution(options.id)); - if (artifacts.length === 0) { - console.log('Experiment run %s has no artifacts.', options.id); - return; - } - const table = createTable({ - columns: [ - { name: 'artifactId', title: 'Artifact', alignment: 'left' }, - { name: 'target', title: 'Target', alignment: 'left' }, - { name: 'step', title: 'Step', alignment: 'left' }, - { name: 'targetExecutionId', title: 'Target execution', alignment: 'left' }, - ], - }); - table.addRows(artifacts); - table.printTable(); -} - -// The ids come from the platform, and are reduced to a single path segment so that one -// containing "../" cannot write outside the chosen directory. basename alone is not -// enough: it leaves ".." as it is. -export function pathSegment(id: string): string { - const segment = path.basename(id); - return segment === '' || segment === '.' || segment === '..' ? '_' : segment; -} - -export interface DownloadOptions { - id: number; - artifact?: string; - targetExecution?: string; - directory: string; - output?: string; -} - -// Every artifact lands in //. Two targets of the -// same step typically produce artifacts of the same name, so the name alone would let -// one download overwrite another. -export async function downloadArtifacts(options: DownloadOptions) { - const selected = collectArtifacts(await fetchExecution(options.id)).filter( - a => - (!options.artifact || a.artifactId === options.artifact) && - (!options.targetExecution || a.targetExecutionId === options.targetExecution) - ); - - if (selected.length === 0) { - throw abortExecution('No matching artifacts found in experiment run %s.', options.id); - } - if (options.output && selected.length > 1) { - throw abortExecution( - '%d artifacts match, but --output takes exactly one. Narrow it down with --artifact and --target-execution.', - selected.length - ); - } - - for (const artifact of selected) { - const file = - options.output ?? - path.join(options.directory, pathSegment(artifact.targetExecutionId), pathSegment(artifact.artifactId)); - const content = await downloadArtifact(options.id, artifact.targetExecutionId, artifact.artifactId); - await fs.mkdir(path.dirname(file), { recursive: true }); - await fs.writeFile(file, content); - console.log('Artifact %s written to %s.', artifact.artifactId, file); - } -} diff --git a/src/execution/cancel.ts b/src/execution/cancel.ts deleted file mode 100644 index bb8819f..0000000 --- a/src/execution/cancel.ts +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { cancelExecution } from './api.ts'; - -export interface Options { - id: number; -} - -export async function cancel(options: Options) { - const outcome = await cancelExecution(options.id); - if (outcome === 'accepted') { - console.log('Experiment run %s is being canceled.', options.id); - } else { - console.log('Experiment run %s has already ended.', options.id); - } -} diff --git a/src/execution/execution.test.ts b/src/execution/execution.test.ts deleted file mode 100644 index a26aa5d..0000000 --- a/src/execution/execution.test.ts +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import path from 'node:path'; -import { describe, expect, it, vi } from 'vitest'; -import { HttpResponse } from 'msw'; -import { respondTo } from '../mocks/recorder.ts'; -import { getTempDir } from '../mocks/tempFiles.ts'; -import { cancel } from './cancel.ts'; -import { addProperty, setProperty } from './property.ts'; -import { collectArtifacts, downloadArtifacts, listArtifacts, pathSegment } from './artifacts.ts'; -import { getExecution } from './get.ts'; -import type { Execution } from './api.ts'; - -const EXECUTION = { - id: 42, - key: 'ADM-1', - state: 'COMPLETED', - steps: [ - { stepType: 'wait', id: 'w' }, - { - stepType: 'action', - actionId: 'com.steadybit.extension_jmeter.run', - targetExecutions: [ - { id: 'te-1', name: 'host-a', artifacts: ['report.zip', 'log.txt'] }, - { id: 'te-2', name: 'host-b', artifacts: ['report.zip'] }, - ], - }, - { - stepType: 'service-validation', - customLabel: 'shop is healthy', - validations: [ - { stepType: 'action', targetExecutions: [{ id: 'te-3', name: 'check', artifacts: ['result.json'] }] }, - ], - }, - ], -} as unknown as Execution; - -describe('execution', () => { - describe('get', () => { - it('prints the run as YAML by default', async () => { - respondTo('get', '/api/experiments/executions/42', () => ({ json: { id: 42, state: 'RUNNING' } })); - const logSpy = vi.spyOn(console, 'log'); - - await getExecution({ id: 42 }); - - expect(logSpy).toHaveBeenCalledWith('id: 42\nstate: RUNNING\n'); - }); - - it('reports a run that does not exist', async () => { - respondTo('get', '/api/experiments/executions/43', () => ({ status: 404 })); - - await expect(getExecution({ id: 43 })).rejects.toThrow('Experiment run 43 not found.'); - }); - - // The platform leaves the steps out unless asked, and with them every artifact. - it('asks the platform to include the steps', async () => { - const requests = respondTo('get', '/api/experiments/executions/42', () => ({ json: { id: 42 } })); - - await getExecution({ id: 42 }); - - expect(requests[0].url.searchParams.get('fields')).toBe('steps'); - }); - }); - - describe('cancel', () => { - it('reports a cancel that was accepted', async () => { - const requests = respondTo('post', '/api/experiments/executions/42/cancel', () => ({ status: 202 })); - const logSpy = vi.spyOn(console, 'log'); - - await cancel({ id: 42 }); - - expect(requests).toHaveLength(1); - expect(logSpy).toHaveBeenCalledWith('Experiment run %s is being canceled.', 42); - }); - - it('reports a run that had already ended', async () => { - respondTo('post', '/api/experiments/executions/42/cancel', () => ({ status: 200 })); - const logSpy = vi.spyOn(console, 'log'); - - await cancel({ id: 42 }); - - expect(logSpy).toHaveBeenCalledWith('Experiment run %s has already ended.', 42); - }); - - it('reports a run that does not exist', async () => { - respondTo('post', '/api/experiments/executions/43/cancel', () => ({ status: 404 })); - - await expect(cancel({ id: 43 })).rejects.toThrow('Experiment run 43 not found.'); - }); - }); - - describe('property', () => { - it('sends a single value as a string, even when it looks like a number', async () => { - const requests = respondTo('post', '/api/experiments/executions/42/properties/ticket/set', () => ({})); - - await setProperty({ id: 42, key: 'ticket', value: ['0042'] }); - - expect(requests[0].body).toBe('0042'); - }); - - it('sends several values as a list', async () => { - const requests = respondTo('post', '/api/experiments/executions/42/properties/tickets/set', () => ({})); - - await setProperty({ id: 42, key: 'tickets', value: ['SHOP-1', 'SHOP-2'] }); - - expect(requests[0].body).toEqual(['SHOP-1', 'SHOP-2']); - }); - - it('parses values as JSON on request', async () => { - const requests = respondTo('post', '/api/experiments/executions/42/properties/score/set', () => ({})); - - await setProperty({ id: 42, key: 'score', value: ['7'], json: true }); - - expect(requests[0].body).toBe(7); - }); - - it.each([ - ['0', 0], - ['false', false], - ['null', null], - ])('sends a falsy JSON value %s rather than dropping it', async (value, expected) => { - const requests = respondTo('post', '/api/experiments/executions/42/properties/score/set', () => ({})); - - await setProperty({ id: 42, key: 'score', value: [value], json: true }); - - expect(requests[0].body).toBe(expected); - }); - - it('sends an empty string', async () => { - const requests = respondTo('post', '/api/experiments/executions/42/properties/note/set', () => ({})); - - await setProperty({ id: 42, key: 'note', value: [''] }); - - expect(requests[0].body).toBe(''); - }); - - it('rejects a value that is not JSON with --json', async () => { - await expect(setProperty({ id: 42, key: 'score', value: ['seven'], json: true })).rejects.toThrow( - "'seven' is not valid JSON" - ); - }); - - it('adds a value to a list property', async () => { - const requests = respondTo('post', '/api/experiments/executions/42/properties/tickets/add', () => ({})); - const logSpy = vi.spyOn(console, 'log'); - - await addProperty({ id: 42, key: 'tickets', value: ['SHOP-3'] }); - - expect(requests[0].body).toBe('SHOP-3'); - expect(logSpy).toHaveBeenCalledWith('Property %s of experiment run %s updated.', 'tickets', 42); - }); - - it('surfaces the platform validation error', async () => { - respondTo('post', '/api/experiments/executions/42/properties/locked/set', () => ({ - status: 422, - json: { title: 'Property locked is not editable in an execution' }, - })); - - await expect(setProperty({ id: 42, key: 'locked', value: ['x'] })).rejects.toThrow( - 'Property locked is not editable in an execution' - ); - }); - }); - - describe('artifacts', () => { - it('collects artifacts of action steps and service validations', () => { - expect(collectArtifacts(EXECUTION)).toEqual([ - { - step: 'com.steadybit.extension_jmeter.run', - target: 'host-a', - targetExecutionId: 'te-1', - artifactId: 'report.zip', - }, - { - step: 'com.steadybit.extension_jmeter.run', - target: 'host-a', - targetExecutionId: 'te-1', - artifactId: 'log.txt', - }, - { - step: 'com.steadybit.extension_jmeter.run', - target: 'host-b', - targetExecutionId: 'te-2', - artifactId: 'report.zip', - }, - { step: 'shop is healthy', target: 'check', targetExecutionId: 'te-3', artifactId: 'result.json' }, - ]); - }); - - it('says so when a run has no artifacts', async () => { - respondTo('get', '/api/experiments/executions/42', () => ({ json: { id: 42, steps: [] } })); - const logSpy = vi.spyOn(console, 'log'); - - await listArtifacts({ id: 42 }); - - expect(logSpy).toHaveBeenCalledWith('Experiment run %s has no artifacts.', 42); - }); - - it('downloads every artifact into a directory per target execution', async () => { - respondTo('get', '/api/experiments/executions/42', () => ({ json: EXECUTION as any })); - respondTo( - 'get', - '/api/experiments/executions/42/artifacts/:target/:artifact', - ({ url }) => new HttpResponse(`content of ${url.pathname.split('/').slice(-2).join('/')}`) - ); - const directory = path.join(getTempDir(), 'artifacts'); - - await downloadArtifacts({ id: 42, directory }); - - await expect(fs.readFile(path.join(directory, 'te-1', 'report.zip'), 'utf8')).resolves.toBe( - 'content of te-1/report.zip' - ); - await expect(fs.readFile(path.join(directory, 'te-2', 'report.zip'), 'utf8')).resolves.toBe( - 'content of te-2/report.zip' - ); - await expect(fs.readFile(path.join(directory, 'te-3', 'result.json'), 'utf8')).resolves.toBe( - 'content of te-3/result.json' - ); - }); - - it('writes a single artifact to --output', async () => { - respondTo('get', '/api/experiments/executions/42', () => ({ json: EXECUTION as any })); - respondTo('get', '/api/experiments/executions/42/artifacts/te-1/log.txt', () => new HttpResponse('the log')); - const output = path.join(getTempDir(), 'my.log'); - - await downloadArtifacts({ id: 42, directory: '.', artifact: 'log.txt', output }); - - await expect(fs.readFile(output, 'utf8')).resolves.toBe('the log'); - }); - - it('refuses --output when several artifacts match', async () => { - respondTo('get', '/api/experiments/executions/42', () => ({ json: EXECUTION as any })); - - await expect( - downloadArtifacts({ id: 42, directory: '.', artifact: 'report.zip', output: 'report.zip' }) - ).rejects.toThrow('2 artifacts match, but --output takes exactly one.'); - }); - - it('keeps downloads inside the directory whatever the artifact is called', async () => { - respondTo('get', '/api/experiments/executions/42', () => ({ - json: { - id: 42, - steps: [{ stepType: 'action', targetExecutions: [{ id: 'te-9', artifacts: ['../../evil'] }] }], - }, - })); - respondTo('get', '/api/experiments/executions/42/artifacts/:target/:artifact', () => new HttpResponse('x')); - const directory = path.join(getTempDir(), 'contained'); - - await downloadArtifacts({ id: 42, directory }); - - await expect(fs.readFile(path.join(directory, 'te-9', 'evil'), 'utf8')).resolves.toBe('x'); - }); - - it('never lets an id step out of a directory', () => { - expect(['..', '.', '', '../..', 'a/../..'].map(pathSegment)).toEqual(['_', '_', '_', '_', '_']); - expect(pathSegment('report.zip')).toBe('report.zip'); - }); - - it('reports when nothing matches', async () => { - respondTo('get', '/api/experiments/executions/42', () => ({ json: EXECUTION as any })); - - await expect(downloadArtifacts({ id: 42, directory: '.', artifact: 'nope' })).rejects.toThrow( - 'No matching artifacts found in experiment run 42.' - ); - }); - }); -}); diff --git a/src/execution/get.ts b/src/execution/get.ts deleted file mode 100644 index 1fde646..0000000 --- a/src/execution/get.ts +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { output } from '../structuredFiles.ts'; -import { fetchExecution } from './api.ts'; - -export interface Options { - id: number; - file?: string; - type?: string; -} - -export async function getExecution(options: Options) { - const execution = await fetchExecution(options.id); - await output(execution, options); - if (options.file) { - console.log('Experiment run %s written to %s.', options.id, options.file); - } -} diff --git a/src/execution/property.ts b/src/execution/property.ts deleted file mode 100644 index 2d383af..0000000 --- a/src/execution/property.ts +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { abortExecution, errorMessage } from '../errors.ts'; -import { changeExecutionProperty, type PropertyOperation } from './api.ts'; - -export interface Options { - id: number; - key: string; - value: string[]; - json?: boolean; -} - -// Values are sent as strings unless --json asks otherwise. Guessing from the text would -// turn a ticket number such as "0042" into the number 42. -function parseValue(value: string, json: boolean | undefined): unknown { - if (!json) { - return value; - } - try { - return JSON.parse(value); - } catch (e) { - throw abortExecution("'%s' is not valid JSON: %s", value, errorMessage(e)); - } -} - -export async function setProperty(options: Options) { - const values = options.value.map(v => parseValue(v, options.json)); - // Several values set a list property; a single one stays a scalar. - await change('set', options, values.length === 1 ? values[0] : values); -} - -export async function addProperty(options: Options) { - if (options.value.length !== 1) { - throw abortExecution('Adding to a list property takes exactly one --value.'); - } - await change('add', options, parseValue(options.value[0], options.json)); -} - -async function change(operation: PropertyOperation, options: Options, value: unknown) { - await changeExecutionProperty(options.id, options.key, operation, value); - console.log('Property %s of experiment run %s updated.', options.key, options.id); -} diff --git a/src/experiment/__snapshots__/get.test.ts.snap b/src/experiment/__snapshots__/get.test.ts.snap deleted file mode 100644 index d494d0a..0000000 --- a/src/experiment/__snapshots__/get.test.ts.snap +++ /dev/null @@ -1,140 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`experiment > get > should output experiment to file 1`] = ` -"key: TST-1 -name: Verify TTR fashion bestseller -team: TST -environment: Global -lanes: - - steps: - - type: action - ignoreFailure: false - parameters: - graceful: 'true' - actionType: container-stop-attack - radius: - targetType: container - predicate: - operator: AND - predicates: - - key: k8s.namespace - operator: EQUALS - values: - - steadybit-demo - - key: k8s.deployment - operator: EQUALS - values: - - fashion-bestseller - query: null - percentage: 50 -" -`; - -exports[`experiment > get > should output experiment to stdout JSON 1`] = ` -"{ - "key": "TST-1", - "name": "Verify TTR fashion bestseller", - "team": "TST", - "environment": "Global", - "lanes": [ - { - "steps": [ - { - "type": "action", - "ignoreFailure": false, - "parameters": { - "graceful": "true" - }, - "actionType": "container-stop-attack", - "radius": { - "targetType": "container", - "predicate": { - "operator": "AND", - "predicates": [ - { - "key": "k8s.namespace", - "operator": "EQUALS", - "values": [ - "steadybit-demo" - ] - }, - { - "key": "k8s.deployment", - "operator": "EQUALS", - "values": [ - "fashion-bestseller" - ] - } - ] - }, - "query": null, - "percentage": 50 - } - } - ] - } - ] -}" -`; - -exports[`experiment > get > should output experiment to stdout YAML 1`] = ` -"key: TST-1 -name: Verify TTR fashion bestseller -team: TST -environment: Global -lanes: - - steps: - - type: action - ignoreFailure: false - parameters: - graceful: 'true' - actionType: container-stop-attack - radius: - targetType: container - predicate: - operator: AND - predicates: - - key: k8s.namespace - operator: EQUALS - values: - - steadybit-demo - - key: k8s.deployment - operator: EQUALS - values: - - fashion-bestseller - query: null - percentage: 50 -" -`; - -exports[`experiment > get > should write experiment to file with same data type JSON 1`] = `"{"key":"TST-1","name":"Verify TTR fashion bestseller","team":"TST","environment":"Global","lanes":[{"steps":[{"type":"action","ignoreFailure":false,"parameters":{"graceful":"true"},"actionType":"container-stop-attack","radius":{"targetType":"container","predicate":{"operator":"AND","predicates":[{"key":"k8s.namespace","operator":"EQUALS","values":["steadybit-demo"]},{"key":"k8s.deployment","operator":"EQUALS","values":["fashion-bestseller"]}]},"query":null,"percentage":50}}]}]}"`; - -exports[`experiment > get > should write experiment to file with same data type YAML 1`] = ` -"key: TST-1 -name: Verify TTR fashion bestseller -team: TST -environment: Global -lanes: - - steps: - - type: action - ignoreFailure: false - parameters: - graceful: 'true' - actionType: container-stop-attack - radius: - targetType: container - predicate: - operator: AND - predicates: - - key: k8s.namespace - operator: EQUALS - values: - - steadybit-demo - - key: k8s.deployment - operator: EQUALS - values: - - fashion-bestseller - query: null - percentage: 50 -" -`; diff --git a/src/experiment/api.ts b/src/experiment/api.ts deleted file mode 100644 index df54af9..0000000 --- a/src/experiment/api.ts +++ /dev/null @@ -1,333 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import type { - ExecuteResult, - ExecutionError, - ExecutionList, - ExecutionResult, - Experiment, - ExperimentList, - UpsertAndExecuteResult, - UpsertResult, -} from './types.ts'; - -import { abortExecution, abortExecutionWithError, getExecutionErrorBody } from '../errors.ts'; -import { ApiError } from '../api/error.ts'; -import { executeApiCall } from '../api/http.ts'; -import { confirm } from '../prompt/confirm.ts'; -import type { Schemas } from '../api/schemas.ts'; - -export async function executeExperiment( - key: string, - yes: boolean, - allowParallelExecutions: boolean = false, - forcePersist: boolean = true -): Promise { - try { - const response = await executeApiCall({ - method: 'POST', - path: `/api/experiments/${encodeURIComponent(key)}/execute?forcePersist=${String(forcePersist)}&allowParallel=${String(allowParallelExecutions)}`, - }); - - let uiLocation = 'please update your platform to get the UI location'; - const body = await response.text(); - if (body && body.length > 0) { - const json = JSON.parse(body); - uiLocation = json.uiLocation; - } - return { location: response.headers.get('Location') ?? '', uiLocation }; - } catch (e) { - if (e instanceof ApiError && e.status === 422) { - throw e; - } - if ( - !allowParallelExecutions && - getExecutionErrorBody(e)?.type === - 'https://steadybit.com/problems/another-experiment-running-exception' && - (yes || - (await confirm(`There is already an experiment running. Do you want to start ${key} in parallel?`, { - defaultYes: false, - defaultWhenNonInteractive: false, - }))) - ) { - // try again, but run in parallel - return executeExperiment(key, yes, true); - } - throw abortExecutionWithError(e, 'Failed to run experiment (%s)', key); - } -} - -export async function upsertAndExecuteExperiment( - experiment: Experiment, - allowParallelExecutions: boolean = false, - forcePersist: boolean = true -): Promise { - try { - const response = await executeApiCall({ - method: 'POST', - path: `/api/experiments/execute?forcePersist=${String(forcePersist)}&allowParallel=${String(allowParallelExecutions)}`, - body: experiment, - }); - - let uiLocation = 'please update your platform to get the UI location'; - let key = experiment.key; - let executionId = undefined; - const body = await response.text(); - if (body && body.length > 0) { - const json = JSON.parse(body); - uiLocation = json.uiLocation; - key = json.key; - executionId = json.executionId; - } - return { - key, - location: response.headers.get('Location') ?? `/api/experiments/executions/${executionId}`, - uiLocation, - }; - } catch (e) { - if (e instanceof ApiError && e.status === 422) { - throw e; - } - if ( - !allowParallelExecutions && - getExecutionErrorBody(e)?.type === - 'https://steadybit.com/problems/another-experiment-running-exception' && - (await confirm( - `There is already an experiment running. Do you want to start ${experiment.key || experiment.name || 'the experiment'} in parallel?`, - { - defaultYes: false, - defaultWhenNonInteractive: false, - } - )) - ) { - // try again, but run in parallel - return upsertAndExecuteExperiment(experiment, true); - } - throw abortExecutionWithError(e, 'Failed to save and run the experiment. HTTP request failed.'); - } -} - -export async function getExperimentExecutionUsingUrl(url: string): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: url, - }); - return (await response.json()) as ExecutionResult; - } catch (e) { - throw abortExecutionWithError(e, 'Failed to get experiment run '); - } -} - -export async function fetchExperiment(key: string, abortOnError = true): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: `/api/experiments/${encodeURIComponent(key)}`, - }); - const experiment = (await response.json()) as Experiment; - delete experiment.version; // We remove the version (as this makes things complicated to use). Will be removed from API in the future. - return experiment; - } catch (e) { - if (!abortOnError) { - throw e; - } - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Experiment %s not found.', key); - } else { - throw abortExecutionWithError(e, 'Failed to get the experiment. HTTP request failed.'); - } - } -} - -export async function updateExperiment(key: string, experiment: Experiment): Promise { - try { - await executeApiCall({ - method: 'POST', - path: `/api/experiments/${encodeURIComponent(key)}`, - body: experiment, - }); - } catch (e) { - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Experiment %s not found.', key); - } else { - throw abortExecutionWithError(e, 'Failed to save the experiment. HTTP request failed.'); - } - } -} - -export async function removeExperiment(key: string): Promise { - try { - await executeApiCall({ - method: 'DELETE', - path: `/api/experiments/${encodeURIComponent(key)}`, - }); - } catch (e) { - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Experiment %s not found.', key); - } else { - throw abortExecutionWithError(e, 'Failed to delete the experiment. HTTP request failed.'); - } - } -} - -export async function upsertExperiment(experiment: Experiment): Promise { - try { - const response = await executeApiCall({ - method: 'POST', - path: '/api/experiments', - body: experiment, - }); - const location = response.headers.get('Location'); - const key = location?.substring(location.lastIndexOf('/') + 1); - return { created: response.status === 201, key }; - } catch (e) { - throw abortExecutionWithError(e, 'Failed to save the experiment. HTTP request failed.'); - } -} - -export async function fetchExperiments(teamKey: string): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: '/api/experiments', - queryParameters: { - team: teamKey, - }, - }); - return (await response.json()) as ExperimentList; - } catch (e) { - throw abortExecutionWithError(e, 'Failed to get the experiments. HTTP request failed.'); - } -} - -export async function fetchExecutionsForExperiment(key: string, abortOnError = true): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: `/api/experiments/${encodeURIComponent(key)}/executions`, - }); - return (await response.json()) as ExecutionList; - } catch (e) { - if (!abortOnError) { - throw e; - } - throw abortExecutionWithError(e, 'Failed to get the executions. HTTP request failed.'); - } -} - -export async function getExperimentExecution(id: number, abortOnError = true): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: `/api/experiments/executions/${id}`, - }); - return (await response.json()) as ExecutionResult; - } catch (e) { - if (abortOnError) { - throw abortExecutionWithError(e, 'Failed to get experiment run '); - } else { - throw e; - } - } -} - -export interface TemplateUsage { - resetProperties: boolean; -} - -// The platform answers a create from a template with the experiment's URL in the -// Location header and no body, the same way a plain upsert does. -export async function createExperimentFromTemplate( - templateId: string, - body: Schemas['CreateExperimentFromTemplateAO'], - { resetProperties }: TemplateUsage -): Promise { - try { - const response = await executeApiCall({ - method: 'POST', - path: `/api/experiments/templates/${encodeURIComponent(templateId)}/experiment-create`, - queryParameters: { resetProperties: String(resetProperties) }, - body, - }); - const location = response.headers.get('Location'); - const key = location?.substring(location.lastIndexOf('/') + 1); - return { created: response.status === 201, key }; - } catch (e) { - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Experiment template %s not found.', templateId); - } - throw abortExecutionWithError(e, 'Failed to create the experiment from template %s', templateId); - } -} - -export async function updateExperimentFromTemplate( - templateId: string, - key: string, - body: Schemas['UpdateExperimentFromTemplateAO'], - { resetProperties }: TemplateUsage -): Promise { - try { - await executeApiCall({ - method: 'POST', - path: `/api/experiments/templates/${encodeURIComponent(templateId)}/experiment-update/${encodeURIComponent(key)}`, - queryParameters: { resetProperties: String(resetProperties) }, - body, - }); - } catch (e) { - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Experiment template %s or experiment %s not found.', templateId, key); - } - throw abortExecutionWithError(e, 'Failed to update experiment %s from template %s', key, templateId); - } -} - -export interface TemplateRun extends TemplateUsage { - yes: boolean; - allowParallel: boolean; - forcePersist: boolean; -} - -// A validation error is rethrown untouched, as for upsertAndExecuteExperiment, so that -// the caller can retry it; another run in progress may be answered with a parallel run. -export async function runExperimentFromTemplate( - templateId: string, - body: Schemas['CreateAndRunExperimentFromTemplateAO'], - run: TemplateRun -): Promise { - try { - const response = await executeApiCall({ - method: 'POST', - path: `/api/experiments/templates/${encodeURIComponent(templateId)}/experiment-execute`, - queryParameters: { - resetProperties: String(run.resetProperties), - allowParallel: String(run.allowParallel), - forcePersist: String(run.forcePersist), - }, - body, - }); - const result = (await response.json()) as Schemas['ExecuteExperimentResponseAO']; - return { key: result.key, location: result.apiLocation, uiLocation: result.uiLocation }; - } catch (e) { - if (e instanceof ApiError && e.status === 422) { - throw e; - } - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Experiment template %s not found.', templateId); - } - if ( - !run.allowParallel && - getExecutionErrorBody(e)?.type === - 'https://steadybit.com/problems/another-experiment-running-exception' && - (run.yes || - (await confirm('There is already an experiment running. Do you want to start this one in parallel?', { - defaultYes: false, - defaultWhenNonInteractive: false, - }))) - ) { - return runExperimentFromTemplate(templateId, body, { ...run, allowParallel: true }); - } - throw abortExecutionWithError(e, 'Failed to run an experiment from template %s', templateId); - } -} diff --git a/src/experiment/apply.test.ts b/src/experiment/apply.test.ts deleted file mode 100644 index 7c14f12..0000000 --- a/src/experiment/apply.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { describe, expect, it, vi } from 'vitest'; -import { EXPERIMENTS } from '../mocks/handlers.ts'; -import { applyExperiments } from './apply.ts'; -import * as fileApi from './files.ts'; -import { getTempDir, writeFile } from '../mocks/tempFiles.ts'; - -describe('experiment', () => { - describe('apply', () => { - it('should update experiment from file', async () => { - const file = await writeFile('experiment.yaml', EXPERIMENTS['TST-1']); - const logSpy = vi.spyOn(console, 'log'); - - await applyExperiments({ file: [file], recursive: false }); - - expect(logSpy).toHaveBeenCalledWith('Experiment %s updated.', 'TST-1'); - }); - - it('should upsert experiment from file', async () => { - const file = await writeFile('experiment.yaml', EXPERIMENTS['NEW']); - const logSpy = vi.spyOn(console, 'log'); - - await applyExperiments({ file: [file], recursive: false }); - - expect(logSpy).toHaveBeenCalledWith('Experiment %s created.', 'NEW-1'); - }); - - it('should upsert experiment from directory', async () => { - await writeFile('experiment-1.yaml', EXPERIMENTS['NEW']); - await writeFile('experiment-2.yaml', EXPERIMENTS['NEW']); - const logSpy = vi.spyOn(console, 'log'); - - await applyExperiments({ file: [getTempDir()], recursive: false }); - - expect(logSpy).toHaveBeenCalledWith('Experiment %s created.', 'NEW-1'); - expect(logSpy).toHaveBeenCalledWith('Experiment %s created.', 'NEW-2'); - }); - - it('should throw when key and two or more files are given', async () => { - await writeFile('experiment-1.yaml', EXPERIMENTS['NEW']); - await writeFile('experiment-2.yaml', EXPERIMENTS['NEW']); - - await expect(applyExperiments({ key: 'TST-1', file: [getTempDir()], recursive: false })).rejects.toThrow( - 'If --key is specified, at most one --file can be specified.' - ); - }); - - it('should keep file data type YML', async () => { - const file = await writeFile('experiment.yaml', { ...EXPERIMENTS['TST-1'], key: undefined }); - const logSpy = vi.spyOn(fileApi, 'writeFile'); - - await applyExperiments({ file: [file], recursive: false }); - - expect(logSpy).toHaveBeenCalledWith(expect.any(String), expect.any(Object), 'yaml'); - }); - - it('should keep file data type JSON', async () => { - const file = await writeFile('experiment.json', { ...EXPERIMENTS['TST-1'], key: undefined }, 'json'); - const logSpy = vi.spyOn(fileApi, 'writeFile'); - - await applyExperiments({ file: [file], recursive: false }); - - expect(logSpy).toHaveBeenCalledWith(expect.any(String), expect.any(Object), 'json'); - }); - }); -}); diff --git a/src/experiment/apply.ts b/src/experiment/apply.ts deleted file mode 100644 index 706301f..0000000 --- a/src/experiment/apply.ts +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { loadExperiment, resolveExperimentFiles, writeFile } from './files.ts'; -import { updateExperiment, upsertExperiment } from './api.ts'; -import { abortExecution } from '../errors.ts'; -import { applyExperimentFromTemplate, type TemplateOptions } from './template.ts'; - -export type Options = { - key?: string; - file?: string[]; - recursive: boolean; -} & Partial; - -export async function applyExperiments(options: Options) { - if (options.template) { - return applyExperimentFromTemplate({ - ...options, - template: options.template, - resetProperties: options.resetProperties ?? true, - }); - } - if (!options.file) { - throw abortExecution('Either --file or --template must be specified.'); - } - - const files = await resolveExperimentFiles(options.file, options.recursive); - - if (options.key && files.length > 1) { - throw abortExecution('If --key is specified, at most one --file can be specified.'); - } - - for (const file of files) { - const { experiment, datatype } = await loadExperiment(file); - const key = options.key || experiment.key; - - console.log('key: ', key, 'file: ', file); - - if (key) { - await updateExperiment(key, experiment); - console.log('Experiment %s updated.', key); - } else { - const result = await upsertExperiment(experiment); - if (result.created) { - await writeFile(file, { key: result.key, ...experiment }, datatype); - console.log('Experiment %s created.', result.key); - } else { - console.log('Experiment %s updated.', result.key); - } - } - } -} diff --git a/src/experiment/delete.test.ts b/src/experiment/delete.test.ts deleted file mode 100644 index 57b25c2..0000000 --- a/src/experiment/delete.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { describe, expect, it, vi } from 'vitest'; -import { deleteExperiment } from './delete.ts'; - -describe('experiment', () => { - describe('delete', () => { - it('should delete an experiment', async () => { - const logSpy = vi.spyOn(console, 'log'); - - await deleteExperiment({ key: 'TST-1' }); - - expect(logSpy).toHaveBeenCalledWith('Experiment %s deleted.', 'TST-1'); - }); - - it('should report not found', async () => { - await expect(deleteExperiment({ key: 'TST-999' })).rejects.toThrow('Experiment TST-999 not found.'); - }); - }); -}); diff --git a/src/experiment/delete.ts b/src/experiment/delete.ts deleted file mode 100644 index c6d12de..0000000 --- a/src/experiment/delete.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { removeExperiment } from './api.ts'; - -export interface Options { - key: string; -} - -export async function deleteExperiment(options: Options) { - await removeExperiment(options.key); - console.log('Experiment %s deleted.', options.key); -} diff --git a/src/experiment/dump.test.ts b/src/experiment/dump.test.ts deleted file mode 100644 index f9906b5..0000000 --- a/src/experiment/dump.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { afterEach, describe, expect, it, vi } from 'vitest'; -import fs from 'node:fs/promises'; -import path from 'node:path'; -import { http, HttpResponse } from 'msw'; -import { server } from '../mocks/server.ts'; -import { givenExecutions } from '../mocks/handlers.ts'; -import { getTempDir } from '../mocks/tempFiles.ts'; -import type { Team } from '../team/types.ts'; -import { dump, selectTeams } from './dump.ts'; - -const teams = [ - { key: 'TST', name: 'Test' }, - { key: 'ADM', name: 'Administrators' }, - { key: 'WEBHOOK', name: 'Webhook' }, -] as Team[]; - -let directoryCount = 0; -function freshDirectory(): string { - return path.join(getTempDir(), `dump-${directoryCount++}`); -} - -describe('selectTeams', () => { - it('should keep every team when none is named', () => { - expect(selectTeams(teams, undefined)).toEqual(teams); - expect(selectTeams(teams, [])).toEqual(teams); - }); - - it('should keep only the named teams', () => { - expect(selectTeams(teams, ['WEBHOOK']).map(t => t.key)).toEqual(['WEBHOOK']); - expect(selectTeams(teams, ['WEBHOOK', 'TST']).map(t => t.key)).toEqual(['TST', 'WEBHOOK']); - }); - - it('should match a key regardless of case', () => { - expect(selectTeams(teams, ['webhook']).map(t => t.key)).toEqual(['WEBHOOK']); - }); - - // Skipping an unknown key would make a dump that covered less than was asked for look - // exactly like one that covered everything. - it('should refuse an unknown key rather than dumping less than asked', () => { - expect(() => selectTeams(teams, ['NOPE'])).toThrow('No accessible team with key NOPE'); - }); - - it('should name the available keys when one is unknown', () => { - expect(() => selectTeams(teams, ['TST', 'NOPE'])).toThrow('Available: ADM, TST, WEBHOOK'); - }); -}); - -describe('experiment dump', () => { - afterEach(() => { - process.exitCode = undefined; - }); - - // The list-gathering phase is paced like everything else, so without this the CLI can - // sit silent for minutes before its first line of output. - it('should report progress while gathering the experiment lists', async () => { - const written: string[] = []; - const stdout = vi.spyOn(process.stdout, 'write').mockImplementation(chunk => { - written.push(String(chunk)); - return true; - }); - - await dump({ directory: freshDirectory() }); - - stdout.mockRestore(); - const output = written.join(''); - expect(output).toContain('Listing experiments for 1 team'); - // The dot lands before the per-team walk starts, not after it. - expect(output.indexOf('.\n')).toBeLessThan(output.indexOf('Fetching experiments for team')); - }); - - it('should write the experiment and its executions', async () => { - givenExecutions('TST-1', [1, 2, 3]); - const directory = freshDirectory(); - - await dump({ directory }); - - expect((await fs.readdir(path.join(directory, 'TST-1'))).sort()).toEqual([ - 'execution-1.yaml', - 'execution-2.yaml', - 'execution-3.yaml', - 'experiment.yaml', - ]); - expect(process.exitCode).toBeUndefined(); - }); - - // The whole point of the change: one bad experiment must not discard the rest. - it('should keep going when an experiment cannot be fetched, and report it', async () => { - givenExecutions('TST-1', [1]); - server.use( - http.get('http://example.com/api/experiments/TST-1', () => - HttpResponse.json({ title: 'Server Error' }, { status: 500 }) - ) - ); - const problems = vi.spyOn(console, 'error').mockImplementation(() => undefined); - const directory = freshDirectory(); - - await dump({ directory }); - - expect(problems.mock.calls.flat().join('\n')).toContain('TST-1'); - expect(problems.mock.calls.flat().join('\n')).toContain('1 experiments and 0 executions could not be dumped'); - problems.mockRestore(); - }); - - it('should exit non-zero when an experiment could not be dumped', async () => { - server.use( - http.get('http://example.com/api/experiments/TST-1', () => - HttpResponse.json({ title: 'Server Error' }, { status: 500 }) - ) - ); - vi.spyOn(console, 'error').mockImplementation(() => undefined); - - await dump({ directory: freshDirectory() }); - - expect(process.exitCode).toEqual(1); - vi.restoreAllMocks(); - }); - - // An execution that could not be fetched is as much a hole in the dump as an - // experiment is, and used to be swallowed without a word or a non-zero status. - it('should count and report executions it could not fetch', async () => { - givenExecutions('TST-1', [1, 2, 3], [2, 3]); - const problems = vi.spyOn(console, 'error').mockImplementation(() => undefined); - const directory = freshDirectory(); - - await dump({ directory }); - - const written = await fs.readdir(path.join(directory, 'TST-1')); - expect(written.sort()).toEqual(['execution-1.yaml', 'experiment.yaml']); - - const reported = problems.mock.calls.flat().join('\n'); - expect(reported).toContain('TST-1: 2 of 3 executions could not be fetched'); - expect(reported).toContain('0 experiments and 2 executions could not be dumped'); - expect(process.exitCode).toEqual(1); - problems.mockRestore(); - }); - - it('should dump only the requested team', async () => { - const directory = freshDirectory(); - - await dump({ directory, team: ['TST'] }); - - expect(await fs.readdir(directory)).toEqual(['TST-1']); - }); -}); diff --git a/src/experiment/dump.ts b/src/experiment/dump.ts deleted file mode 100644 index 9ce49b2..0000000 --- a/src/experiment/dump.ts +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import fs from 'node:fs/promises'; -import { mapWithConcurrency } from '../concurrency.ts'; -import { abortExecution, errorMessage } from '../errors.ts'; -import { rateLimiter } from '../api/rateLimit.ts'; -import { getAllTeams } from '../team/get.ts'; -import type { Team } from '../team/types.ts'; -import { fetchExecutionsForExperiment, fetchExperiment, fetchExperiments, getExperimentExecution } from './api.ts'; -import { type Datatype, writeFile } from './files.ts'; -import type { ExecutionList, ExperimentList } from './types.ts'; - -export interface Options { - directory: string; - type?: Datatype; - team?: string[]; -} - -// A dump walks every experiment of every team and every execution of every experiment. -// Both levels are bounded so the request volume stays predictable instead of scaling -// with tenant size, rather than relying on a connection cap: the global fetch, unlike -// the node-fetch agents it replaced, imposes no per-origin limit of its own. The -// resulting ceiling is in the same range as the 64 sockets that pool used to allow. -const EXPERIMENT_CONCURRENCY = 4; -const EXECUTION_CONCURRENCY = 16; - -const LARGE_DUMP_EXPERIMENTS = 100; - -export async function dump(options: Options) { - await ensureDirectoryExists(options.directory); - let totalExperiments = 0; - let totalExecutions = 0; - let totalFailedExperiments = 0; - let totalFailedExecutions = 0; - - // The experiment lists are fetched up front, which costs nothing extra because each - // team needs one anyway, so that the size of the walk is known before it starts. - // These requests are paced like any other, so on a tenant with many teams, or a - // reduced allowance, they take long enough that silence here reads as a hang. - const teams = selectTeams(await getAllTeams(false), options.team); - process.stdout.write(`Listing experiments for ${teams.length} ${teams.length === 1 ? 'team' : 'teams'}`); - const listPerTeam = new Map(); - for (const team of teams) { - listPerTeam.set(team.key, await fetchExperiments(team.key)); - process.stdout.write('.'); - } - process.stdout.write('\n'); - warnAboutLargeDump([...listPerTeam.values()].reduce((total, list) => total + list.experiments.length, 0)); - - for (const team of teams) { - process.stdout.write(`Fetching experiments for team ${team.name} (${team.key})... `); - const teamDump = await getAllExperimentsForTeam( - listPerTeam.get(team.key)!, - options.directory, - options.type ?? 'yaml' - ); - totalExperiments += teamDump.countExperiments; - totalExecutions += teamDump.countExecutions; - totalFailedExperiments += teamDump.failedExperiments; - totalFailedExecutions += teamDump.failedExecutions; - - const failed = teamDump.failedExperiments + teamDump.failedExecutions; - process.stdout.write( - `experiments: ${teamDump.countExperiments}, executions: ${teamDump.countExecutions}` + - `${failed > 0 ? `, failed: ${failed}` : ''}\n` - ); - // Only once the progress line above is terminated, so that the two streams stay - // readable when they are redirected to different places. - for (const problem of teamDump.problems) { - console.error(` ${problem}`); - } - } - console.log(`Written ${totalExperiments} experiments with ${totalExecutions} executions`); - - if (totalFailedExperiments > 0 || totalFailedExecutions > 0) { - // Everything that could be fetched has been written; the non-zero status is what - // stops a pipeline treating an incomplete dump as a complete one. Executions count - // for this too — a dump missing half its runs is not a complete dump either. - console.error( - `Incomplete: ${totalFailedExperiments} experiments and ${totalFailedExecutions} executions could not be dumped` - ); - process.exitCode = 1; - } -} - -// Keys are matched case-insensitively because that is how they are shown and typed. An -// unknown one aborts rather than being skipped: a dump that quietly covers less than was -// asked for is indistinguishable from one that covered everything. -export function selectTeams(teams: Team[], keys: string[] | undefined): Team[] { - if (!keys || keys.length === 0) { - return teams; - } - - const wanted = new Set(keys.map(key => key.toUpperCase())); - const selected = teams.filter(team => wanted.has(team.key.toUpperCase())); - - const missing = [...wanted].filter(key => !selected.some(team => team.key.toUpperCase() === key)); - if (missing.length > 0) { - throw abortExecution( - 'No accessible team with key %s. Available: %s', - missing.join(', '), - teams - .map(team => team.key) - .sort() - .join(', ') - ); - } - return selected; -} - -// Every experiment costs at least two requests, its design and its execution list, and -// each execution one more. Past the burst the platform meters those out slowly, so a -// large dump is a long job and saying so up front beats discovering it an hour later. -function warnAboutLargeDump(countExperiments: number): void { - if (countExperiments <= LARGE_DUMP_EXPERIMENTS) { - return; - } - const minimumMinutes = Math.ceil(rateLimiter.millisFor(countExperiments * 2) / 60000); - console.error( - `Dumping ${countExperiments} experiments. Requests are paced to the platform's rate limit, ` + - `so this takes at least ${minimumMinutes} minutes, longer with executions.\n` - ); -} - -function removeDeprecatedFields(experiment: Record) { - if (Array.isArray(experiment.lanes)) { - experiment.lanes.forEach(lane => { - if (Array.isArray(lane.steps)) { - lane.steps.forEach((step: Record) => { - if (step && typeof step === 'object' && 'radius' in step) { - delete step.radius.query; - delete step.radius.list; - } - }); - } - }); - } - - return experiment; -} - -export interface TeamDump { - countExperiments: number; - countExecutions: number; - failedExperiments: number; - failedExecutions: number; - // Returned rather than printed, so the caller can finish its progress line first. - // Emitting them as they happen tore the two streams across each other. - problems: string[]; -} - -async function getAllExperimentsForTeam(response: ExperimentList, dir: string, datatype: Datatype): Promise { - // A single unlucky request must not discard the whole walk. Failures are counted and - // reported at both levels instead of ending the command on the spot, and an execution - // that could not be fetched is as much a hole in the dump as an experiment is. - const results = await mapWithConcurrency(response.experiments, EXPERIMENT_CONCURRENCY, async item => { - const subdir = `${dir}/${item.key}`; - try { - // The design, the execution list and the directory are independent of one another, - // so waiting for them in turn would put two extra round trips on the critical path - // of every experiment. - const [, experiment, executions] = await Promise.all([ - ensureDirectoryExists(subdir), - fetchExperiment(item.key, false), - fetchExecutionsForExperiment(item.key, false), - ]); - - await writeFile(`${subdir}/experiment.${datatype}`, removeDeprecatedFields(experiment), datatype); - const { written, failed } = await writeExecutions(executions.executions, subdir, datatype); - return { - countExecutions: written, - failedExperiments: 0, - failedExecutions: failed, - problems: failed > 0 ? [`${item.key}: ${failed} of ${written + failed} executions could not be fetched`] : [], - }; - } catch (e) { - return { - countExecutions: 0, - failedExperiments: 1, - failedExecutions: 0, - problems: [`${item.key}: ${errorMessage(e)}`], - }; - } - }); - - const sum = (pick: (r: (typeof results)[number]) => number) => results.reduce((total, r) => total + pick(r), 0); - const failedExperiments = sum(r => r.failedExperiments); - return { - countExperiments: response.experiments.length - failedExperiments, - countExecutions: sum(r => r.countExecutions), - failedExperiments, - failedExecutions: sum(r => r.failedExecutions), - problems: results.flatMap(r => r.problems), - }; -} - -async function writeExecutions(executions: ExecutionList['executions'], dir: string, datatype: Datatype) { - const written = await mapWithConcurrency(executions, EXECUTION_CONCURRENCY, async item => { - try { - const execution = await getExperimentExecution(item.id, false); - await writeFile(`${dir}/execution-${item.id}.${datatype}`, execution, datatype); - return true; - } catch { - return false; - } - }); - return { written: written.filter(Boolean).length, failed: written.filter(ok => !ok).length }; -} - -async function ensureDirectoryExists(dir: string) { - await fs.mkdir(dir, { recursive: true }); -} diff --git a/src/experiment/exec.interactive.test.ts b/src/experiment/exec.interactive.test.ts deleted file mode 100644 index 1e37ade..0000000 --- a/src/experiment/exec.interactive.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { wrapPrompt } from '@inquirer/testing/vitest'; -import { answerPrompt } from '../mocks/prompts.ts'; -import { givenAnotherExperimentIsRunning } from '../mocks/handlers.ts'; -import { executeExperiments } from './exec.ts'; - -vi.mock('@inquirer/confirm', async importOriginal => { - const actual = await importOriginal(); - return { ...actual, default: wrapPrompt(actual.default) }; -}); - -// These go through the prompts rather than past them. exec.test.ts covers the same -// commands with no terminal attached, which is what CI does and what the -// defaultWhenNonInteractive values are about; both branches matter. -describe('experiment run, with a terminal', () => { - let logged: string[]; - - beforeEach(() => { - Object.defineProperty(process.stdout, 'isTTY', { value: true, configurable: true }); - logged = []; - vi.spyOn(console, 'log').mockImplementation((...args) => { - logged.push(args.join(' ')); - }); - }); - - afterEach(() => { - Object.defineProperty(process.stdout, 'isTTY', { value: undefined, configurable: true }); - vi.restoreAllMocks(); - }); - - it('should not run anything when the confirmation is declined', async () => { - // Throwing rather than returning, so that the flow stops here the way a real exit - // would. A no-op stub lets execution carry on and run the experiment anyway. - const exit = vi.spyOn(process, 'exit').mockImplementation(code => { - throw new Error(`exited with ${code}`); - }); - - const done = executeExperiments({ key: 'TST-1', recursive: false }); - await answerPrompt('Are you sure you want to run the experiment?', 'n'); - - await expect(done).rejects.toThrow('exited with 0'); - expect(exit).toHaveBeenCalledWith(0); - expect(logged.join('\n')).not.toContain('Executing experiment'); - }); - - // This recovery was unreachable until the response body it inspects stopped being - // consumed before it got there, so it has never been exercised end to end. - it('should offer to run in parallel when another experiment is already running', async () => { - givenAnotherExperimentIsRunning(); - - const done = executeExperiments({ key: 'TST-1', recursive: false }); - await answerPrompt('Are you sure you want to run the experiment?', 'y'); - await answerPrompt('There is already an experiment running', 'y'); - await done; - - expect(logged.join('\n')).toContain('Executing experiment: TST-1'); - }); - - it('should give up when running in parallel is declined', async () => { - givenAnotherExperimentIsRunning(); - - const done = executeExperiments({ key: 'TST-1', recursive: false }); - await answerPrompt('Are you sure you want to run the experiment?', 'y'); - await answerPrompt('There is already an experiment running', 'n'); - - await expect(done).rejects.toThrow('Failed to run experiment (TST-1)'); - expect(logged.join('\n')).not.toContain('Executing experiment'); - }); -}); diff --git a/src/experiment/exec.test.ts b/src/experiment/exec.test.ts deleted file mode 100644 index e78802e..0000000 --- a/src/experiment/exec.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { describe, expect, it, vi } from 'vitest'; -import { EXPERIMENTS, setValidationFailures } from '../mocks/handlers.ts'; -import { executeExperiments } from './exec.ts'; -import { getTempDir, writeFile } from '../mocks/tempFiles.ts'; - -describe('experiment', () => { - describe('exec', () => { - it('should throw when neither key nor file is given', async () => { - await expect(executeExperiments({ recursive: false, yes: true })).rejects.toThrow( - 'Either --key, --file or --template must be specified.' - ); - }); - - it('should run experiment by key', async () => { - const logSpy = vi.spyOn(console, 'log'); - - await executeExperiments({ key: 'TST-1', recursive: false, yes: true }); - - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'TST-1'); - expect(logSpy).toHaveBeenCalledWith('Experiment run API:', 'http://example.com/api/experiments/executions/1'); - expect(logSpy).toHaveBeenCalledWith( - 'Experiment run UI:', - 'http://example.com/experiments/edit/TST-1/executions/1?tenant=example&team=EXAMPLE' - ); - }); - - it('should run experiment by file with update', async () => { - const file = await writeFile('experiment.yaml', EXPERIMENTS['TST-1']); - const logSpy = vi.spyOn(console, 'log'); - - await executeExperiments({ file: [file], recursive: false, yes: true }); - - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'TST-1'); - expect(logSpy).toHaveBeenCalledWith('Experiment run API:', 'http://example.com/api/experiments/executions/1'); - expect(logSpy).toHaveBeenCalledWith( - 'Experiment run UI:', - 'http://example.com/experiments/edit/TST-1/executions/1?tenant=example&team=EXAMPLE' - ); - }); - - it('should run experiment by file with upsert', async () => { - const file = await writeFile('experiment.yaml', EXPERIMENTS['NEW']); - const logSpy = vi.spyOn(console, 'log'); - - await executeExperiments({ file: [file], recursive: false, yes: true }); - - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'NEW-1'); - expect(logSpy).toHaveBeenCalledWith('Experiment run API:', 'http://example.com/api/experiments/executions/1'); - expect(logSpy).toHaveBeenCalledWith( - 'Experiment run UI:', - 'http://example.com/experiments/edit/TST-1/executions/1?tenant=example&team=EXAMPLE' - ); - }); - - it('should run experiments from directory with upsert', async () => { - await writeFile('experiment-1.yaml', EXPERIMENTS['NEW']); - await writeFile('experiment-2.yaml', EXPERIMENTS['NEW']); - const logSpy = vi.spyOn(console, 'log'); - - await executeExperiments({ file: [getTempDir()], recursive: false, yes: true }); - - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'NEW-1'); - expect(logSpy).toHaveBeenCalledWith('Experiment run API:', 'http://example.com/api/experiments/executions/1'); - expect(logSpy).toHaveBeenCalledWith( - 'Experiment run UI:', - 'http://example.com/experiments/edit/NEW-1/executions/1?tenant=example&team=EXAMPLE' - ); - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'NEW-2'); - expect(logSpy).toHaveBeenCalledWith( - 'Experiment run UI:', - 'http://example.com/experiments/edit/NEW-2/executions/2?tenant=example&team=EXAMPLE' - ); - }); - - it('should retry on 422 validation error and succeed when resolved (by key)', async () => { - setValidationFailures(2); - const logSpy = vi.spyOn(console, 'log'); - - await executeExperiments({ key: 'TST-1', recursive: false, yes: true, retries: 3, retryInterval: 0 }); - - expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Experiment has validation errors (attempt 1/4)')); - expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Experiment has validation errors (attempt 2/4)')); - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'TST-1'); - }); - - it('should retry on 422 validation error and succeed when resolved (by file with upsert)', async () => { - setValidationFailures(1); - const file = await writeFile('experiment.yaml', EXPERIMENTS['NEW']); - const logSpy = vi.spyOn(console, 'log'); - - await executeExperiments({ file: [file], recursive: false, yes: true, retries: 2, retryInterval: 0 }); - - expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Experiment has validation errors (attempt 1/3)')); - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'NEW-1'); - }); - - it('should fail after exhausting all retries on 422 validation error', async () => { - setValidationFailures(5); - - await expect( - executeExperiments({ key: 'TST-1', recursive: false, yes: true, retries: 2, retryInterval: 0 }) - ).rejects.toThrow(); - }); - - it('should use forcePersist=true and skip validation when retries is 0', async () => { - setValidationFailures(1); - const logSpy = vi.spyOn(console, 'log').mockClear(); - - // With retries=0, forcePersist=true so the mock won't return 422 - await executeExperiments({ key: 'TST-1', recursive: false, yes: true }); - - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'TST-1'); - expect(logSpy).not.toHaveBeenCalledWith(expect.stringContaining('validation errors')); - }); - - it('should throw when key and two or more files are given', async () => { - await writeFile('experiment-1.yaml', EXPERIMENTS['NEW']); - await writeFile('experiment-2.yaml', EXPERIMENTS['NEW']); - - await expect( - executeExperiments({ - key: 'TST-1', - file: [getTempDir()], - recursive: false, - yes: true, - }) - ).rejects.toThrow('If --key is specified, at most one --file can be specified.'); - }); - }); -}); diff --git a/src/experiment/exec.ts b/src/experiment/exec.ts deleted file mode 100644 index 8b59d08..0000000 --- a/src/experiment/exec.ts +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { setTimeout as sleep } from 'node:timers/promises'; -import { confirm } from '../prompt/confirm.ts'; -import { loadExperiment, resolveExperimentFiles, writeFile } from './files.ts'; -import * as api from './api.ts'; -import { ApiError } from '../api/error.ts'; -import { abortExecution, abortExecutionWithError } from '../errors.ts'; -import type { ExecuteResult } from './types.ts'; -import { toCreateFromTemplate, type TemplateOptions } from './template.ts'; - -type Options = { - key?: string; - file?: string[]; - yes?: boolean; - wait?: boolean; - recursive: boolean; - allowParallel?: boolean; - retries?: number; - retryInterval?: number; - executionVariable?: Record; -} & Partial; - -export async function executeExperiments(options: Options) { - if (!options.yes) { - const confirmation = await confirm('Are you sure you want to run the experiment?', { - defaultYes: false, - defaultWhenNonInteractive: true, - }); - if (!confirmation) { - process.exit(0); - } - } - - if (options.template && options.key) { - throw abortExecution( - '--key cannot be combined with --template. Use `experiment apply --template -k` to update it.' - ); - } else if (options.template) { - await executeFromTemplate({ - ...options, - template: options.template, - resetProperties: options.resetProperties ?? true, - }); - } else if (!options.file && !options.key) { - throw abortExecution('Either --key, --file or --template must be specified.'); - } else if (options.file) { - const files = await resolveExperimentFiles(options.file, options.recursive); - if (files.length > 1 && options.key) { - throw abortExecution('If --key is specified, at most one --file can be specified.'); - } - - for (const file of files) { - const { experiment, datatype } = await loadExperiment(file); - let key = options.key || experiment.key; - let result: ExecuteResult; - - const hasRetries = (options.retries ?? 0) > 0; - if (key) { - await api.updateExperiment(key, experiment); - result = await executeWithRetry( - () => api.executeExperiment(key!, !!options.yes, options.allowParallel, !hasRetries), - options.retries, - options.retryInterval - ); - } else { - const upsertResult = await executeWithRetry( - () => api.upsertAndExecuteExperiment(experiment, options.allowParallel, !hasRetries), - options.retries, - options.retryInterval - ); - key = upsertResult.key; - result = upsertResult; - if (!experiment.key) { - await writeFile(file, { key, ...experiment }, datatype); - } - } - - console.log('Executing experiment:', key); - console.log('Experiment run API:', result.location); - console.log('Experiment run UI:', result.uiLocation); - /* eslint-disable @typescript-eslint/no-unused-expressions */ - options.wait && result.location && (await waitFor(result.location)); - } - } else if (options.key) { - const hasRetries = (options.retries ?? 0) > 0; - const result = await executeWithRetry( - () => api.executeExperiment(options.key!, !!options.yes, options.allowParallel, !hasRetries), - options.retries, - options.retryInterval - ); - console.log('Experiment run API:', result.location); - console.log('Experiment run UI:', result.uiLocation); - console.log('Executing experiment:', options.key); - /* eslint-disable @typescript-eslint/no-unused-expressions */ - options.wait && result.location && (await waitFor(result.location)); - } -} - -async function executeFromTemplate(options: Options & TemplateOptions) { - const body = { ...(await toCreateFromTemplate(options)), executionVariables: options.executionVariable }; - const hasRetries = (options.retries ?? 0) > 0; - const result = await executeWithRetry( - () => - api.runExperimentFromTemplate(options.template, body, { - resetProperties: options.resetProperties, - yes: !!options.yes, - allowParallel: !!options.allowParallel, - forcePersist: !hasRetries, - }), - options.retries, - options.retryInterval - ); - console.log('Executing experiment:', result.key); - console.log('Experiment run API:', result.location); - console.log('Experiment run UI:', result.uiLocation); - /* eslint-disable @typescript-eslint/no-unused-expressions */ - options.wait && result.location && (await waitFor(result.location)); -} - -async function executeWithRetry(fn: () => Promise, retries = 0, retryInterval = 10): Promise { - for (let attempt = 0; attempt <= retries; attempt++) { - try { - return await fn(); - } catch (e) { - if (e instanceof ApiError && e.status === 422 && attempt < retries) { - console.log( - `Experiment has validation errors (attempt ${attempt + 1}/${retries + 1}). Retrying in ${retryInterval}s...` - ); - await sleep(retryInterval * 1000); - continue; - } - throw abortExecutionWithError(e, 'Failed to execute experiment'); - } - } - throw new Error('Unexpected end of retry loop'); -} - -async function waitFor(location: string): Promise { - // Loaded lazily because polling a run is the only thing in the CLI that needs rxjs, - // and importing it costs far more than the rest of this subcommand's module graph. - const { filter, firstValueFrom, from, interval, switchMap, tap } = await import('rxjs'); - - const executionResult = await firstValueFrom( - interval(5000) - .pipe(switchMap(() => from(api.getExperimentExecutionUsingUrl(location ?? '')))) - .pipe(tap(e => console.log('Current run state:', e.state.toLowerCase()))) - .pipe( - filter(e => e.state === 'FAILED' || e.state === 'ERRORED' || e.state === 'CANCELED' || e.state === 'COMPLETED') - ) - ); - - if (executionResult && executionResult.state !== 'COMPLETED') { - console.error( - `Experiment ${executionResult.key} (#${executionResult.id}) ${executionResult.state.toLowerCase()}${executionResult.reason ? `, reason: ${executionResult.reason}` : ''}` - ); - process.exit(1); - } -} diff --git a/src/experiment/files.test.ts b/src/experiment/files.test.ts deleted file mode 100644 index ba56243..0000000 --- a/src/experiment/files.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { beforeAll, describe, expect, it } from 'vitest'; -import { getTempDir, writeFile } from '../mocks/tempFiles.ts'; -import { resolveExperimentFiles } from './files.ts'; - -describe('experiment/files', () => { - describe('resolveExperimentFiles', () => { - let files: string[]; - let nestedFiles: string[]; - - beforeAll(async () => { - files = await Promise.all([writeFile('experiment.yaml', {}), writeFile('experiment.yml', {})]); - nestedFiles = await Promise.all([ - writeFile('nested/experiment.yaml', {}), - writeFile('nested/experiment.yml', {}), - ]); - await Promise.all([writeFile('other.txt', {}), writeFile('nested/other.txt', {})]); - }); - - it('should resolve all files recursive', async () => { - expect(await resolveExperimentFiles([getTempDir()], true)).toEqual([...files, ...nestedFiles]); - }); - it('should resolve files non-recursive', async () => { - expect(await resolveExperimentFiles([getTempDir()], false)).toEqual(files); - }); - it('should resolve files directly', async () => { - expect(await resolveExperimentFiles(files, false)).toEqual(files); - }); - it('should resolve empty files', async () => { - expect(await resolveExperimentFiles([], false)).toEqual([]); - }); - }); -}); diff --git a/src/experiment/files.ts b/src/experiment/files.ts deleted file mode 100644 index affbcdf..0000000 --- a/src/experiment/files.ts +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import type { Experiment } from './types.ts'; -import fs from 'node:fs/promises'; -import { dump, load } from '../yaml.ts'; -import { abortExecution, errorMessage } from '../errors.ts'; -import path from 'node:path'; - -export type Datatype = 'json' | 'yaml'; - -export interface ExperimentFromFile { - experiment: Experiment; - datatype: Datatype; -} - -export async function resolveExperimentFiles(files: string[], recursive: boolean): Promise { - const results = []; - - for (const file of files) { - try { - const stat = await fs.stat(file); - - if (stat.isDirectory()) { - const subDirectories = []; - for (const entry of await fs.readdir(file, { withFileTypes: true })) { - if (entry.isDirectory()) { - subDirectories.push(path.join(file, entry.name)); - } else if ( - entry.isFile() && - (entry.name.toLowerCase().endsWith('.yaml') || entry.name.toLowerCase().endsWith('.yml')) - ) { - results.push(path.join(file, entry.name)); - } - } - if (recursive && subDirectories.length > 0) { - results.push(...(await resolveExperimentFiles(subDirectories, recursive))); - } - } else { - results.push(file); - } - } catch (e: any) { - if (e.code === 'ENOENT') { - throw abortExecution(`File or directory '${file}' not found.`); - } else { - throw e; - } - } - } - - return results; -} - -export async function writeFile(file: string, content: unknown, datatype: Datatype): Promise { - await fs.writeFile(file, datatype === 'json' ? JSON.stringify(content) : dump(content), { encoding: 'utf8' }); -} - -export async function loadExperiment(file: string): Promise { - let fileContent: string; - try { - fileContent = await fs.readFile(file, { encoding: 'utf8' }); - } catch (e) { - throw abortExecution("Failed to read experiment file at path '%s': %s", file, errorMessage(e)); - } - - try { - const experiment = JSON.parse(fileContent) as Experiment; - return { experiment, datatype: 'json' }; - } catch { - try { - const experiment = load(fileContent) as Experiment; - return { experiment, datatype: 'yaml' }; - } catch (e) { - throw abortExecution("Failed to parse experiment file at path '%s' as YAML/JSON: %s", file, errorMessage(e)); - } - } -} diff --git a/src/experiment/get.test.ts b/src/experiment/get.test.ts deleted file mode 100644 index a7307f0..0000000 --- a/src/experiment/get.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { describe, expect, it, vi } from 'vitest'; -import { getExperiment } from './get.ts'; -import path from 'node:path'; -import fs from 'node:fs/promises'; -import { getTempDir, writeFile } from '../mocks/tempFiles.ts'; -import { EXPERIMENTS } from '../mocks/handlers.ts'; - -describe('experiment', () => { - describe('get', () => { - it('should output experiment to file', async () => { - const file = path.join(getTempDir(), 'experiment.yaml'); - - await getExperiment({ key: 'TST-1', file }); - - const content = await fs.readFile(file, 'utf-8'); - expect(content).toMatchSnapshot(); - }); - - // The snapshot alone would have kept passing while stdout held Node's inspect - // format, which looks close enough to JSON to read but no parser accepts. - it('should output experiment to stdout JSON', async () => { - const logSpy = vi.spyOn(console, 'log'); - logSpy.mockClear(); - - await getExperiment({ key: 'TST-1', type: 'json' }); - - const [stdout] = logSpy.mock.calls[0]; - expect(typeof stdout).toEqual('string'); - expect(JSON.parse(String(stdout))).toEqual(EXPERIMENTS['TST-1']); - expect(stdout).toMatchSnapshot(); - }); - - it('should output experiment to stdout YAML', async () => { - const logSpy = vi.spyOn(console, 'log'); - logSpy.mockClear(); - - await getExperiment({ key: 'TST-1' }); - - const [stdout] = logSpy.mock.calls[0]; - expect(stdout).toMatchSnapshot(); - }); - - it('should report not found', async () => { - await expect(getExperiment({ key: 'TST-999' })).rejects.toThrow('Experiment TST-999 not found.'); - }); - - it('should write experiment to file with same data type YAML', async () => { - const file = path.join(getTempDir(), 'experiment.yaml'); - await writeFile(file, EXPERIMENTS['TST-1']); - - await getExperiment({ key: 'TST-1', file }); - - const content = await fs.readFile(file, 'utf-8'); - expect(content).toMatchSnapshot(); - }); - - it('should write experiment to file with same data type JSON', async () => { - const file = path.join(getTempDir(), 'experiment.json'); - await writeFile(file, EXPERIMENTS['TST-1'], 'json'); - - await getExperiment({ key: 'TST-1', file }); - - const content = await fs.readFile(file, 'utf-8'); - expect(content).toMatchSnapshot(); - }); - }); -}); diff --git a/src/experiment/get.ts b/src/experiment/get.ts deleted file mode 100644 index 02aebd3..0000000 --- a/src/experiment/get.ts +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { dump } from '../yaml.ts'; -import { type Datatype, writeFile } from './files.ts'; -import { fetchExperiment } from './api.ts'; - -export interface Options { - key: string; - file?: string; - type?: Datatype; -} - -export async function getExperiment(options: Options) { - const experiment = await fetchExperiment(options.key); - const datatype: Datatype = options.type ? options.type : options.file?.endsWith('.json') ? 'json' : 'yaml'; - - if (!options.file) { - // Serialised, not handed to console.log as an object: that prints Node's inspect - // format, with unquoted keys and single quotes, which no JSON reader accepts. - console.log(datatype === 'json' ? JSON.stringify(experiment, undefined, 2) : dump(experiment)); - } else { - await writeFile(options.file, experiment, datatype); - console.log('Experiment %s written to %s.', options.key, options.file); - } -} diff --git a/src/experiment/template.test.ts b/src/experiment/template.test.ts deleted file mode 100644 index 581c3ab..0000000 --- a/src/experiment/template.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it, vi } from 'vitest'; -import { applyExperiments } from './apply.ts'; -import { executeExperiments } from './exec.ts'; -import { respondTo } from '../mocks/recorder.ts'; -import { writeFile } from '../mocks/tempFiles.ts'; - -const TEMPLATE = 'd7e65100-1d20-4980-be87-c351704910b8'; - -describe('experiment from template', () => { - describe('apply', () => { - it('creates an experiment with the placeholders from file and flags', async () => { - const requests = respondTo('post', `/api/experiments/templates/${TEMPLATE}/experiment-create`, () => ({ - status: 201, - headers: { location: 'http://example.com/api/experiments/ADM-12' }, - })); - const placeholders = await writeFile('values.yml', { CLUSTER: 'dev', REPLICAS: 3 }); - const logSpy = vi.spyOn(console, 'log'); - - await applyExperiments({ - recursive: false, - template: TEMPLATE, - team: 'ADM', - environment: 'Global', - externalId: 'shop-latency', - placeholders, - placeholder: { CLUSTER: 'prod' }, - variable: { endpoint: 'http://shop?x=1' }, - resetProperties: false, - }); - - expect(requests).toHaveLength(1); - expect(requests[0].url.searchParams.get('resetProperties')).toBe('false'); - expect(requests[0].body).toEqual({ - team: 'ADM', - environment: 'Global', - externalId: 'shop-latency', - placeholders: [ - { key: 'CLUSTER', value: 'prod' }, - { key: 'REPLICAS', value: 3 }, - ], - experimentVariables: { endpoint: 'http://shop?x=1' }, - }); - expect(logSpy).toHaveBeenCalledWith('Experiment %s %s from template %s.', 'ADM-12', 'created', TEMPLATE); - }); - - it('accepts the platform list form of placeholders in a file', async () => { - const requests = respondTo('post', `/api/experiments/templates/${TEMPLATE}/experiment-create`, () => ({ - status: 200, - headers: { location: 'http://example.com/api/experiments/ADM-12' }, - })); - const placeholders = await writeFile('values.json', [{ key: 'LIST', value: ['a', 'b'] }], 'json'); - - await applyExperiments({ - recursive: false, - template: TEMPLATE, - team: 'ADM', - placeholders, - resetProperties: true, - }); - - expect((requests[0].body as any).placeholders).toEqual([{ key: 'LIST', value: ['a', 'b'] }]); - }); - - it('rejects a placeholders file that is neither a map nor a list of entries', async () => { - const placeholders = await writeFile('values.yml', ['just', 'strings']); - - await expect( - applyExperiments({ recursive: false, template: TEMPLATE, team: 'ADM', placeholders, resetProperties: true }) - ).rejects.toThrow('must be a map of key to value or a list of {key, value} entries'); - }); - - it('requires a team to create an experiment', async () => { - await expect(applyExperiments({ recursive: false, template: TEMPLATE, resetProperties: true })).rejects.toThrow( - '--team is required to create an experiment from a template.' - ); - }); - - it('updates an existing experiment when a key is given', async () => { - const requests = respondTo('post', `/api/experiments/templates/${TEMPLATE}/experiment-update/ADM-12`, () => ({ - status: 200, - })); - const logSpy = vi.spyOn(console, 'log'); - - await applyExperiments({ - recursive: false, - key: 'ADM-12', - template: TEMPLATE, - placeholder: { CLUSTER: 'prod' }, - resetProperties: true, - }); - - expect(requests[0].body).toEqual({ placeholders: [{ key: 'CLUSTER', value: 'prod' }] }); - expect(requests[0].url.searchParams.get('resetProperties')).toBe('true'); - expect(logSpy).toHaveBeenCalledWith('Experiment %s updated from template %s.', 'ADM-12', TEMPLATE); - }); - - it('refuses options an update would silently ignore', async () => { - await expect( - applyExperiments({ recursive: false, key: 'ADM-12', template: TEMPLATE, team: 'ADM', resetProperties: true }) - ).rejects.toThrow('only takes placeholders; remove --team'); - }); - - it('reports a template that does not exist', async () => { - respondTo('post', `/api/experiments/templates/${TEMPLATE}/experiment-create`, () => ({ status: 404 })); - - await expect( - applyExperiments({ recursive: false, template: TEMPLATE, team: 'ADM', resetProperties: true }) - ).rejects.toThrow(`Experiment template ${TEMPLATE} not found.`); - }); - - it('still requires a file or a template', async () => { - await expect(applyExperiments({ recursive: false })).rejects.toThrow( - 'Either --file or --template must be specified.' - ); - }); - }); - - describe('run', () => { - const executed = (key: string, run: number) => ({ - status: 200, - json: { - key, - executionId: run, - apiLocation: `http://example.com/api/experiments/executions/${run}`, - uiLocation: `http://example.com/experiments/edit/${key}/executions/${run}`, - }, - }); - - it('creates and runs an experiment from a template', async () => { - const requests = respondTo('post', `/api/experiments/templates/${TEMPLATE}/experiment-execute`, () => - executed('ADM-12', 7) - ); - const logSpy = vi.spyOn(console, 'log'); - - await executeExperiments({ - recursive: false, - yes: true, - wait: false, - template: TEMPLATE, - team: 'ADM', - placeholder: { CLUSTER: 'prod' }, - executionVariable: { region: 'eu' }, - resetProperties: true, - }); - - expect(requests[0].body).toEqual({ - team: 'ADM', - placeholders: [{ key: 'CLUSTER', value: 'prod' }], - executionVariables: { region: 'eu' }, - }); - expect(Object.fromEntries(requests[0].url.searchParams)).toEqual({ - resetProperties: 'true', - allowParallel: 'false', - forcePersist: 'true', - }); - expect(logSpy).toHaveBeenCalledWith('Executing experiment:', 'ADM-12'); - expect(logSpy).toHaveBeenCalledWith('Experiment run API:', 'http://example.com/api/experiments/executions/7'); - expect(logSpy).toHaveBeenCalledWith( - 'Experiment run UI:', - 'http://example.com/experiments/edit/ADM-12/executions/7' - ); - }); - - it('retries validation errors without persisting the failed runs', async () => { - let failures = 1; - const requests = respondTo('post', `/api/experiments/templates/${TEMPLATE}/experiment-execute`, () => - failures-- > 0 ? { status: 422, json: { title: 'no targets' } } : executed('ADM-12', 8) - ); - - await executeExperiments({ - recursive: false, - yes: true, - wait: false, - template: TEMPLATE, - team: 'ADM', - retries: 2, - retryInterval: 0, - resetProperties: true, - }); - - expect(requests).toHaveLength(2); - expect(requests.every(r => r.url.searchParams.get('forcePersist') === 'false')).toBe(true); - }); - - it('runs in parallel with --yes when another experiment is running', async () => { - const requests = respondTo('post', `/api/experiments/templates/${TEMPLATE}/experiment-execute`, ({ url }) => - url.searchParams.get('allowParallel') === 'true' - ? executed('ADM-12', 9) - : { - status: 409, - json: { type: 'https://steadybit.com/problems/another-experiment-running-exception' }, - } - ); - - await executeExperiments({ - recursive: false, - yes: true, - wait: false, - template: TEMPLATE, - team: 'ADM', - resetProperties: true, - }); - - expect(requests.map(r => r.url.searchParams.get('allowParallel'))).toEqual(['false', 'true']); - }); - - it('refuses a key together with a template', async () => { - await expect( - executeExperiments({ recursive: false, yes: true, key: 'ADM-1', template: TEMPLATE, resetProperties: true }) - ).rejects.toThrow('--key cannot be combined with --template.'); - }); - }); -}); diff --git a/src/experiment/template.ts b/src/experiment/template.ts deleted file mode 100644 index fceee1f..0000000 --- a/src/experiment/template.ts +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { Schemas } from '../api/schemas.ts'; -import { abortExecution } from '../errors.ts'; -import { readStructuredFile } from '../structuredFiles.ts'; -import { createExperimentFromTemplate, updateExperimentFromTemplate } from './api.ts'; - -type PlaceholderValue = Schemas['ExperimentTemplatePlaceholderValueAO']; - -export interface TemplateOptions { - template: string; - team?: string; - environment?: string; - externalId?: string; - placeholder?: Record; - placeholders?: string; - variable?: Record; - resetProperties: boolean; -} - -// A placeholders file is either a map of key to value, which is what someone writes by -// hand, or the platform's own list of {key, value} pairs, which is what they copy out -// of an API call. Placeholders given with -p are applied on top, so a pipeline can keep -// the shared values in a file and override one per stage. -export async function resolvePlaceholders(options: TemplateOptions): Promise { - const values = new Map(); - - if (options.placeholders) { - const { content } = await readStructuredFile(options.placeholders, 'placeholders'); - if (Array.isArray(content)) { - for (const entry of content) { - if (typeof entry?.key !== 'string' || !('value' in entry)) { - throw abortExecution( - "Placeholders file '%s' must be a map of key to value or a list of {key, value} entries.", - options.placeholders - ); - } - values.set(entry.key, entry.value); - } - } else if (content && typeof content === 'object') { - Object.entries(content).forEach(([key, value]) => values.set(key, value)); - } else { - throw abortExecution( - "Placeholders file '%s' must be a map of key to value or a list of {key, value} entries.", - options.placeholders - ); - } - } - - Object.entries(options.placeholder ?? {}).forEach(([key, value]) => values.set(key, value)); - - // The spec types a placeholder value as an object although it is any JSON value. - return [...values].map(([key, value]) => ({ key, value: value as PlaceholderValue['value'] })); -} - -export async function toCreateFromTemplate( - options: TemplateOptions -): Promise { - if (!options.team) { - throw abortExecution('--team is required to create an experiment from a template.'); - } - return { - team: options.team, - environment: options.environment, - externalId: options.externalId, - placeholders: await resolvePlaceholders(options), - experimentVariables: options.variable, - }; -} - -export interface ApplyFromTemplateOptions extends TemplateOptions { - key?: string; -} - -export async function applyExperimentFromTemplate(options: ApplyFromTemplateOptions): Promise { - if (options.key) { - // Updating an experiment only re-renders it with new placeholder values. It keeps - // its team and environment, so accepting those here would silently do nothing. - const ignored = (['team', 'environment', 'externalId', 'variable'] as const).filter(o => options[o] !== undefined); - if (ignored.length > 0) { - throw abortExecution( - 'Updating experiment %s from a template only takes placeholders; remove %s.', - options.key, - ignored.map(o => `--${o === 'externalId' ? 'external-id' : o}`).join(', ') - ); - } - await updateExperimentFromTemplate( - options.template, - options.key, - { placeholders: await resolvePlaceholders(options) }, - { resetProperties: options.resetProperties } - ); - console.log('Experiment %s updated from template %s.', options.key, options.template); - return; - } - - const result = await createExperimentFromTemplate(options.template, await toCreateFromTemplate(options), { - resetProperties: options.resetProperties, - }); - console.log( - 'Experiment %s %s from template %s.', - result.key, - result.created ? 'created' : 'updated', - options.template - ); -} diff --git a/src/experiment/types.ts b/src/experiment/types.ts deleted file mode 100644 index f8ea03b..0000000 --- a/src/experiment/types.ts +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -export type ExecutionError = { - type: string; -}; - -export type ExecuteResult = { - location?: string; - uiLocation?: string; -}; - -export type ExecutionResult = { - state: string; - key: string; - id: string; - reason?: string; -}; - -export type Experiment = Record & { - externalId?: string; - key?: string; -}; - -export type UpsertResult = { - created?: boolean; - key?: string; -}; - -export type UpsertAndExecuteResult = ExecuteResult & { - key?: string; -}; - -export type ExperimentList = { - experiments: Record & - { - key: string; - name: string; - }[]; -}; - -export type ExecutionList = { - executions: Record & - { - id: number; - key: string; - name: string; - created: string; - ended: string; - state: string; - }[]; -}; diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts deleted file mode 100644 index 6f5f11b..0000000 --- a/src/mocks/handlers.ts +++ /dev/null @@ -1,382 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH -import { http, HttpResponse } from 'msw'; -import type { Experiment } from '../experiment/types.ts'; -import type { FetchAdviceRequest, FetchAdviceResponse } from '../advice/types.ts'; - -let retryCount = 0; -let runSequence = 1; -let experimentSequence = 1; -let experimentStore: Record = {}; -let validationFailuresRemaining = 0; -let executionsPerExperiment: Record = {}; -let unfetchableExecutions = new Set(); -let anotherExperimentRunning = false; - -// Lets a test reach the "an experiment is already running, run it in parallel?" recovery, -// which the platform only offers when the caller has not already asked for parallel. -export const givenAnotherExperimentIsRunning = () => { - anotherExperimentRunning = true; -}; - -function executionsFor(key: string): { id: number }[] { - return (executionsPerExperiment[key] ?? []).map(id => ({ id })); -} - -// Lets a dump test set up an experiment whose executions cannot all be fetched. -export const givenExecutions = (key: string, ids: number[], unfetchable: number[] = []) => { - executionsPerExperiment[key] = ids; - unfetchable.forEach(id => unfetchableExecutions.add(id)); -}; - -export const resetExperiments = () => { - retryCount = 0; - experimentSequence = 1; - runSequence = 1; - experimentStore = { 'TST-1': EXPERIMENTS['TST-1'] }; - validationFailuresRemaining = 0; - executionsPerExperiment = {}; - unfetchableExecutions = new Set(); - anotherExperimentRunning = false; -}; - -export const setValidationFailures = (count: number) => { - validationFailuresRemaining = count; -}; - -export const EXPERIMENTS: Record = { - 'TST-1': { - key: 'TST-1', - name: 'Verify TTR fashion bestseller', - team: 'TST', - environment: 'Global', - lanes: [ - { - steps: [ - { - type: 'action', - ignoreFailure: false, - parameters: { - graceful: 'true', - }, - actionType: 'container-stop-attack', - radius: { - targetType: 'container', - predicate: { - operator: 'AND', - predicates: [ - { - key: 'k8s.namespace', - operator: 'EQUALS', - values: ['steadybit-demo'], - }, - { - key: 'k8s.deployment', - operator: 'EQUALS', - values: ['fashion-bestseller'], - }, - ], - }, - query: null, - percentage: 50, - }, - }, - ], - }, - ], - }, - NEW: { - name: 'Verify TTR fashion bestseller', - team: 'TST', - environment: 'Global', - lanes: [ - { - steps: [ - { - type: 'action', - ignoreFailure: false, - parameters: { - graceful: 'true', - }, - actionType: 'container-stop-attack', - radius: { - targetType: 'container', - predicate: { - operator: 'AND', - predicates: [ - { - key: 'k8s.namespace', - operator: 'EQUALS', - values: ['steadybit-demo'], - }, - { - key: 'k8s.deployment', - operator: 'EQUALS', - values: ['fashion-bestseller'], - }, - ], - }, - query: null, - percentage: 50, - }, - }, - ], - }, - ], - }, -}; - -const getTooManyRequestsHandler = http.get('http://example.com/api/status', async ({ request }) => { - const headers: Record = {}; - const query = new URL(request.url).searchParams; - const reset = query.get('reset'); - const times = Number(query.get('times')); - let code = Number(query.get('code')) || 200; - if (reset) { - headers['RateLimit-Reset'] = reset; - } - if (times) { - if (retryCount < times) { - retryCount++; - } else { - code = 200; - } - } - return HttpResponse.text(String(query.get('body')), { status: code, headers: headers }); -}); - -const getExperimentHandler = http.get('http://example.com/api/experiments/:key', async ({ params }) => { - const experiment = experimentStore[String(params.key)]; - if (experiment) { - return HttpResponse.json(experiment); - } else { - return HttpResponse.json('', { status: 404 }); - } -}); - -const deleteExperimentHandler = http.delete('http://example.com/api/experiments/:key', async ({ params }) => { - const experiment = experimentStore[String(params.key)]; - delete experimentStore[String(params.key)]; - return HttpResponse.json('', { status: experiment ? 200 : 404 }); -}); - -const updateExperimentHandler = http.post('http://example.com/api/experiments/:key', async ({ request, params }) => { - const experiment = experimentStore[String(params.key)]; - if (experiment) { - experimentStore[String(params.key)] = request.json(); - } - return HttpResponse.json('', { status: experiment ? 200 : 404 }); -}); - -const upsertExperimentHandler = http.post('http://example.com/api/experiments', async ({ request }) => { - const key = `NEW-${experimentSequence++}`; - experimentStore[key] = request.json(); - return HttpResponse.json('', { status: 201, headers: { location: `http://example.com/api/experiments/${key}` } }); -}); - -const executeExperimentHandler = http.post('http://example.com/api/experiments/:key/execute', ({ params, request }) => { - const experiment = experimentStore[String(params.key)]; - const requestUrl = new URL(request.url); - const forcePersist = requestUrl.searchParams.get('forcePersist'); - - if (validationFailuresRemaining > 0 && forcePersist === 'false') { - validationFailuresRemaining--; - return HttpResponse.json( - { - type: 'https://steadybit.com/problems/experiment-invalid-exception', - title: - 'Had validation errors (lanes[0].steps[0].blastRadius.predicate: Please specify a query to select targets).', - status: 422, - instance: `/api/experiments/${params.key}/execute`, - }, - { status: 422 } - ); - } - - if (anotherExperimentRunning && requestUrl.searchParams.get('allowParallel') !== 'true') { - return HttpResponse.json( - { - type: 'https://steadybit.com/problems/another-experiment-running-exception', - title: 'Another experiment is currently running.', - status: 409, - instance: `/api/experiments/${params.key}/execute`, - }, - { status: 409 } - ); - } - - const run = runSequence++; - if (experiment) { - return HttpResponse.json( - { - key: params.key, - executionId: run, - apiLocation: `http://example.com/api/experiments/executions/${run}`, - uiLocation: `http://example.com/experiments/edit/${params.key}/executions/${run}?tenant=example&team=EXAMPLE`, - }, - { - status: 201, - headers: { location: `http://example.com/api/experiments/executions/${run}` }, - } - ); - } else { - return HttpResponse.json('', { status: 404 }); - } -}); - -const executeUpsertExperimentHandler = http.post('http://example.com/api/experiments/execute', ({ request }) => { - const requestUrl = new URL(request.url); - const forcePersist = requestUrl.searchParams.get('forcePersist'); - - if (validationFailuresRemaining > 0 && forcePersist === 'false') { - validationFailuresRemaining--; - return HttpResponse.json( - { - type: 'https://steadybit.com/problems/experiment-invalid-exception', - title: - 'Had validation errors (lanes[0].steps[0].blastRadius.predicate: Please specify a query to select targets).', - status: 422, - instance: '/api/experiments/execute', - }, - { status: 422 } - ); - } - - const key = `NEW-${experimentSequence++}`; - const run = runSequence++; - experimentStore[key] = request.json(); - return HttpResponse.json( - { - key: key, - executionId: run, - apiLocation: `http://example.com/api/experiments/executions/${run}`, - uiLocation: `http://example.com/experiments/edit/${key}/executions/${run}?tenant=example&team=EXAMPLE`, - }, - { status: 201, headers: { location: `http://example.com/api/experiments/executions/${run}` } } - ); -}); - -const fetchAdviceHandler = http.post('http://example.com/api/advice', async ({ request }) => { - const body = (await request.json()) as FetchAdviceRequest; - if (body.query === 'mock.response=ok') { - const response: FetchAdviceResponse = { - totalItems: 1, - items: [ - { - target: { - reference: 'target-1-ref', - label: 'target-1', - type: 'host', - }, - advice: { - type: 'advice-type-1', - label: 'advice-1', - status: 'IMPLEMENTED', - }, - url: 'http://example.com/api/advice/1111', - }, - ], - }; - return HttpResponse.json(response); - } - if (body.offset === 0) { - const response: FetchAdviceResponse = { - nextOffset: 2, - totalItems: 3, - items: [ - { - target: { - reference: 'target-1-ref', - label: 'target-1', - type: 'host', - }, - advice: { - type: 'advice-type-1', - label: 'advice-1', - status: 'VALIDATION_NEEDED', - }, - url: 'http://example.com/api/advice/1111', - }, - { - target: { - reference: 'target-2-ref', - label: 'target-2', - type: 'host', - }, - advice: { - type: 'advice-type-2', - label: 'advice-2', - status: 'IMPLEMENTED', - }, - url: 'http://example.com/api/advice/2222', - }, - ], - }; - return HttpResponse.json(response); - } else { - const response: FetchAdviceResponse = { - totalItems: 3, - items: [ - { - target: { - reference: 'target-3-ref', - label: 'target-3', - type: 'host', - }, - advice: { - type: 'advice-type-3', - label: 'advice-3', - status: 'ACTION_NEEDED', - }, - url: 'http://example.com/api/advice/3333', - }, - ], - }; - return HttpResponse.json(response); - } -}); - -// The dump walk: teams, then each team's experiments, then each experiment's executions. -const getTeamsHandler = http.get('http://example.com/api/teams', () => - HttpResponse.json({ teams: [{ key: 'TST', name: 'Test Team' }] }) -); - -const listExperimentsHandler = http.get('http://example.com/api/experiments', ({ request }) => { - const team = new URL(request.url).searchParams.get('team'); - const experiments = Object.values(experimentStore) - .filter(experiment => experiment.team === team) - .map(experiment => ({ key: experiment.key, name: experiment.name })); - return HttpResponse.json({ experiments }); -}); - -const listExecutionsHandler = http.get('http://example.com/api/experiments/:key/executions', ({ params }) => - experimentStore[String(params.key)] - ? HttpResponse.json({ executions: executionsFor(String(params.key)) }) - : HttpResponse.json({ title: 'Not Found' }, { status: 404 }) -); - -const getExecutionHandler = http.get('http://example.com/api/experiments/executions/:id', ({ params }) => - unfetchableExecutions.has(Number(params.id)) - ? HttpResponse.json({ title: 'Server Error' }, { status: 500 }) - : HttpResponse.json({ id: Number(params.id), key: 'TST-1', state: 'COMPLETED' }) -); - -const getProblemHandler = http.get('http://example.com/api/problem', () => - HttpResponse.json({ type: 'https://steadybit.com/problems/another-experiment-running-exception' }, { status: 409 }) -); - -export const handlers = [ - getTeamsHandler, - listExecutionsHandler, - getExecutionHandler, - listExperimentsHandler, - getProblemHandler, - executeUpsertExperimentHandler, - executeExperimentHandler, - upsertExperimentHandler, - updateExperimentHandler, - deleteExperimentHandler, - getExperimentHandler, - fetchAdviceHandler, - getTooManyRequestsHandler, -]; diff --git a/src/mocks/prompts.ts b/src/mocks/prompts.ts deleted file mode 100644 index 529cb74..0000000 --- a/src/mocks/prompts.ts +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { screen } from '@inquirer/testing/vitest'; - -// A prompt is not always on screen the moment the call under test starts: confirm() -// reaches its prompt through a dynamic import, and a rejected answer re-renders the -// same question. Polling for the text covers both without the caller having to know -// which case it is in, and reports the screen it did see when the wait runs out. -export async function waitForPrompt(text: string, timeoutMillis = 4000): Promise { - const deadline = Date.now() + timeoutMillis; - while (Date.now() < deadline) { - if (screen.getScreen().includes(text)) { - return; - } - await new Promise(resolve => setTimeout(resolve, 5)); - } - throw new Error(`Prompt "${text}" never appeared. Last screen was:\n${screen.getScreen()}`); -} - -// Enter is a carriage return; readline does not submit on a newline. `replace` clears -// what is already in the field first, which a prompt keeps after rejecting an answer — -// without it a second attempt is appended to the first rather than replacing it. -export async function answerPrompt(prompt: string, answer: string, { replace = false } = {}): Promise { - await waitForPrompt(prompt); - if (replace) { - screen.input.write('\x7f'.repeat(64)); // backspace past anything already typed - } - screen.input.write(`${answer}\r`); -} - -export function pressCtrlC(): void { - // Written as an escape rather than the literal byte, which is invisible in an editor - // and reads as an empty string. - screen.input.write('\x03'); -} diff --git a/src/mocks/recorder.ts b/src/mocks/recorder.ts deleted file mode 100644 index db8517e..0000000 --- a/src/mocks/recorder.ts +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { http, HttpResponse, type JsonBodyType } from 'msw'; -import { server } from './server.ts'; - -export interface RecordedRequest { - method: string; - url: URL; - body: unknown; -} - -type Method = 'get' | 'post' | 'put' | 'patch' | 'delete'; - -// Answers one endpoint and keeps what was sent to it, for tests that assert on the -// request a command makes rather than only on what it prints. -export function respondTo( - method: Method, - path: string, - reply: ( - request: RecordedRequest - ) => Response | { status?: number; json?: JsonBodyType; headers?: Record } -): RecordedRequest[] { - const requests: RecordedRequest[] = []; - server.use( - http[method](`http://example.com${path}`, async ({ request }) => { - const text = await request.text(); - const recorded = { method: request.method, url: new URL(request.url), body: text ? JSON.parse(text) : undefined }; - requests.push(recorded); - const response = reply(recorded); - if (response instanceof Response) { - return response; - } - return response.json === undefined - ? new HttpResponse(null, { status: response.status ?? 200, headers: response.headers }) - : HttpResponse.json(response.json, { status: response.status ?? 200, headers: response.headers }); - }) - ); - return requests; -} diff --git a/src/mocks/server.ts b/src/mocks/server.ts deleted file mode 100644 index 9b12327..0000000 --- a/src/mocks/server.ts +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { setupServer } from 'msw/node'; -import { handlers } from './handlers.ts'; - -export const server = setupServer(...handlers); diff --git a/src/mocks/tempFiles.ts b/src/mocks/tempFiles.ts deleted file mode 100644 index f61ea01..0000000 --- a/src/mocks/tempFiles.ts +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import fs from 'node:fs/promises'; -import path from 'node:path'; -import os from 'node:os'; -import { dump } from '../yaml.ts'; -import type { Datatype } from '../experiment/files.ts'; - -let tempDir: string; - -export async function createTempDir() { - tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'steadybit-cli-test')); -} - -export async function writeFile(name: string, content: any, datatype: Datatype = 'yaml') { - const file = path.join(tempDir, name); - await fs.mkdir(path.dirname(file), { recursive: true }); - await fs.writeFile(file, datatype === 'json' ? JSON.stringify(content) : dump(content)); - return file; -} - -export function getTempDir() { - return tempDir; -} - -export async function removeTempDir() { - await fs.rm(tempDir, { recursive: true }); -} diff --git a/src/packageJson.ts b/src/packageJson.ts deleted file mode 100644 index 1be01e9..0000000 --- a/src/packageJson.ts +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { createRequire } from 'node:module'; - -interface PackageJson { - name: string; - version: string; - engines: { node: string }; -} - -// Read at runtime rather than imported, so that package.json stays outside of the -// TypeScript root directory and the compiled output keeps its flat dist/ layout. -export const packageJson: PackageJson = createRequire(import.meta.url)('../package.json'); diff --git a/src/prompt/cancellation.test.ts b/src/prompt/cancellation.test.ts deleted file mode 100644 index 51e4877..0000000 --- a/src/prompt/cancellation.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { wrapPrompt } from '@inquirer/testing/vitest'; -import { pressCtrlC, waitForPrompt } from '../mocks/prompts.ts'; -import { confirm } from './confirm.ts'; -import { cancelable } from './cancellation.ts'; - -vi.mock('@inquirer/confirm', async importOriginal => { - const actual = await importOriginal(); - return { ...actual, default: wrapPrompt(actual.default) }; -}); - -describe('cancelable', () => { - beforeEach(() => { - Object.defineProperty(process.stdout, 'isTTY', { value: true, configurable: true }); - }); - - afterEach(() => { - Object.defineProperty(process.stdout, 'isTTY', { value: undefined, configurable: true }); - vi.restoreAllMocks(); - }); - - // inquirer used to re-raise SIGINT; the @inquirer prompts reject instead, which would - // otherwise surface as an unhandled rejection with a stack trace. Previously this was - // only covered by throwing a hand-made ExitPromptError, never by a real cancellation. - it('should exit quietly with the conventional SIGINT status when the user cancels', async () => { - const exit = vi.spyOn(process, 'exit').mockImplementation((() => undefined) as never); - - // process.exit is stubbed, so cancelable() falls through to its rethrow instead of - // ending the process. In production it never gets that far. - void confirm('Run it?').catch(() => undefined); - await waitForPrompt('Run it?'); - pressCtrlC(); - - await vi.waitFor(() => expect(exit).toHaveBeenCalledWith(130)); - }); - - it('should let every other failure through untouched', async () => { - const boom = new Error('something else'); - - await expect(cancelable(Promise.reject(boom))).rejects.toBe(boom); - }); - - it('should pass a normal answer straight through', async () => { - await expect(cancelable(Promise.resolve('answered'))).resolves.toEqual('answered'); - }); -}); diff --git a/src/prompt/cancellation.ts b/src/prompt/cancellation.ts deleted file mode 100644 index cc28508..0000000 --- a/src/prompt/cancellation.ts +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -// inquirer used to re-raise SIGINT so that Ctrl+C terminated the CLI quietly. The -// @inquirer prompts instead reject with an ExitPromptError, which would otherwise -// surface as an unhandled rejection with a stack trace. This restores the quiet exit -// with the conventional SIGINT status. -const SIGINT_EXIT_CODE = 130; - -export async function cancelable(prompt: Promise): Promise { - try { - return await prompt; - } catch (e) { - if (e instanceof Error && e.name === 'ExitPromptError') { - process.exit(SIGINT_EXIT_CODE); - } - throw e; - } -} diff --git a/src/prompt/confirm.test.ts b/src/prompt/confirm.test.ts deleted file mode 100644 index 4395406..0000000 --- a/src/prompt/confirm.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { wrapPrompt } from '@inquirer/testing/vitest'; -import { answerPrompt, waitForPrompt } from '../mocks/prompts.ts'; -import { confirm } from './confirm.ts'; - -// confirm() reaches the prompt through a dynamic import, so the mock has to survive -// `await import(...)` rather than only a static one. -vi.mock('@inquirer/confirm', async importOriginal => { - const actual = await importOriginal(); - return { ...actual, default: wrapPrompt(actual.default) }; -}); - -describe('confirm', () => { - describe('without a terminal', () => { - it('should answer with the non-interactive default instead of prompting', async () => { - await expect(confirm('Run it?', { defaultWhenNonInteractive: false })).resolves.toBe(false); - await expect(confirm('Run it?', { defaultWhenNonInteractive: true })).resolves.toBe(true); - }); - }); - - describe('with a terminal', () => { - beforeEach(() => { - // The guard in confirm() decides whether to prompt at all. Faking it is the only - // way in-process; whether the guard reads the real terminal correctly is left to - // the container tests, which get a genuine tty from `docker run -t`. - Object.defineProperty(process.stdout, 'isTTY', { value: true, configurable: true }); - }); - - afterEach(() => { - Object.defineProperty(process.stdout, 'isTTY', { value: undefined, configurable: true }); - }); - - it('should take the answer the user gives', async () => { - const answer = confirm('Run it?', { defaultYes: false }); - - await answerPrompt('Run it?', 'y'); - - await expect(answer).resolves.toBe(true); - }); - - it('should offer the configured default', async () => { - const answer = confirm('Run it?', { defaultYes: false }); - - await waitForPrompt('(y/N)'); // the configured default is the one offered - await answerPrompt('Run it?', ''); - - await expect(answer).resolves.toBe(false); - }); - }); -}); diff --git a/src/prompt/confirm.ts b/src/prompt/confirm.ts deleted file mode 100644 index 8232ce9..0000000 --- a/src/prompt/confirm.ts +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { cancelable } from './cancellation.ts'; - -export interface ConfirmOptions { - defaultYes?: boolean; - defaultWhenNonInteractive?: boolean; -} - -export async function confirm( - message: string, - { defaultYes = true, defaultWhenNonInteractive = true }: ConfirmOptions = {} -): Promise { - if (!process.stdout.isTTY) { - return defaultWhenNonInteractive; - } - - // Loaded lazily so that non-interactive runs, the common case in CI, never pay - // the cost of importing the prompt implementation. - const { default: confirmPrompt } = await import('@inquirer/confirm'); - return await cancelable(confirmPrompt({ message, default: defaultYes })); -} diff --git a/src/prompt/validation.ts b/src/prompt/validation.ts deleted file mode 100644 index 63f61e1..0000000 --- a/src/prompt/validation.ts +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -export function validateNotBlank(input: string): boolean { - return input != null && input.trim().length > 0; -} - -export function validateHttpUrl(input: string): boolean | string { - try { - const url = new URL(input); - if (url.protocol !== 'http:' && url.protocol !== 'https:') { - return `Unsupported protocol ${url.protocol}. Only http: and https: are supported.`; - } - return true; - } catch { - return 'Invalid URL. Please specify an absolute URL.'; - } -} diff --git a/src/schedule/api.ts b/src/schedule/api.ts deleted file mode 100644 index d866cbf..0000000 --- a/src/schedule/api.ts +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { Schemas } from '../api/schemas.ts'; -import { ApiError } from '../api/error.ts'; -import { executeApiCall } from '../api/http.ts'; -import { abortExecution, abortExecutionWithError } from '../errors.ts'; - -export type Schedule = Schemas['ExperimentScheduleAO']; -export type UpsertSchedule = Schemas['UpsertExperimentScheduleAO']; -export type PatchSchedule = Schemas['PatchExperimentScheduleAO']; - -function notFoundOr(e: unknown, id: string, msg: string): Error { - if (e instanceof ApiError && e.status === 404) { - return abortExecution('Experiment schedule %s not found.', id); - } - return abortExecutionWithError(e, msg, id); -} - -export async function fetchSchedules(filter: { team?: string[]; experiment?: string[] }): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: '/api/experiments/schedules/v2', - queryParameters: { team: filter.team, experiment: filter.experiment }, - }); - return (await response.json()) as Schedule[]; - } catch (e) { - throw abortExecutionWithError(e, 'Failed to get the experiment schedules'); - } -} - -export async function fetchSchedule(id: string): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: `/api/experiments/schedules/${encodeURIComponent(id)}`, - }); - return (await response.json()) as Schedule; - } catch (e) { - throw notFoundOr(e, id, 'Failed to get experiment schedule %s'); - } -} - -export async function upsertSchedule(schedule: UpsertSchedule): Promise<{ created: boolean; schedule: Schedule }> { - try { - const response = await executeApiCall({ method: 'POST', path: '/api/experiments/schedules', body: schedule }); - return { created: response.status === 201, schedule: (await response.json()) as Schedule }; - } catch (e) { - throw abortExecutionWithError(e, 'Failed to save the experiment schedule for %s', schedule.experimentKey); - } -} - -export async function patchSchedule(id: string, patch: PatchSchedule): Promise { - try { - const response = await executeApiCall({ - method: 'PATCH', - path: `/api/experiments/schedules/${encodeURIComponent(id)}`, - body: patch, - }); - return (await response.json()) as Schedule; - } catch (e) { - throw notFoundOr(e, id, 'Failed to update experiment schedule %s'); - } -} - -export async function removeSchedule(id: string): Promise { - try { - await executeApiCall({ method: 'DELETE', path: `/api/experiments/schedules/${encodeURIComponent(id)}` }); - } catch (e) { - throw notFoundOr(e, id, 'Failed to delete experiment schedule %s'); - } -} diff --git a/src/schedule/commands.ts b/src/schedule/commands.ts deleted file mode 100644 index cb3f774..0000000 --- a/src/schedule/commands.ts +++ /dev/null @@ -1,185 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import { createTable } from '../table.ts'; -import { abortExecution } from '../errors.ts'; -import { format, output, readStructuredFile } from '../structuredFiles.ts'; -import { resolveExperimentFiles } from '../experiment/files.ts'; -import { - fetchSchedule, - fetchSchedules, - patchSchedule, - type PatchSchedule, - removeSchedule, - type Schedule, - type UpsertSchedule, - upsertSchedule, -} from './api.ts'; - -export interface ListOptions { - team?: string[]; - experiment?: string[]; -} - -export async function listSchedules(options: ListOptions) { - const schedules = await fetchSchedules(options); - if (schedules.length === 0) { - console.log('No experiment schedules found.'); - return; - } - const table = createTable({ - columns: [ - { name: 'id', title: 'Id', alignment: 'left' }, - { name: 'experiment', title: 'Experiment', alignment: 'left' }, - { name: 'when', title: 'When', alignment: 'left' }, - { name: 'enabled', title: 'Enabled', alignment: 'left' }, - { name: 'allowParallel', title: 'Parallel', alignment: 'left' }, - ], - }); - table.addRows( - schedules.map(s => ({ - id: s.id, - experiment: s.experimentKey, - when: s.cron ? `${s.cron}${s.timezone ? ` (${s.timezone})` : ''}` : (s.startAt ?? ''), - enabled: String(s.enabled ?? true), - allowParallel: String(s.allowParallel ?? true), - })) - ); - table.printTable(); -} - -export interface GetOptions { - id: string; - file?: string; - type?: string; -} - -export async function getSchedule(options: GetOptions) { - await output(toFileContent(await fetchSchedule(options.id)), options); - if (options.file) { - console.log('Experiment schedule %s written to %s.', options.id, options.file); - } -} - -// What the platform reports about the last edit and the next run is not part of what -// can be sent, so it is left out of files, keeping `get` followed by `apply` a round trip. -function toFileContent(schedule: Schedule): UpsertSchedule { - const content: Partial = { ...schedule }; - delete content.editedBy; - delete content.lastUpdated; - delete content.nextExecution; - return content as UpsertSchedule; -} - -export interface ApplyOptions { - file: string[]; - recursive: boolean; -} - -export async function applySchedules(options: ApplyOptions) { - const files = await resolveExperimentFiles(options.file, options.recursive); - for (const file of files) { - const { content, datatype } = await readStructuredFile(file, 'schedule'); - if (!content?.experimentKey) { - throw abortExecution("Schedule file '%s' does not name an experimentKey.", file); - } - const result = await upsertSchedule(toFileContent(content)); - if (!content.id) { - // As `experiment apply` does with the key: the next apply of this file then - // updates the schedule instead of creating a second one. - // The id goes first in the file, where people look, even if the file had an empty one. - const withId = Object.assign({ id: result.schedule.id }, content, { id: result.schedule.id }); - await fs.writeFile(file, format(withId, datatype), { encoding: 'utf8' }); - } - console.log( - 'Experiment schedule %s for %s %s.', - result.schedule.id, - result.schedule.experimentKey, - result.created ? 'created' : 'updated' - ); - } -} - -export interface ScheduleFields { - cron?: string; - startAt?: string; - timezone?: string; - allowParallel?: boolean; - variable?: Record; -} - -export interface CreateOptions extends ScheduleFields { - experiment: string; - disabled?: boolean; -} - -export async function createSchedule(options: CreateOptions) { - rejectCronWithStartAt(options); - requireOneOfCronOrStartAt(options); - const { schedule } = await upsertSchedule({ - experimentKey: options.experiment, - cron: options.cron, - startAt: options.startAt, - timezone: options.timezone, - allowParallel: options.allowParallel, - enabled: !options.disabled, - variables: options.variable, - }); - console.log('Experiment schedule %s for %s created.', schedule.id, schedule.experimentKey); -} - -function requireOneOfCronOrStartAt(options: ScheduleFields) { - if (!options.cron && !options.startAt) { - throw abortExecution('Either --cron or --start-at must be specified.'); - } -} - -// A schedule either repeats or runs once. The platform rejects both together on create, -// and on update would clear whichever it applied first. -function rejectCronWithStartAt(options: ScheduleFields) { - if (options.cron && options.startAt) { - throw abortExecution('--cron and --start-at cannot be combined.'); - } -} - -export interface UpdateOptions extends ScheduleFields { - id: string; -} - -export async function updateSchedule(options: UpdateOptions) { - rejectCronWithStartAt(options); - const patch: PatchSchedule = { - cron: options.cron, - startAt: options.startAt, - timezone: options.timezone, - allowParallel: options.allowParallel, - variables: options.variable, - }; - if (Object.values(patch).every(value => value === undefined)) { - throw abortExecution('Nothing to update. Pass at least one of the options, see --help.'); - } - await patchAndReport(options.id, patch, 'updated'); -} - -export interface IdOptions { - id: string; -} - -export async function enableSchedule(options: IdOptions) { - await patchAndReport(options.id, { enabled: true }, 'enabled'); -} - -export async function disableSchedule(options: IdOptions) { - await patchAndReport(options.id, { enabled: false }, 'disabled'); -} - -async function patchAndReport(id: string, patch: PatchSchedule, outcome: string) { - const schedule = await patchSchedule(id, patch); - console.log('Experiment schedule %s for %s %s.', id, schedule.experimentKey, outcome); -} - -export async function deleteSchedule(options: IdOptions) { - await removeSchedule(options.id); - console.log('Experiment schedule %s deleted.', options.id); -} diff --git a/src/schedule/schedule.test.ts b/src/schedule/schedule.test.ts deleted file mode 100644 index 3faa714..0000000 --- a/src/schedule/schedule.test.ts +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import { describe, expect, it, vi } from 'vitest'; -import { respondTo } from '../mocks/recorder.ts'; -import { writeFile } from '../mocks/tempFiles.ts'; -import { load } from '../yaml.ts'; -import { - applySchedules, - createSchedule, - deleteSchedule, - disableSchedule, - enableSchedule, - getSchedule, - listSchedules, - updateSchedule, -} from './commands.ts'; - -const ID = '01951394-727f-76a0-8675-c7519ebd0ff5'; -const SCHEDULE = { - id: ID, - experimentKey: 'ADM-1', - cron: '0 0 9 ? * MON-FRI', - timezone: 'Europe/Berlin', - enabled: true, - allowParallel: false, - editedBy: { username: 'jane' }, - lastUpdated: '2026-09-01T10:00:00Z', - nextExecution: '2026-09-02T07:00:00Z', -}; - -describe('schedule', () => { - describe('list', () => { - it('filters by teams and experiments with repeated parameters', async () => { - const requests = respondTo('get', '/api/experiments/schedules/v2', () => ({ json: [SCHEDULE] })); - const logSpy = vi.spyOn(console, 'log'); - - await listSchedules({ team: ['ADM', 'OPS'], experiment: ['ADM-1'] }); - - expect(requests[0].url.searchParams.getAll('team')).toEqual(['ADM', 'OPS']); - expect(requests[0].url.searchParams.getAll('experiment')).toEqual(['ADM-1']); - const printed = logSpy.mock.calls.flat().join('\n'); - expect(printed).toContain(ID); - expect(printed).toContain('0 0 9 ? * MON-FRI (Europe/Berlin)'); - }); - - it('sends no filter when none is given', async () => { - const requests = respondTo('get', '/api/experiments/schedules/v2', () => ({ json: [] })); - const logSpy = vi.spyOn(console, 'log'); - - await listSchedules({}); - - expect(requests[0].url.search).toBe(''); - expect(logSpy).toHaveBeenCalledWith('No experiment schedules found.'); - }); - }); - - describe('get', () => { - it('leaves out the read-only fields so the file can be applied again', async () => { - respondTo('get', `/api/experiments/schedules/${ID}`, () => ({ json: SCHEDULE })); - const file = await writeFile('schedule.yml', {}); - - await getSchedule({ id: ID, file }); - - const expected: Partial = { ...SCHEDULE }; - delete expected.editedBy; - delete expected.lastUpdated; - delete expected.nextExecution; - expect(load(await fs.readFile(file, 'utf8'))).toEqual(expected); - }); - - it('reports a schedule that does not exist', async () => { - respondTo('get', '/api/experiments/schedules/nope', () => ({ status: 404 })); - - await expect(getSchedule({ id: 'nope' })).rejects.toThrow('Experiment schedule nope not found.'); - }); - }); - - describe('apply', () => { - it('creates a schedule and writes its id back into the file', async () => { - const requests = respondTo('post', '/api/experiments/schedules', () => ({ status: 201, json: SCHEDULE })); - const file = await writeFile('new-schedule.yml', { experimentKey: 'ADM-1', cron: '0 0 9 ? * MON-FRI' }); - const logSpy = vi.spyOn(console, 'log'); - - await applySchedules({ file: [file], recursive: false }); - - expect(requests[0].body).toEqual({ experimentKey: 'ADM-1', cron: '0 0 9 ? * MON-FRI' }); - expect(load(await fs.readFile(file, 'utf8'))).toEqual({ - id: ID, - experimentKey: 'ADM-1', - cron: '0 0 9 ? * MON-FRI', - }); - expect(logSpy).toHaveBeenCalledWith('Experiment schedule %s for %s %s.', ID, 'ADM-1', 'created'); - }); - - it('updates a schedule that has an id without touching the file', async () => { - const requests = respondTo('post', '/api/experiments/schedules', () => ({ status: 200, json: SCHEDULE })); - const file = await writeFile('schedule.json', SCHEDULE, 'json'); - const before = await fs.readFile(file, 'utf8'); - - await applySchedules({ file: [file], recursive: false }); - - expect(requests[0].body).not.toHaveProperty('editedBy'); - expect(requests[0].body).not.toHaveProperty('nextExecution'); - expect(requests[0].body).toHaveProperty('id', ID); - expect(await fs.readFile(file, 'utf8')).toBe(before); - }); - - it('rejects a file without an experiment key', async () => { - const file = await writeFile('broken.yml', { cron: '* * * ? * *' }); - - await expect(applySchedules({ file: [file], recursive: false })).rejects.toThrow( - 'does not name an experimentKey' - ); - }); - }); - - describe('create', () => { - it('creates a schedule from flags', async () => { - const requests = respondTo('post', '/api/experiments/schedules', () => ({ status: 201, json: SCHEDULE })); - - await createSchedule({ - experiment: 'ADM-1', - cron: '0 0 9 ? * MON-FRI', - timezone: 'Europe/Berlin', - allowParallel: false, - variable: { region: 'eu' }, - }); - - expect(requests[0].body).toEqual({ - experimentKey: 'ADM-1', - cron: '0 0 9 ? * MON-FRI', - timezone: 'Europe/Berlin', - allowParallel: false, - enabled: true, - variables: { region: 'eu' }, - }); - }); - - it('creates a disabled one-off schedule', async () => { - const requests = respondTo('post', '/api/experiments/schedules', () => ({ status: 201, json: SCHEDULE })); - - await createSchedule({ experiment: 'ADM-1', startAt: '2026-10-01T09:00:00Z', disabled: true }); - - expect(requests[0].body).toEqual({ experimentKey: 'ADM-1', startAt: '2026-10-01T09:00:00Z', enabled: false }); - }); - - it('needs either --cron or --start-at, not both', async () => { - await expect(createSchedule({ experiment: 'ADM-1' })).rejects.toThrow( - 'Either --cron or --start-at must be specified.' - ); - await expect(createSchedule({ experiment: 'ADM-1', cron: 'x', startAt: 'y' })).rejects.toThrow( - '--cron and --start-at cannot be combined.' - ); - }); - }); - - describe('update', () => { - it('patches only the given fields', async () => { - const requests = respondTo('patch', `/api/experiments/schedules/${ID}`, () => ({ json: SCHEDULE })); - - await updateSchedule({ id: ID, cron: '0 30 8 ? * *' }); - - expect(requests[0].body).toEqual({ cron: '0 30 8 ? * *' }); - }); - - it('refuses an update without changes', async () => { - await expect(updateSchedule({ id: ID })).rejects.toThrow('Nothing to update.'); - }); - - it('enables and disables', async () => { - const requests = respondTo('patch', `/api/experiments/schedules/${ID}`, () => ({ json: SCHEDULE })); - const logSpy = vi.spyOn(console, 'log'); - - await disableSchedule({ id: ID }); - await enableSchedule({ id: ID }); - - expect(requests.map(r => r.body)).toEqual([{ enabled: false }, { enabled: true }]); - expect(logSpy).toHaveBeenCalledWith('Experiment schedule %s for %s %s.', ID, 'ADM-1', 'disabled'); - }); - }); - - describe('delete', () => { - it('deletes a schedule', async () => { - const requests = respondTo('delete', `/api/experiments/schedules/${ID}`, () => ({})); - const logSpy = vi.spyOn(console, 'log'); - - await deleteSchedule({ id: ID }); - - expect(requests).toHaveLength(1); - expect(logSpy).toHaveBeenCalledWith('Experiment schedule %s deleted.', ID); - }); - - it('reports a schedule that does not exist', async () => { - respondTo('delete', '/api/experiments/schedules/nope', () => ({ status: 404 })); - - await expect(deleteSchedule({ id: 'nope' })).rejects.toThrow('Experiment schedule nope not found.'); - }); - }); -}); diff --git a/src/service/api.ts b/src/service/api.ts deleted file mode 100644 index a9577d5..0000000 --- a/src/service/api.ts +++ /dev/null @@ -1,180 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { Schemas } from '../api/schemas.ts'; -import { ApiError } from '../api/error.ts'; -import { executeApiCall } from '../api/http.ts'; -import { fetchAllPages } from '../api/paging.ts'; -import { abortExecution, abortExecutionWithError } from '../errors.ts'; -import { refusedForProvidedExperiments } from './refusal.ts'; - -export type Service = Schemas['ServiceAO']; -export type UpsertService = Schemas['UpsertServiceAO']; -export type ServiceSummary = Schemas['ServiceSummaryAO']; -export type ServiceExperiment = Schemas['ServiceExperimentAO']; -export type ServiceRisk = Schemas['ServiceRiskAO']; -export type Variables = NonNullable; - -function notFoundOr(e: unknown, id: string, msg: string): Error { - if (e instanceof ApiError && e.status === 404) { - return abortExecution('Service %s not found.', id); - } - return abortExecutionWithError(e, msg, id); -} - -const servicePath = (id: string) => `/api/services/${encodeURIComponent(id)}`; - -export interface ServiceFilter { - team?: string[]; - environment?: string[]; - experiment?: string[]; -} - -export async function fetchServices(filter: ServiceFilter): Promise { - try { - return await fetchAllPages('/api/services', { - teamKey: filter.team, - environmentName: filter.environment, - experimentKey: filter.experiment, - }); - } catch (e) { - throw abortExecutionWithError(e, 'Failed to get the services'); - } -} - -export async function fetchService(id: string): Promise { - try { - return (await (await executeApiCall({ method: 'GET', path: servicePath(id) })).json()) as Service; - } catch (e) { - throw notFoundOr(e, id, 'Failed to get service %s'); - } -} - -export async function upsertService( - service: UpsertService, - deleteExperiments: boolean -): Promise<{ created: boolean; service: Service }> { - try { - const response = await executeApiCall({ - method: 'POST', - path: '/api/services', - queryParameters: { deleteExperiments: String(deleteExperiments) }, - body: service, - }); - return { created: response.status === 201, service: (await response.json()) as Service }; - } catch (e) { - if (!deleteExperiments && refusedForProvidedExperiments(e)) { - throw abortExecution( - 'Service %s was not saved: the change would remove provided experiments. Pass --delete-experiments to delete them.', - service.name - ); - } - throw abortExecutionWithError(e, 'Failed to save service %s', service.name); - } -} - -export async function removeService(id: string): Promise { - try { - await executeApiCall({ method: 'DELETE', path: servicePath(id) }); - } catch (e) { - throw notFoundOr(e, id, 'Failed to delete service %s'); - } -} - -export async function fetchServiceRisk(id: string): Promise { - try { - return (await (await executeApiCall({ method: 'GET', path: `${servicePath(id)}/risk` })).json()) as ServiceRisk; - } catch (e) { - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Service %s not found, or its risk has not been calculated yet.', id); - } - throw abortExecutionWithError(e, 'Failed to get the risk of service %s', id); - } -} - -export interface ServiceExperimentFilter { - category?: string[]; - type?: string[]; -} - -export async function fetchServiceExperiments( - id: string, - filter: ServiceExperimentFilter -): Promise { - try { - return await fetchAllPages(`${servicePath(id)}/experiments`, { - category: filter.category, - type: filter.type, - }); - } catch (e) { - throw notFoundOr(e, id, 'Failed to get the experiments of service %s'); - } -} - -export async function linkCustomExperiment(id: string, experimentKey: string, category: string): Promise { - try { - await executeApiCall({ - method: 'POST', - path: `${servicePath(id)}/experiments/custom`, - body: { experimentKey, category } satisfies Schemas['LinkCustomExperimentRequestAO'], - }); - } catch (e) { - throw notFoundOr(e, id, `Failed to link experiment ${experimentKey} to service %s`); - } -} - -export async function unlinkCustomExperiment(id: string, experimentKey: string): Promise { - try { - await executeApiCall({ - method: 'DELETE', - path: `${servicePath(id)}/experiments/custom`, - queryParameters: { experimentKey }, - }); - } catch (e) { - throw notFoundOr(e, id, `Failed to unlink experiment ${experimentKey} from service %s`); - } -} - -// Like a create from a template, the platform names the experiment in the Location -// header and sends no body. -export async function upsertProvidedExperiment( - id: string, - request: Schemas['UpsertProvidedExperimentRequestAO'], - resetProperties: boolean -): Promise<{ created: boolean; key?: string }> { - try { - const response = await executeApiCall({ - method: 'POST', - path: `${servicePath(id)}/experiments/provided`, - queryParameters: { resetProperties: String(resetProperties) }, - body: request, - }); - const location = response.headers.get('Location'); - return { - created: response.status === 201, - key: location ? location.substring(location.lastIndexOf('/') + 1) : (request.experimentKey ?? undefined), - }; - } catch (e) { - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Service %s or experiment template %s not found.', id, request.templateId); - } - throw abortExecutionWithError(e, 'Failed to save the provided experiment of service %s', id); - } -} - -export async function fetchServiceVariables(id: string): Promise { - try { - return (await (await executeApiCall({ method: 'GET', path: `${servicePath(id)}/variables` })).json()) as Variables; - } catch (e) { - throw notFoundOr(e, id, 'Failed to get the variables of service %s'); - } -} - -// PUT replaces every variable of the service, PATCH merges the given ones in. -export async function writeServiceVariables(id: string, variables: Variables, replace: boolean): Promise { - try { - await executeApiCall({ method: replace ? 'PUT' : 'PATCH', path: `${servicePath(id)}/variables`, body: variables }); - } catch (e) { - throw notFoundOr(e, id, 'Failed to update the variables of service %s'); - } -} diff --git a/src/service/commands.ts b/src/service/commands.ts deleted file mode 100644 index ab11cf3..0000000 --- a/src/service/commands.ts +++ /dev/null @@ -1,289 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import { abortExecution } from '../errors.ts'; -import { format, output, readStructuredFile } from '../structuredFiles.ts'; -import { resolveExperimentFiles } from '../experiment/files.ts'; -import { resolvePlaceholders } from '../experiment/template.ts'; -import { createTable } from '../table.ts'; -import { - fetchService, - fetchServiceExperiments, - fetchServiceRisk, - fetchServices, - fetchServiceVariables, - linkCustomExperiment, - removeService, - type Service, - type ServiceExperimentFilter, - type ServiceFilter, - unlinkCustomExperiment, - type UpsertService, - upsertProvidedExperiment, - upsertService, - type Variables, - writeServiceVariables, -} from './api.ts'; - -export async function listServices(options: ServiceFilter) { - const services = await fetchServices(options); - if (services.length === 0) { - console.log('No services found.'); - return; - } - const table = createTable({ - columns: [ - { name: 'id', title: 'Id', alignment: 'left' }, - { name: 'name', title: 'Name', alignment: 'left' }, - { name: 'team', title: 'Team', alignment: 'left' }, - { name: 'environment', title: 'Environment', alignment: 'left' }, - ], - }); - table.addRows(services.map(s => ({ id: s.id, name: s.name, team: s.team, environment: s.environment }))); - table.printTable(); -} - -export interface GetOptions { - id: string; - file?: string; - type?: string; -} - -export async function getService(options: GetOptions) { - await output(toFileContent(await fetchService(options.id)), options); - if (options.file) { - console.log('Service %s written to %s.', options.id, options.file); - } -} - -// Who created and edited the service is not part of what can be sent. The version is -// left out as `experiment get` does: kept in a file, it turns every apply after a change -// made in the UI into a conflict. -function toFileContent(service: Service): UpsertService { - const content: Partial = { ...service }; - delete content.created; - delete content.createdBy; - delete content.edited; - delete content.editedBy; - delete content.version; - return content as UpsertService; -} - -export interface ApplyOptions { - file: string[]; - recursive: boolean; - deleteExperiments: boolean; -} - -export async function applyServices(options: ApplyOptions) { - const files = await resolveExperimentFiles(options.file, options.recursive); - for (const file of files) { - const { content, datatype } = await readStructuredFile(file, 'service'); - if (!content?.name) { - throw abortExecution("Service file '%s' does not name the service.", file); - } - const result = await upsertService(toFileContent(content), options.deleteExperiments); - if (!content.id) { - // The next apply of the file then updates this service instead of creating another. - const withId = Object.assign({ id: result.service.id }, content, { id: result.service.id }); - await fs.writeFile(file, format(withId, datatype), { encoding: 'utf8' }); - } - console.log('Service %s (%s) %s.', result.service.name, result.service.id, result.created ? 'created' : 'updated'); - } -} - -export interface IdOptions { - id: string; -} - -export async function deleteService(options: IdOptions) { - await removeService(options.id); - console.log('Service %s deleted.', options.id); -} - -export interface RiskOptions { - id: string; - type?: string; - failAbove?: number; -} - -export async function showServiceRisk(options: RiskOptions) { - const risk = await fetchServiceRisk(options.id); - - if (options.type) { - await output(risk, { type: options.type }); - } else { - console.log( - 'Risk of service %s: %s (calculated %s)', - options.id, - risk.risk ?? 'unknown', - risk.lastCalculated ?? 'never' - ); - const categories = Object.entries(risk.categoryRisks ?? {}); - if (categories.length > 0) { - const table = createTable({ - columns: [ - { name: 'category', title: 'Category', alignment: 'left' }, - { name: 'total', title: 'Total' }, - { name: 'experiment', title: 'Experiments' }, - { name: 'advice', title: 'Advice' }, - ], - }); - table.addRows( - categories.map(([category, r]) => ({ category, total: r.total, experiment: r.experiment, advice: r.advice })) - ); - table.printTable(); - } - if ((risk.experimentRisks ?? []).length > 0) { - const table = createTable({ - columns: [ - { name: 'experimentKey', title: 'Experiment', alignment: 'left' }, - { name: 'risk', title: 'Risk' }, - ], - }); - table.addRows(risk.experimentRisks ?? []); - table.printTable(); - } - } - - // Lets a pipeline stop a rollout of a service whose risk is too high, the way - // `advice validate-status` does for advice. - if (options.failAbove !== undefined && (risk.risk === undefined || risk.risk > options.failAbove)) { - throw abortExecution( - 'Risk of service %s is %s, above the accepted %d.', - options.id, - risk.risk ?? 'unknown', - options.failAbove - ); - } -} - -export interface ExperimentListOptions extends ServiceExperimentFilter { - id: string; -} - -export async function listServiceExperiments(options: ExperimentListOptions) { - const experiments = await fetchServiceExperiments(options.id, { - category: options.category, - type: options.type?.map(t => t.toUpperCase()), - }); - if (experiments.length === 0) { - const filtered = (options.category?.length ?? 0) > 0 || (options.type?.length ?? 0) > 0; - console.log(filtered ? 'Service %s has no matching experiments.' : 'Service %s has no experiments.', options.id); - return; - } - const table = createTable({ - columns: [ - { name: 'category', title: 'Category', alignment: 'left' }, - { name: 'associationType', title: 'Type', alignment: 'left' }, - // A provided experiment that has not been created yet has no key, only the - // template it would be created from. - { name: 'experimentKey', title: 'Experiment', alignment: 'left' }, - { name: 'templateId', title: 'Template', alignment: 'left' }, - ], - }); - table.addRows( - experiments.map(e => ({ - category: e.category, - associationType: e.associationType, - experimentKey: e.experimentKey ?? '(not created)', - templateId: e.templateId ?? '', - })) - ); - table.printTable(); -} - -export interface LinkOptions { - id: string; - experiment: string; - category: string; -} - -export async function linkExperiment(options: LinkOptions) { - await linkCustomExperiment(options.id, options.experiment, options.category); - console.log('Experiment %s linked to service %s in category %s.', options.experiment, options.id, options.category); -} - -export interface UnlinkOptions { - id: string; - experiment: string; -} - -export async function unlinkExperiment(options: UnlinkOptions) { - await unlinkCustomExperiment(options.id, options.experiment); - console.log('Experiment %s unlinked from service %s.', options.experiment, options.id); -} - -export interface ProvideOptions { - id: string; - template: string; - experiment?: string; - placeholder?: Record; - placeholders?: string; - resetProperties: boolean; -} - -export async function provideExperiment(options: ProvideOptions) { - const placeholders = await resolvePlaceholders(options); - const result = await upsertProvidedExperiment( - options.id, - { templateId: options.template, experimentKey: options.experiment, placeholders }, - options.resetProperties - ); - console.log( - 'Provided experiment %s of service %s %s from template %s.', - result.key ?? '', - options.id, - result.created ? 'created' : 'updated', - options.template - ); -} - -export interface VariableGetOptions { - id: string; - type?: string; -} - -export async function getServiceVariables(options: VariableGetOptions) { - await output(await fetchServiceVariables(options.id), { type: options.type }); -} - -export interface VariableSetOptions { - id: string; - file?: string; - replace: boolean; -} - -// Values from a file may be lists or select expressions; KEY=VALUE arguments are always -// plain strings and override entries of the file. -export async function setServiceVariables(pairs: string[], options: VariableSetOptions) { - const given: Record = {}; - for (const pair of pairs) { - const separator = pair.indexOf('='); - if (separator <= 0) { - throw abortExecution("'%s' is not in the form KEY=VALUE.", pair); - } - given[pair.slice(0, separator)] = pair.slice(separator + 1); - } - - let variables: Variables = {}; - if (options.file) { - const { content } = await readStructuredFile(options.file, 'variables'); - if (!content || typeof content !== 'object' || Array.isArray(content)) { - throw abortExecution("Variables file '%s' must be a map of variable names to values.", options.file); - } - variables = content; - } - variables = { ...variables, ...given }; - if (Object.keys(variables).length === 0 && !options.replace) { - throw abortExecution('No variables given. Pass KEY=VALUE arguments or --file.'); - } - await writeServiceVariables(options.id, variables, options.replace); - console.log( - '%d variable(s) of service %s %s.', - Object.keys(variables).length, - options.id, - options.replace ? 'set, all others removed' : 'set' - ); -} diff --git a/src/service/refusal.ts b/src/service/refusal.ts deleted file mode 100644 index 849ea60..0000000 --- a/src/service/refusal.ts +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { ApiError } from '../api/error.ts'; -import { getExecutionErrorBody } from '../errors.ts'; - -interface Violations { - violations?: { message?: string }[]; -} - -// The platform refuses a change to a service or profile that would orphan provided -// experiments, and names its query parameter in the reason. Recognised here so the -// user is pointed at the CLI flag instead. -export function refusedForProvidedExperiments(e: unknown): boolean { - return ( - e instanceof ApiError && - e.status === 422 && - (getExecutionErrorBody(e)?.violations ?? []).some(v => v.message?.includes('deleteExperiments')) - ); -} diff --git a/src/service/service.test.ts b/src/service/service.test.ts deleted file mode 100644 index cb047f7..0000000 --- a/src/service/service.test.ts +++ /dev/null @@ -1,245 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import { describe, expect, it, vi } from 'vitest'; -import { respondTo } from '../mocks/recorder.ts'; -import { writeFile } from '../mocks/tempFiles.ts'; -import { load } from '../yaml.ts'; -import { - applyServices, - deleteService, - getService, - getServiceVariables, - linkExperiment, - listServiceExperiments, - listServices, - provideExperiment, - setServiceVariables, - showServiceRisk, - unlinkExperiment, -} from './commands.ts'; - -const ID = '019cd80d-a4c9-775b-bdf8-2672a280ce7c'; -const SERVICE = { - id: ID, - name: 'Checkout', - environment: 'Global', - team: 'ADM', - logoId: 'service', - logoColor: 'blue', - query: 'k8s.namespace="shop"', - validations: [], - serviceProfile: 'Steadybit Starter', - properties: {}, - variables: { region: 'eu' }, - version: 3, - created: '2026-07-06T07:22:09Z', - createdBy: { username: 'jane' }, - edited: '2026-07-06T07:22:09Z', - editedBy: { username: 'jane' }, -}; - -const printed = (spy: ReturnType) => spy.mock.calls.flat().join('\n'); - -describe('service', () => { - it('lists services across pages with filters', async () => { - const requests = respondTo('get', '/api/services', ({ url }) => - url.searchParams.get('page') === '0' - ? { json: { items: [{ id: 'a', name: 'Checkout', team: 'ADM', environment: 'Global' }], nextPage: 1 } } - : { json: { items: [{ id: 'b', name: 'Catalog', team: 'ADM', environment: 'Global' }] } } - ); - const logSpy = vi.spyOn(console, 'log'); - - await listServices({ team: ['ADM'], environment: ['Global'] }); - - expect(requests[0].url.searchParams.getAll('teamKey')).toEqual(['ADM']); - expect(requests[0].url.searchParams.getAll('environmentName')).toEqual(['Global']); - expect(printed(logSpy)).toContain('Checkout'); - expect(printed(logSpy)).toContain('Catalog'); - }); - - it('writes a service without the fields apply cannot send', async () => { - respondTo('get', `/api/services/${ID}`, () => ({ json: SERVICE })); - const file = await writeFile('service.yml', {}); - - await getService({ id: ID, file }); - - const written = load(await fs.readFile(file, 'utf8')) as Record; - expect(written).toMatchObject({ id: ID, name: 'Checkout', variables: { region: 'eu' } }); - for (const field of ['version', 'created', 'createdBy', 'edited', 'editedBy']) { - expect(written).not.toHaveProperty(field); - } - }); - - it('reports a service that does not exist', async () => { - respondTo('get', '/api/services/nope', () => ({ status: 404 })); - - await expect(getService({ id: 'nope' })).rejects.toThrow('Service nope not found.'); - }); - - it('creates a service from a file and writes the id back', async () => { - const requests = respondTo('post', '/api/services', () => ({ status: 201, json: SERVICE })); - const withoutId: Partial = { ...SERVICE }; - delete withoutId.id; - delete withoutId.version; - const file = await writeFile('new-service.yml', withoutId); - const logSpy = vi.spyOn(console, 'log'); - - await applyServices({ file: [file], recursive: false, deleteExperiments: true }); - - expect(requests[0].url.searchParams.get('deleteExperiments')).toBe('true'); - expect(requests[0].body).not.toHaveProperty('createdBy'); - expect(requests[0].body).not.toHaveProperty('version'); - expect((load(await fs.readFile(file, 'utf8')) as Record).id).toBe(ID); - expect(logSpy).toHaveBeenCalledWith('Service %s (%s) %s.', 'Checkout', ID, 'created'); - }); - - it('rejects a service file without a name', async () => { - const file = await writeFile('nameless.yml', { team: 'ADM' }); - - await expect(applyServices({ file: [file], recursive: false, deleteExperiments: false })).rejects.toThrow( - 'does not name the service' - ); - }); - - it('deletes a service', async () => { - const requests = respondTo('delete', `/api/services/${ID}`, () => ({})); - - await deleteService({ id: ID }); - - expect(requests).toHaveLength(1); - }); - - describe('risk', () => { - const RISK = { - risk: 42, - categoryRisks: { Redundancy: { total: 40, experiment: 50, advice: 30 } }, - experimentRisks: [{ experimentKey: 'ADM-1', risk: 60 }], - lastCalculated: '2026-09-25T09:03:46Z', - }; - - it('prints the overall, category and experiment risks', async () => { - respondTo('get', `/api/services/${ID}/risk`, () => ({ json: RISK })); - const logSpy = vi.spyOn(console, 'log'); - - await showServiceRisk({ id: ID }); - - expect(logSpy).toHaveBeenCalledWith('Risk of service %s: %s (calculated %s)', ID, 42, RISK.lastCalculated); - expect(printed(logSpy)).toContain('Redundancy'); - expect(printed(logSpy)).toContain('ADM-1'); - }); - - it('passes at or below the accepted risk and fails above it', async () => { - respondTo('get', `/api/services/${ID}/risk`, () => ({ json: RISK })); - - await expect(showServiceRisk({ id: ID, failAbove: 42, type: 'json' })).resolves.toBeUndefined(); - await expect(showServiceRisk({ id: ID, failAbove: 41, type: 'json' })).rejects.toThrow( - `Risk of service ${ID} is 42, above the accepted 41.` - ); - }); - - it('fails a gate when the risk has not been calculated', async () => { - respondTo('get', `/api/services/${ID}/risk`, () => ({ json: {} })); - - await expect(showServiceRisk({ id: ID, failAbove: 100, type: 'json' })).rejects.toThrow('is unknown'); - }); - }); - - describe('experiments', () => { - it('lists provided and custom experiments, including ones not created yet', async () => { - const requests = respondTo('get', `/api/services/${ID}/experiments`, () => ({ - json: { - items: [ - { experimentKey: 'ADM-1', category: 'Redundancy', associationType: 'CUSTOM' }, - { templateId: 't-1', category: 'Scalability', associationType: 'PROVIDED' }, - ], - }, - })); - const logSpy = vi.spyOn(console, 'log'); - - await listServiceExperiments({ id: ID, type: ['custom', 'provided'], category: ['Redundancy'] }); - - expect(requests[0].url.searchParams.getAll('type')).toEqual(['CUSTOM', 'PROVIDED']); - expect(requests[0].url.searchParams.getAll('category')).toEqual(['Redundancy']); - expect(printed(logSpy)).toContain('(not created)'); - }); - - it('says when nothing matches the filters rather than that there are none', async () => { - respondTo('get', `/api/services/${ID}/experiments`, () => ({ json: { items: [] } })); - const logSpy = vi.spyOn(console, 'log'); - - await listServiceExperiments({ id: ID, type: ['custom'] }); - await listServiceExperiments({ id: ID }); - - expect(logSpy).toHaveBeenCalledWith('Service %s has no matching experiments.', ID); - expect(logSpy).toHaveBeenCalledWith('Service %s has no experiments.', ID); - }); - - it('links and unlinks a custom experiment', async () => { - const links = respondTo('post', `/api/services/${ID}/experiments/custom`, () => ({ status: 201 })); - const unlinks = respondTo('delete', `/api/services/${ID}/experiments/custom`, () => ({})); - - await linkExperiment({ id: ID, experiment: 'ADM-1', category: 'Redundancy' }); - await unlinkExperiment({ id: ID, experiment: 'ADM-1' }); - - expect(links[0].body).toEqual({ experimentKey: 'ADM-1', category: 'Redundancy' }); - expect(unlinks[0].url.searchParams.get('experimentKey')).toBe('ADM-1'); - }); - - it('creates a provided experiment from a template with placeholders', async () => { - const requests = respondTo('post', `/api/services/${ID}/experiments/provided`, () => ({ - status: 201, - headers: { location: 'http://example.com/api/experiments/ADM-9' }, - })); - const logSpy = vi.spyOn(console, 'log'); - - await provideExperiment({ id: ID, template: 't-1', placeholder: { REPLICAS: '3' }, resetProperties: false }); - - expect(requests[0].body).toEqual({ templateId: 't-1', placeholders: [{ key: 'REPLICAS', value: '3' }] }); - expect(requests[0].url.searchParams.get('resetProperties')).toBe('false'); - expect(logSpy).toHaveBeenCalledWith( - 'Provided experiment %s of service %s %s from template %s.', - 'ADM-9', - ID, - 'created', - 't-1' - ); - }); - }); - - describe('variables', () => { - it('prints the variables', async () => { - respondTo('get', `/api/services/${ID}/variables`, () => ({ json: { region: 'eu' } })); - const logSpy = vi.spyOn(console, 'log'); - - await getServiceVariables({ id: ID, type: 'json' }); - - expect(logSpy).toHaveBeenCalledWith(JSON.stringify({ region: 'eu' }, undefined, 2)); - }); - - it('merges variables from a file and arguments', async () => { - const requests = respondTo('patch', `/api/services/${ID}/variables`, () => ({})); - const file = await writeFile('variables.yml', { hosts: ['a', 'b'], region: 'us' }); - - await setServiceVariables(['region=eu', 'url=http://x?a=b'], { id: ID, file, replace: false }); - - expect(requests[0].body).toEqual({ hosts: ['a', 'b'], region: 'eu', url: 'http://x?a=b' }); - }); - - it('replaces all variables with --replace, even with none given', async () => { - const requests = respondTo('put', `/api/services/${ID}/variables`, () => ({})); - - await setServiceVariables([], { id: ID, replace: true }); - - expect(requests[0].body).toEqual({}); - }); - - it('rejects malformed arguments and empty merges', async () => { - await expect(setServiceVariables(['novalue'], { id: ID, replace: false })).rejects.toThrow( - "'novalue' is not in the form KEY=VALUE." - ); - await expect(setServiceVariables([], { id: ID, replace: false })).rejects.toThrow('No variables given.'); - }); - }); -}); diff --git a/src/serviceProfile/api.ts b/src/serviceProfile/api.ts deleted file mode 100644 index 8de91b5..0000000 --- a/src/serviceProfile/api.ts +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { Schemas } from '../api/schemas.ts'; -import { ApiError } from '../api/error.ts'; -import { executeApiCall } from '../api/http.ts'; -import { fetchAllPages } from '../api/paging.ts'; -import { abortExecution, abortExecutionWithError } from '../errors.ts'; -import { refusedForProvidedExperiments } from '../service/refusal.ts'; - -export type ServiceProfile = Schemas['ServiceProfileAO']; -export type UpsertServiceProfile = Schemas['UpsertServiceProfileAO']; - -const profilePath = (id: string) => `/api/services/profiles/${encodeURIComponent(id)}`; - -function notFoundOr(e: unknown, id: string, msg: string): Error { - if (e instanceof ApiError && e.status === 404) { - return abortExecution('Service profile %s not found.', id); - } - return abortExecutionWithError(e, msg, id); -} - -export interface ServiceProfileFilter { - name?: string; - origin?: string[]; - default?: boolean; -} - -export async function fetchServiceProfiles(filter: ServiceProfileFilter): Promise { - try { - return await fetchAllPages('/api/services/profiles', { - name: filter.name, - origin: filter.origin, - defaultProfile: filter.default === undefined ? undefined : String(filter.default), - }); - } catch (e) { - throw abortExecutionWithError(e, 'Failed to get the service profiles'); - } -} - -export async function fetchServiceProfile(id: string): Promise { - try { - return (await (await executeApiCall({ method: 'GET', path: profilePath(id) })).json()) as ServiceProfile; - } catch (e) { - throw notFoundOr(e, id, 'Failed to get service profile %s'); - } -} - -export async function upsertServiceProfile( - profile: UpsertServiceProfile, - deleteExperiments: boolean -): Promise<{ created: boolean; profile: ServiceProfile }> { - try { - const response = await executeApiCall({ - method: 'POST', - path: '/api/services/profiles', - queryParameters: { deleteExperiments: String(deleteExperiments) }, - body: profile, - }); - return { created: response.status === 201, profile: (await response.json()) as ServiceProfile }; - } catch (e) { - if (!deleteExperiments && refusedForProvidedExperiments(e)) { - throw abortExecution( - 'Service profile %s was not saved: the change would remove provided experiments. Pass --delete-experiments to delete them.', - profile.name - ); - } - throw abortExecutionWithError(e, 'Failed to save service profile %s', profile.name); - } -} - -export async function removeServiceProfile(id: string): Promise { - try { - await executeApiCall({ method: 'DELETE', path: profilePath(id) }); - } catch (e) { - if (e instanceof ApiError && e.status === 422) { - throw abortExecution('Service profile %s is provided by Steadybit and cannot be deleted.', id); - } - throw notFoundOr(e, id, 'Failed to delete service profile %s'); - } -} diff --git a/src/serviceProfile/commands.ts b/src/serviceProfile/commands.ts deleted file mode 100644 index 43c33c0..0000000 --- a/src/serviceProfile/commands.ts +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import { abortExecution } from '../errors.ts'; -import { format, output, readStructuredFile } from '../structuredFiles.ts'; -import { resolveExperimentFiles } from '../experiment/files.ts'; -import { createTable } from '../table.ts'; -import { - fetchServiceProfile, - fetchServiceProfiles, - removeServiceProfile, - type ServiceProfile, - type ServiceProfileFilter, - type UpsertServiceProfile, - upsertServiceProfile, -} from './api.ts'; - -export async function listServiceProfiles(options: ServiceProfileFilter) { - const profiles = await fetchServiceProfiles({ ...options, origin: options.origin?.map(o => o.toUpperCase()) }); - if (profiles.length === 0) { - console.log('No service profiles found.'); - return; - } - const table = createTable({ - columns: [ - { name: 'id', title: 'Id', alignment: 'left' }, - { name: 'name', title: 'Name', alignment: 'left' }, - { name: 'origin', title: 'Origin', alignment: 'left' }, - { name: 'defaultProfile', title: 'Default', alignment: 'left' }, - { name: 'templates', title: 'Templates' }, - ], - }); - table.addRows( - profiles.map(p => ({ - id: p.id, - name: p.name, - origin: p.origin, - defaultProfile: String(p.defaultProfile), - templates: p.templates.reduce((count, category) => count + (category.templateIds?.length ?? 0), 0), - })) - ); - table.printTable(); -} - -export interface GetOptions { - id: string; - file?: string; - type?: string; -} - -export async function getServiceProfile(options: GetOptions) { - await output(toFileContent(await fetchServiceProfile(options.id)), options); - if (options.file) { - console.log('Service profile %s written to %s.', options.id, options.file); - } -} - -// Only what can be sent back is kept, so that `get` followed by `apply` is a round trip. -// Whether a profile is the default is not part of it; it is changed in the platform. -function toFileContent(profile: ServiceProfile): UpsertServiceProfile { - const content: Partial = { ...profile }; - delete content.created; - delete content.createdBy; - delete content.edited; - delete content.editedBy; - delete content.version; - delete content.defaultProfile; - return content as UpsertServiceProfile; -} - -export interface ApplyOptions { - file: string[]; - recursive: boolean; - deleteExperiments: boolean; -} - -export async function applyServiceProfiles(options: ApplyOptions) { - const files = await resolveExperimentFiles(options.file, options.recursive); - for (const file of files) { - const { content, datatype } = await readStructuredFile(file, 'service profile'); - if (!content?.name) { - throw abortExecution("Service profile file '%s' does not name the profile.", file); - } - // Profiles written by hand are the team's own; PROVIDED ones come from Steadybit. - const upsert = toFileContent(content); - const profile: UpsertServiceProfile = { ...upsert, origin: upsert.origin ?? 'CUSTOM' }; - const result = await upsertServiceProfile(profile, options.deleteExperiments); - if (!content.id) { - const withId = Object.assign({ id: result.profile.id }, content, { id: result.profile.id }); - await fs.writeFile(file, format(withId, datatype), { encoding: 'utf8' }); - } - console.log( - 'Service profile %s (%s) %s.', - result.profile.name, - result.profile.id, - result.created ? 'created' : 'updated' - ); - } -} - -export interface IdOptions { - id: string; -} - -export async function deleteServiceProfile(options: IdOptions) { - await removeServiceProfile(options.id); - console.log('Service profile %s deleted.', options.id); -} diff --git a/src/serviceProfile/serviceProfile.test.ts b/src/serviceProfile/serviceProfile.test.ts deleted file mode 100644 index 6cf3f8b..0000000 --- a/src/serviceProfile/serviceProfile.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import { describe, expect, it, vi } from 'vitest'; -import { respondTo } from '../mocks/recorder.ts'; -import { writeFile } from '../mocks/tempFiles.ts'; -import { load } from '../yaml.ts'; -import { applyServiceProfiles, deleteServiceProfile, getServiceProfile, listServiceProfiles } from './commands.ts'; - -const ID = '019eacd7-fb2c-733a-bed5-99a935323db5'; -const PROFILE = { - id: ID, - name: 'High Redundancy', - origin: 'CUSTOM', - templates: [{ category: 'Instance', templateIds: ['t-1', 't-2'] }], - defaultProfile: false, - version: 1, - created: '2026-06-09T14:44:56Z', - createdBy: 'jane', - edited: '2026-06-09T14:49:13Z', - editedBy: 'jane', -}; - -describe('service profile', () => { - it('lists profiles with filters', async () => { - const requests = respondTo('get', '/api/services/profiles', () => ({ json: { items: [PROFILE] } })); - const logSpy = vi.spyOn(console, 'log'); - - await listServiceProfiles({ name: 'Redund', origin: ['custom'], default: true }); - - expect(Object.fromEntries(requests[0].url.searchParams)).toMatchObject({ - name: 'Redund', - origin: 'CUSTOM', - defaultProfile: 'true', - }); - expect(logSpy.mock.calls.flat().join('\n')).toContain('High Redundancy'); - }); - - it('writes a profile that can be applied again', async () => { - respondTo('get', `/api/services/profiles/${ID}`, () => ({ json: PROFILE })); - const file = await writeFile('profile.yml', {}); - - await getServiceProfile({ id: ID, file }); - - expect(load(await fs.readFile(file, 'utf8'))).toEqual({ - id: ID, - name: 'High Redundancy', - origin: 'CUSTOM', - templates: PROFILE.templates, - }); - }); - - it('creates a custom profile when the file names no origin', async () => { - const requests = respondTo('post', '/api/services/profiles', () => ({ status: 201, json: PROFILE })); - const file = await writeFile('new-profile.yml', { name: 'High Redundancy', templates: PROFILE.templates }); - - await applyServiceProfiles({ file: [file], recursive: false, deleteExperiments: false }); - - expect(requests[0].body).toEqual({ name: 'High Redundancy', templates: PROFILE.templates, origin: 'CUSTOM' }); - expect(requests[0].url.searchParams.get('deleteExperiments')).toBe('false'); - expect((load(await fs.readFile(file, 'utf8')) as { id: string }).id).toBe(ID); - }); - - it('points at --delete-experiments when a change would remove provided experiments', async () => { - respondTo('post', '/api/services/profiles', () => ({ - status: 422, - json: { - type: 'https://steadybit.com/problems/validation-exception', - violations: [ - { field: 'templates', message: 'Cannot remove templates without setting `deleteExperiments` to true.' }, - ], - }, - })); - const file = await writeFile('shrunk-profile.yml', { ...PROFILE, templates: [] }); - - await expect(applyServiceProfiles({ file: [file], recursive: false, deleteExperiments: false })).rejects.toThrow( - 'Service profile High Redundancy was not saved: the change would remove provided experiments. Pass --delete-experiments to delete them.' - ); - }); - - it('explains why a provided profile cannot be deleted', async () => { - respondTo('delete', `/api/services/profiles/${ID}`, () => ({ status: 422 })); - - await expect(deleteServiceProfile({ id: ID })).rejects.toThrow('is provided by Steadybit and cannot be deleted.'); - }); - - it('reports a profile that does not exist', async () => { - respondTo('delete', '/api/services/profiles/nope', () => ({ status: 404 })); - - await expect(deleteServiceProfile({ id: 'nope' })).rejects.toThrow('Service profile nope not found.'); - }); -}); diff --git a/src/setupTests.ts b/src/setupTests.ts deleted file mode 100644 index abf6a93..0000000 --- a/src/setupTests.ts +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import { afterAll, afterEach, beforeAll, beforeEach } from 'vitest'; -import { server } from './mocks/server.ts'; -import { resetExperiments } from './mocks/handlers.ts'; -import { createTempDir, removeTempDir } from './mocks/tempFiles.ts'; - -process.env.STEADYBIT_URL = 'http://example.com'; -process.env.STEADYBIT_TOKEN = 'abcdefgh'; - -beforeAll(async () => { - await createTempDir(); - server.listen(); -}); - -beforeEach(async () => { - resetExperiments(); -}); - -afterEach(async () => { - server.resetHandlers(); -}); - -afterAll(async () => { - await removeTempDir(); - server.close(); -}); diff --git a/src/structuredFiles.ts b/src/structuredFiles.ts deleted file mode 100644 index 70b4518..0000000 --- a/src/structuredFiles.ts +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import fs from 'node:fs/promises'; -import { dump, load } from './yaml.ts'; -import { abortExecution, errorMessage } from './errors.ts'; - -export type Datatype = 'json' | 'yaml'; - -// Explicit type first, then the file's extension, then YAML, which is what people put -// into Git repositories. -export function resolveDatatype(type: string | undefined, file?: string): Datatype { - if (type === 'json' || type === 'yaml') { - return type; - } - if (type) { - throw abortExecution('Unsupported output format \'%s\'. Use "json" or "yaml".', type); - } - return file?.toLowerCase().endsWith('.json') ? 'json' : 'yaml'; -} - -export function format(content: unknown, datatype: Datatype): string { - return datatype === 'json' ? JSON.stringify(content, undefined, 2) : dump(content); -} - -// Writes to the file when one is given, to stdout otherwise, so that a command's output -// can be piped as readily as saved. -export async function output(content: unknown, options: { file?: string; type?: string }): Promise { - const datatype = resolveDatatype(options.type, options.file); - if (options.file) { - await fs.writeFile(options.file, format(content, datatype), { encoding: 'utf8' }); - } else { - console.log(format(content, datatype)); - } -} - -export async function readStructuredFile(file: string, what: string): Promise<{ content: T; datatype: Datatype }> { - let text: string; - try { - text = await fs.readFile(file, { encoding: 'utf8' }); - } catch (e) { - throw abortExecution("Failed to read %s file at path '%s': %s", what, file, errorMessage(e)); - } - - try { - return { content: JSON.parse(text) as T, datatype: 'json' }; - } catch { - try { - return { content: load(text) as T, datatype: 'yaml' }; - } catch (e) { - throw abortExecution("Failed to parse %s file at path '%s' as YAML/JSON: %s", what, file, errorMessage(e)); - } - } -} diff --git a/src/table.test.ts b/src/table.test.ts deleted file mode 100644 index a18b502..0000000 --- a/src/table.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it } from 'vitest'; -import { createTable } from './table.ts'; - -describe('table', () => { - // The test runner's stdout is not a terminal, just as a pipe is not. - it('renders without escape codes when stdout is not a terminal', () => { - const table = createTable({ columns: [{ name: 'id', title: 'Id' }] }); - table.addRow({ id: 'abc' }, { color: 'red' }); - - const rendered = table.render(); - - expect(rendered).toContain('abc'); - expect(rendered).not.toContain('\x1b['); - }); -}); diff --git a/src/table.ts b/src/table.ts deleted file mode 100644 index bad4dcc..0000000 --- a/src/table.ts +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { Table } from 'console-table-printer'; -import { colorsSupported } from './colors.ts'; - -type TableOptions = NonNullable[0]>; - -// console-table-printer colours its output on its own, whatever stdout is, which put -// escape codes into every listing piped into grep or a file. It follows the same terminal -// check as the rest of the CLI instead. -export function createTable(options: Exclude = {}): Table { - return new Table({ ...options, shouldDisableColors: !colorsSupported }); -} diff --git a/src/team/get.ts b/src/team/get.ts deleted file mode 100644 index 99010e0..0000000 --- a/src/team/get.ts +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -import type { Team, TeamSummary } from './types.ts'; -import { executeApiCall } from '../api/http.ts'; - -export async function getAllTeams(onlyAccessible = true): Promise { - const response: Response = await executeApiCall({ - method: 'get', - path: '/api/teams', - queryParameters: { - onlyAccessible: String(onlyAccessible), - }, - }); - const summary = (await response.json()) as TeamSummary; - return summary.teams; -} diff --git a/src/team/types.ts b/src/team/types.ts deleted file mode 100644 index 978d836..0000000 --- a/src/team/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2022 Steadybit GmbH - -export interface TeamSummary { - teams: Team[]; -} - -export interface Team { - key: string; - name: string; - allowedEnvironments: string[]; -} diff --git a/src/template/api.ts b/src/template/api.ts deleted file mode 100644 index 3674f46..0000000 --- a/src/template/api.ts +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import type { Schemas } from '../api/schemas.ts'; -import { ApiError } from '../api/error.ts'; -import { executeApiCall } from '../api/http.ts'; -import { abortExecution, abortExecutionWithError } from '../errors.ts'; - -export interface TemplateFilter { - tag?: string[]; - targetType?: string[]; - action?: string[]; - search?: string[]; -} - -export async function fetchTemplates(filter: TemplateFilter): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: '/api/experiments/templates', - queryParameters: { - tag: filter.tag, - targetType: filter.targetType, - action: filter.action, - freeTextPhrases: filter.search, - }, - }); - return ((await response.json()) as Schemas['ExperimentTemplateSummariesAO']).templates ?? []; - } catch (e) { - throw abortExecutionWithError(e, 'Failed to get the experiment templates'); - } -} - -export async function fetchTemplate(id: string): Promise { - try { - const response = await executeApiCall({ - method: 'GET', - path: `/api/experiments/templates/${encodeURIComponent(id)}`, - }); - return (await response.json()) as Schemas['ExperimentTemplateAO']; - } catch (e) { - if (e instanceof ApiError && e.status === 404) { - throw abortExecution('Experiment template %s not found.', id); - } - throw abortExecutionWithError(e, 'Failed to get experiment template %s', id); - } -} diff --git a/src/template/commands.ts b/src/template/commands.ts deleted file mode 100644 index 95b7496..0000000 --- a/src/template/commands.ts +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { createTable } from '../table.ts'; -import { output } from '../structuredFiles.ts'; -import { fetchTemplate, fetchTemplates, type TemplateFilter } from './api.ts'; - -export async function listTemplates(options: TemplateFilter) { - const templates = await fetchTemplates(options); - if (templates.length === 0) { - console.log('No experiment templates found.'); - return; - } - const table = createTable({ - columns: [ - { name: 'id', title: 'Id', alignment: 'left' }, - { name: 'templateTitle', title: 'Title', alignment: 'left' }, - ], - }); - table.addRows(templates.map(t => ({ id: t.id, templateTitle: t.templateTitle }))); - table.printTable(); -} - -export interface GetOptions { - id: string; - file?: string; - type?: string; - placeholders?: boolean; -} - -export async function getTemplate(options: GetOptions) { - const template = await fetchTemplate(options.id); - if (options.placeholders) { - // A starting point for --placeholders: every key the template asks for, with an - // empty value to fill in. - await output(Object.fromEntries((template.placeholders ?? []).map(p => [p.key, ''])), options); - } else { - await output(template, options); - } - if (options.file) { - console.log('Experiment template %s written to %s.', options.id, options.file); - } -} diff --git a/src/template/template.test.ts b/src/template/template.test.ts deleted file mode 100644 index b22d84d..0000000 --- a/src/template/template.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it, vi } from 'vitest'; -import { respondTo } from '../mocks/recorder.ts'; -import { getTemplate, listTemplates } from './commands.ts'; - -const ID = 'd7e65100-1d20-4980-be87-c351704910b8'; - -describe('template', () => { - it('lists templates matching the filters', async () => { - const requests = respondTo('get', '/api/experiments/templates', () => ({ - json: { templates: [{ id: ID, templateTitle: 'Shop survives a database outage' }] }, - })); - const logSpy = vi.spyOn(console, 'log'); - - await listTemplates({ search: ['shop'], tag: ['k8s', 'db'] }); - - expect(requests[0].url.searchParams.getAll('freeTextPhrases')).toEqual(['shop']); - expect(requests[0].url.searchParams.getAll('tag')).toEqual(['k8s', 'db']); - expect(logSpy.mock.calls.flat().join('\n')).toContain('Shop survives a database outage'); - }); - - it('prints the placeholders of a template as a file to fill in', async () => { - respondTo('get', `/api/experiments/templates/${ID}`, () => ({ - json: { - id: ID, - templateTitle: 't', - placeholders: [ - { key: 'CLUSTER', name: 'Cluster', description: '' }, - { key: 'NAMESPACE', name: 'Namespace', description: '' }, - ], - }, - })); - const logSpy = vi.spyOn(console, 'log'); - - await getTemplate({ id: ID, placeholders: true }); - - expect(logSpy).toHaveBeenCalledWith("CLUSTER: ''\nNAMESPACE: ''\n"); - }); - - it('reports a template that does not exist', async () => { - respondTo('get', `/api/experiments/templates/${ID}`, () => ({ status: 404 })); - - await expect(getTemplate({ id: ID })).rejects.toThrow(`Experiment template ${ID} not found.`); - }); -}); diff --git a/src/yaml.test.ts b/src/yaml.test.ts deleted file mode 100644 index 5cb4795..0000000 --- a/src/yaml.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { describe, expect, it } from 'vitest'; -import { dump, load } from './yaml.ts'; - -describe('yaml', () => { - it('should expand merge keys instead of keeping a literal << property', () => { - const parsed = load(` -defaults: &defaults - ignoreFailure: false -lanes: - - steps: - - <<: *defaults - type: action -`) as any; - - expect(parsed.lanes[0].steps[0]).toEqual({ ignoreFailure: false, type: 'action' }); - expect(parsed.lanes[0].steps[0]).not.toHaveProperty('<<'); - }); - - it('should parse timestamps into dates', () => { - const parsed = load('created: 2024-01-15T10:30:00Z') as any; - - expect(parsed.created).toBeInstanceOf(Date); - }); - - it('should keep quoted scalars as strings', () => { - const parsed = load("graceful: 'true'") as any; - - expect(parsed.graceful).toBe('true'); - }); - - it('should round-trip dumped documents', () => { - const original = { key: 'TST-1', lanes: [{ steps: [{ type: 'action', ignoreFailure: false }] }] }; - - expect(load(dump(original))).toEqual(original); - }); -}); diff --git a/src/yaml.ts b/src/yaml.ts deleted file mode 100644 index 1a02e36..0000000 --- a/src/yaml.ts +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { - CORE_SCHEMA, - binaryTag, - dump as dumpYaml, - load as loadYaml, - mergeTag, - omapTag, - pairsTag, - setTag, - timestampTag, -} from 'js-yaml'; - -// js-yaml 5 narrowed its default schema to the YAML core schema, which silently drops -// merge keys (`<<:`) and timestamps: an experiment factoring shared step fields into an -// anchor would parse into a literal "<<" property. This restores the tag set that -// js-yaml 4 enabled by default, so experiment files keep round-tripping unchanged. -const schema = CORE_SCHEMA.withTags(mergeTag, timestampTag, binaryTag, omapTag, pairsTag, setTag); - -export function load(input: string): unknown { - return loadYaml(input, { schema }); -} - -export function dump(input: unknown): string { - return dumpYaml(input, { schema }); -} diff --git a/tsconfig.build.json b/tsconfig.build.json deleted file mode 100644 index 7edd86b..0000000 --- a/tsconfig.build.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*.ts"], - "compilerOptions": { - "rootDir": "src", - // Stated rather than left to the default: this file governs the published package, - // which ships dist/ without src/, so any map emitted here would point at a source - // file that is not in the tarball. - "sourceMap": false - }, - "exclude": ["src/**/*.test.ts", "src/mocks", "src/setupTests.ts"] -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 53c7bdb..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "include": ["src/**/*.ts", "vitest.config.ts"], - "compilerOptions": { - "strict": true, - "target": "es2023", - "outDir": "dist", - "module": "nodenext", - // Lets relative imports name the .ts file that is actually on disk; the extension - // is rewritten to .js on emit, which is what Node's ESM resolver needs. - "rewriteRelativeImportExtensions": true, - // Declared explicitly so that an unrelated @types package cannot leak globals. - // TypeScript 7 additionally requires this, as it no longer includes @types/* implicitly. - "types": ["node"], - // Required because "dom" is intentionally absent from lib: some dependencies ship - // declarations that assume it. Omitting dom is what makes fetch/Response resolve - // to the Node types rather than the browser ones. - "skipLibCheck": true - } -} diff --git a/vitest.config.ts b/vitest.config.ts deleted file mode 100644 index 29ee33e..0000000 --- a/vitest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -// SPDX-FileCopyrightText: 2026 Steadybit GmbH - -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - test: { - include: ['src/**/*.test.ts'], - setupFiles: ['./src/setupTests.ts'], - }, -}); From a422b55937267bae3b8a75749acaa49abe67c6f1 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 14:04:05 +0200 Subject: [PATCH 6/9] test: isolate the home directory on Windows too; keep LF line endings --- .gitattributes | 4 ++++ internal/config/config_test.go | 12 ++++++++++-- internal/platform/client_test.go | 2 +- internal/platformtest/platformtest.go | 12 +++++++++++- internal/tools/headers_test.go | 2 +- 5 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..311b207 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# The fixtures compare exact bytes and the scripts run in Linux containers, so line +# endings stay LF on every platform. +* text=auto eol=lf +*.zip binary diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 349ae1c..f7ab854 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -16,6 +16,7 @@ import ( func TestReadsProfilesWrittenByTheTypeScriptCLI(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) + t.Setenv("USERPROFILE", home) require.NoError(t, os.MkdirAll(filepath.Join(home, ".steadybit"), 0o755)) require.NoError(t, os.WriteFile(filepath.Join(home, ".steadybit", "profiles.json"), []byte(`[ {"name": "prod", "apiAccessToken": "p", "baseUrl": "https://platform.steadybit.com"}, @@ -32,7 +33,7 @@ func TestReadsProfilesWrittenByTheTypeScriptCLI(t *testing.T) { } func TestEnvironmentWinsAndAnEmptyTokenCounts(t *testing.T) { - t.Setenv("HOME", t.TempDir()) + setHome(t) require.NoError(t, AddProfile(Profile{Name: "p", APIAccessToken: "from-profile"})) t.Setenv("STEADYBIT_TOKEN", "") t.Setenv("STEADYBIT_URL", "http://localhost:8080") @@ -45,7 +46,7 @@ func TestEnvironmentWinsAndAnEmptyTokenCounts(t *testing.T) { } func TestFallsBackToTheFirstProfile(t *testing.T) { - t.Setenv("HOME", t.TempDir()) + setHome(t) require.NoError(t, AddProfile(Profile{Name: "a", APIAccessToken: "1"})) require.NoError(t, AddProfile(Profile{Name: "b", APIAccessToken: "2"})) @@ -54,3 +55,10 @@ func TestFallsBackToTheFirstProfile(t *testing.T) { require.NoError(t, err) assert.Equal(t, "a", active.Name) } + +// Go reads USERPROFILE for the home directory on Windows and HOME elsewhere. +func setHome(t *testing.T) { + home := t.TempDir() + t.Setenv("HOME", home) + t.Setenv("USERPROFILE", home) +} diff --git a/internal/platform/client_test.go b/internal/platform/client_test.go index 67ac779..2a9fe4c 100644 --- a/internal/platform/client_test.go +++ b/internal/platform/client_test.go @@ -84,7 +84,7 @@ func TestRetriesTransportFailuresOnlyForIdempotentMethods(t *testing.T) { } }() t.Cleanup(func() { _ = listener.Close() }) - t.Setenv("HOME", t.TempDir()) + platformtest.Home(t) t.Setenv("STEADYBIT_URL", "http://"+listener.Addr().String()) t.Setenv("STEADYBIT_TOKEN", "t") client, err := platform.New() diff --git a/internal/platformtest/platformtest.go b/internal/platformtest/platformtest.go index d8ca345..565715b 100644 --- a/internal/platformtest/platformtest.go +++ b/internal/platformtest/platformtest.go @@ -61,7 +61,7 @@ func New(t *testing.T) *Platform { p.server = httptest.NewServer(http.HandlerFunc(p.serve)) t.Cleanup(p.server.Close) p.URL = p.server.URL - t.Setenv("HOME", t.TempDir()) + Home(t) t.Setenv("STEADYBIT_URL", p.server.URL) t.Setenv("STEADYBIT_TOKEN", "test-token") client, err := platform.New() @@ -148,6 +148,16 @@ func (p *Platform) serve(w http.ResponseWriter, r *http.Request) { } } +// Home gives the test an empty home directory. Go reads USERPROFILE for it on Windows +// and HOME elsewhere, so both are set. +func Home(t *testing.T) string { + t.Helper() + home := t.TempDir() + t.Setenv("HOME", home) + t.Setenv("USERPROFILE", home) + return home +} + // Stdout captures what fn prints. func Stdout(t *testing.T, fn func() error) (string, error) { t.Helper() diff --git a/internal/tools/headers_test.go b/internal/tools/headers_test.go index 333cf38..00e287f 100644 --- a/internal/tools/headers_test.go +++ b/internal/tools/headers_test.go @@ -11,7 +11,7 @@ import ( "testing" ) -var header = regexp.MustCompile(`^(#!.*\n)?// SPDX-License-Identifier: MIT\n// SPDX-FileCopyrightText: \d{4} Steadybit GmbH\n`) +var header = regexp.MustCompile(`^(#!.*\r?\n)?// SPDX-License-Identifier: MIT\r?\n// SPDX-FileCopyrightText: \d{4} Steadybit GmbH\r?\n`) // Every hand-written source file starts with the SPDX header; generated ones are exempt. func TestSourceFilesCarryTheSPDXHeader(t *testing.T) { From bc8a2d64f5ed750a5140fdd8d7733ba4b6d10910 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 14:05:31 +0200 Subject: [PATCH 7/9] style: gofmt the tests; let every platform finish in CI --- .github/workflows/ci.yml | 2 ++ internal/execution/execution_test.go | 4 +++- internal/service/service_test.go | 10 +++++----- internal/serviceprofile/serviceprofile_test.go | 4 +++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a7f3b3..a2c6a07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,8 @@ jobs: verify: # Paths, terminals and line endings differ between them, and users run all three. strategy: + # Each platform's failures are worth seeing, not only the first one's. + fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} diff --git a/internal/execution/execution_test.go b/internal/execution/execution_test.go index 7a8d505..5b96c0b 100644 --- a/internal/execution/execution_test.go +++ b/internal/execution/execution_test.go @@ -114,7 +114,9 @@ func TestDownloadsEveryArtifactIntoADirectoryPerTarget(t *testing.T) { }) dir := t.TempDir() - _, err := platformtest.Stdout(t, func() error { return execution.Download(ctx, p.Client, execution.DownloadOptions{ID: 42, Directory: dir}) }) + _, err := platformtest.Stdout(t, func() error { + return execution.Download(ctx, p.Client, execution.DownloadOptions{ID: 42, Directory: dir}) + }) require.NoError(t, err) for _, f := range []string{"te-1/report.zip", "te-1/log.txt", "te-2/report.zip", "te-3/result.json"} { diff --git a/internal/service/service_test.go b/internal/service/service_test.go index 684ba50..5e78478 100644 --- a/internal/service/service_test.go +++ b/internal/service/service_test.go @@ -123,9 +123,9 @@ func TestExperimentsOfAService(t *testing.T) { require.NoError(t, err) assert.Contains(t, out, "(not created)") assert.Contains(t, out, "Provided experiment ADM-9 of service "+id+" created from template d7e65100-1d20-4980-be87-c351704910b8.") - assert.Equal(t, []string{"PROVIDED"}, p.Requests("GET /api/services/"+id+"/experiments")[0].Query["type"]) - assert.Equal(t, map[string]any{"experimentKey": "ADM-1", "category": "Redundancy"}, p.Requests("POST /api/services/"+id+"/experiments/custom")[0].JSON(t)) - assert.Equal(t, []string{"ADM-1"}, p.Requests("DELETE /api/services/"+id+"/experiments/custom")[0].Query["experimentKey"]) + assert.Equal(t, []string{"PROVIDED"}, p.Requests("GET /api/services/" + id + "/experiments")[0].Query["type"]) + assert.Equal(t, map[string]any{"experimentKey": "ADM-1", "category": "Redundancy"}, p.Requests("POST /api/services/" + id + "/experiments/custom")[0].JSON(t)) + assert.Equal(t, []string{"ADM-1"}, p.Requests("DELETE /api/services/" + id + "/experiments/custom")[0].Query["experimentKey"]) } func TestVariables(t *testing.T) { @@ -143,8 +143,8 @@ func TestVariables(t *testing.T) { }) require.NoError(t, err) - assert.Equal(t, map[string]any{"hosts": []any{"a", "b"}, "region": "eu", "url": "http://x?a=b"}, p.Requests("PATCH /api/services/"+id+"/variables")[0].JSON(t)) - assert.Equal(t, map[string]any{}, p.Requests("PUT /api/services/"+id+"/variables")[0].JSON(t)) + assert.Equal(t, map[string]any{"hosts": []any{"a", "b"}, "region": "eu", "url": "http://x?a=b"}, p.Requests("PATCH /api/services/" + id + "/variables")[0].JSON(t)) + assert.Equal(t, map[string]any{}, p.Requests("PUT /api/services/" + id + "/variables")[0].JSON(t)) assert.EqualError(t, service.SetVariables(ctx, p.Client, []string{"novalue"}, service.VariableSetOptions{ID: id}), "'novalue' is not in the form KEY=VALUE.") assert.EqualError(t, service.SetVariables(ctx, p.Client, nil, service.VariableSetOptions{ID: id}), "No variables given. Pass KEY=VALUE arguments or --file.") } diff --git a/internal/serviceprofile/serviceprofile_test.go b/internal/serviceprofile/serviceprofile_test.go index 1469569..ee9e914 100644 --- a/internal/serviceprofile/serviceprofile_test.go +++ b/internal/serviceprofile/serviceprofile_test.go @@ -55,7 +55,9 @@ func TestApplyDefaultsToACustomProfile(t *testing.T) { file := filepath.Join(t.TempDir(), "p.yml") require.NoError(t, os.WriteFile(file, []byte("name: P\ntemplates: []\n"), 0o644)) - _, err := platformtest.Stdout(t, func() error { return serviceprofile.Apply(ctx, p.Client, serviceprofile.ApplyOptions{Files: []string{file}}) }) + _, err := platformtest.Stdout(t, func() error { + return serviceprofile.Apply(ctx, p.Client, serviceprofile.ApplyOptions{Files: []string{file}}) + }) require.NoError(t, err) assert.Equal(t, map[string]any{"name": "P", "templates": []any{}, "origin": "CUSTOM"}, p.Requests("POST /api/services/profiles")[0].JSON(t)) From be9dc5ff980302b11ce83bbc8d19bfaabdd9f91a Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 14:54:13 +0200 Subject: [PATCH 8/9] fix(go): restore experiment delete It had been left out of the port. A test now pins every command of the TypeScript CLI, so none can go missing again. --- internal/cli/commands_test.go | 37 ++++++++++++++++++++++++++ internal/cli/experiment.go | 18 ++++++++++++- internal/experiment/experiment.go | 13 +++++++++ internal/experiment/experiment_test.go | 12 +++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 internal/cli/commands_test.go diff --git a/internal/cli/commands_test.go b/internal/cli/commands_test.go new file mode 100644 index 0000000..12cb48d --- /dev/null +++ b/internal/cli/commands_test.go @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2026 Steadybit GmbH + +package cli + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Every command of the TypeScript CLI, which pipelines call by name. Removing one is a +// breaking change; this list is what the Go CLI promised to keep. +var typeScriptCommands = []string{ + "advice validate-status", + "config show", "config profile add", "config profile list", "config profile ls", "config profile remove", "config profile select", + "experiment run", "experiment exec", "experiment get", "experiment apply", "experiment delete", "experiment dump", + "template list", "template get", + "execution get", "execution cancel", "execution property set", "execution property add", "execution artifact list", "execution artifact download", + "schedule list", "schedule get", "schedule apply", "schedule create", "schedule update", "schedule enable", "schedule disable", "schedule delete", + "service list", "service get", "service apply", "service delete", "service risk", + "service experiment list", "service experiment provide", "service experiment link", "service experiment unlink", + "service variable get", "service variable set", + "service-profile list", "service-profile get", "service-profile apply", "service-profile delete", +} + +func TestKeepsEveryCommandOfTheTypeScriptCLI(t *testing.T) { + root := newRoot() + for _, path := range typeScriptCommands { + cmd, rest, err := root.Find(strings.Fields(path)) + if assert.NoError(t, err, path) { + assert.Empty(t, rest, path) + assert.Contains(t, append(cmd.Aliases, cmd.Name()), strings.Fields(path)[len(strings.Fields(path))-1], path) + } + } +} diff --git a/internal/cli/experiment.go b/internal/cli/experiment.go index 2e1820d..6ad6b2c 100644 --- a/internal/cli/experiment.go +++ b/internal/cli/experiment.go @@ -17,7 +17,7 @@ import ( func newExperiment() *cobra.Command { cmd := &cobra.Command{Use: "experiment", Short: "Check and run experiments."} - cmd.AddCommand(newExperimentRun(), newExperimentGet(), newExperimentApply(), newExperimentDump()) + cmd.AddCommand(newExperimentRun(), newExperimentGet(), newExperimentApply(), newExperimentDelete(), newExperimentDump()) return cmd } @@ -162,3 +162,19 @@ func newExperimentDump() *cobra.Command { variadic(cmd, "team") return cmd } + +func newExperimentDelete() *cobra.Command { + var key string + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete an experiment from Steadybit.", + Args: cobra.NoArgs, + Example: examples("steadybit experiment delete -k ADM-1"), + RunE: withClient(func(ctx context.Context, c *platform.Client, _ []string) error { + return experiment.Delete(ctx, c, key) + }), + } + cmd.Flags().StringVarP(&key, "key", "k", "", "The experiment key.") + _ = cmd.MarkFlagRequired("key") + return cmd +} diff --git a/internal/experiment/experiment.go b/internal/experiment/experiment.go index 00d15d9..b4eae6c 100644 --- a/internal/experiment/experiment.go +++ b/internal/experiment/experiment.go @@ -87,6 +87,19 @@ func Get(ctx context.Context, c *platform.Client, o GetOptions) error { return nil } +// Delete removes an experiment. +func Delete(ctx context.Context, c *platform.Client, key string) error { + _, _, err := platform.Read(c.DeleteExperiment(ctx, key)) + if platform.IsStatus(err, http.StatusNotFound) { + return fmt.Errorf("Experiment %s not found.", key) + } + if err != nil { + return platform.Failed(err, "Failed to delete the experiment. HTTP request failed.") + } + fmt.Printf("Experiment %s deleted.\n", key) + return nil +} + // ResolveFiles expands directories into their YAML files, recursively on request. func ResolveFiles(paths []string, recursive bool) ([]string, error) { var files []string diff --git a/internal/experiment/experiment_test.go b/internal/experiment/experiment_test.go index b3cd232..79e5d1f 100644 --- a/internal/experiment/experiment_test.go +++ b/internal/experiment/experiment_test.go @@ -306,3 +306,15 @@ func TestDumpRefusesAnUnknownTeam(t *testing.T) { assert.EqualError(t, err, "No accessible team with key NOPE. Available: A, B") } + +func TestDelete(t *testing.T) { + p := platformtest.New(t) + p.Reply("DELETE /api/experiments/TST-1", platformtest.Reply{}) + p.Reply("DELETE /api/experiments/TST-9", platformtest.Reply{Status: http.StatusNotFound}) + + out, err := platformtest.Stdout(t, func() error { return experiment.Delete(ctx, p.Client, "TST-1") }) + + require.NoError(t, err) + assert.Equal(t, "Experiment TST-1 deleted.\n", out) + assert.EqualError(t, experiment.Delete(ctx, p.Client, "TST-9"), "Experiment TST-9 not found.") +} From 46da7fe18e6755bc5e7c526df7a74f78786948c3 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Fri, 25 Sep 2026 15:07:12 +0200 Subject: [PATCH 9/9] docs: the Go CLI is released as v6.0.0 v5.0.0 is the last TypeScript release; its changelog entry is kept as it was released. --- CHANGELOG.md | 29 ++++++++++++++++++++--------- CONTRIBUTING.md | 4 ++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d89a48b..cb48c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,22 @@ # Changelog -## v5.0.0 +## v6.0.0 - **The CLI is now a single binary written in Go.** It runs without Node.js, and is installed the same ways: `npm install -g steadybit`, which now installs the binary for your platform and works with any Node.js from 18 on, or the `steadybit/cli` container image, now 18 MB - instead of 249 MB. It can also be downloaded directly from the GitHub releases. Commands, flags, messages, exit - codes, profiles in `~/.steadybit` and the `STEADYBIT_*` variables are unchanged, and - experiment, schedule and service files are written byte for byte as before. + instead of 249 MB. It can also be downloaded directly from the GitHub releases. Commands, + flags, messages, exit codes, profiles in `~/.steadybit` and the `STEADYBIT_*` variables + are unchanged, and experiment, schedule and service files are written byte for byte as + before. - Writing a new experiment's key back into a YAML file no longer rewrites the file: the key is added at the top and comments, anchors and formatting are kept. - Shell completion: `steadybit completion bash|zsh|fish|powershell`. +- Errors about a missing required flag are worded differently (`required flag(s) "key" not + set`); they still exit with 1. + +## v5.0.0 + - `experiment apply --template ` creates an experiment from an experiment template, or updates the one created before with the same `--external-id`. With `-k` it re-renders an existing experiment with new placeholder values. Placeholders are given with @@ -34,11 +40,14 @@ - Every command now shows examples in its `--help`. - Fixed the table printed by `advice validate-status` containing colour escape codes when piped. Tables now follow the same terminal check as the rest of the output. -- The CLI's API client is generated from the platform's OpenAPI spec. CI builds it against - the live platform daily and before every release, so a breaking API change is caught - before it reaches a pipeline. -- **Security:** `-v, --verbose` no longer prints the API access token, which CI jobs using - it had in their logs. +- The CLI's API types are generated from the platform's OpenAPI spec. CI checks them + against the live platform daily and before every release, so a breaking API change is + caught before it reaches a pipeline. +- **Breaking:** Node.js 22.13.0 or later is now required. Node.js 18 and 20 have reached + end of life and the CLI's dependencies no longer support them. +- The CLI is now published as an ES module. +- **Security:** `-v, --verbose` no longer prints the API access token. Commander passes the + flag on to spawned subcommands, so CI jobs using it had the token in their logs. - **Security:** requests are only ever sent to the configured platform. An absolute URL in a platform response, such as the `Location` header of a started run, now has its origin replaced by the configured one so that the access token cannot be sent elsewhere. @@ -76,6 +85,8 @@ `STEADYBIT_RATE_LIMIT_INTERVAL` override the assumed rate limit for deployments configured differently. A value that is not a positive whole number is reported and ignored rather than silently changing how hard the CLI polls. +- Replaced `inquirer` with the `@inquirer/*` prompt packages and `colors` with `picocolors`. +- Replaced `node-fetch` with the Node.js built-in `fetch`. - Dependency updates ## v4.3.2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe2966a..2a8c744 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -104,8 +104,8 @@ platform, and `steadybit`, which installs the right one), and pushes the Docker git commit -am 'chore: prepare release' # 2. Tag and push -git tag v5.0.0 -git push origin main v5.0.0 +git tag v6.0.0 +git push origin main v6.0.0 ``` Use a major version for breaking changes to commands, flags, output or exit codes.