diff --git a/cmd/environment.go b/cmd/environment.go index efc08ff..f42b7f1 100644 --- a/cmd/environment.go +++ b/cmd/environment.go @@ -156,7 +156,15 @@ func runEnvironmentList(cmd *cobra.Command, args []string) error { tbl := cli.NewTable("ID/Slug", "Name", "Description", "Monthly $", "Daily $") for _, env := range environments { - tbl.AddRow(env.Slug, env.Name, env.Description, env.Cost.Monthly.Average.Amount, env.Cost.Daily.Average.Amount) + monthly := "" + daily := "" + if env.Cost.Monthly.Average.Amount != nil { + monthly = fmt.Sprintf("%v", *env.Cost.Monthly.Average.Amount) + } + if env.Cost.Daily.Average.Amount != nil { + daily = fmt.Sprintf("%v", *env.Cost.Daily.Average.Amount) + } + tbl.AddRow(env.Slug, env.Name, env.Description, monthly, daily) } tbl.Print() diff --git a/cmd/project.go b/cmd/project.go index c2164c6..ea22c68 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -163,7 +163,15 @@ func runProjectList(cmd *cobra.Command, args []string) error { tbl := cli.NewTable("ID/Slug", "Name", "Description", "Monthly $", "Daily $") for _, project := range projects { - tbl.AddRow(project.Slug, project.Name, project.Description, project.Cost.Monthly.Average.Amount, project.Cost.Daily.Average.Amount) + monthly := "" + daily := "" + if project.Cost.Monthly.Average.Amount != nil { + monthly = fmt.Sprintf("%v", *project.Cost.Monthly.Average.Amount) + } + if project.Cost.Daily.Average.Amount != nil { + daily = fmt.Sprintf("%v", *project.Cost.Daily.Average.Amount) + } + tbl.AddRow(project.Slug, project.Name, project.Description, monthly, daily) } tbl.Print() diff --git a/cmd/templates/environment.get.md.tmpl b/cmd/templates/environment.get.md.tmpl index 03eec12..47094d8 100644 --- a/cmd/templates/environment.get.md.tmpl +++ b/cmd/templates/environment.get.md.tmpl @@ -12,6 +12,6 @@ {{- end}} ## Cost -**Monthly Average:** ${{.Cost.Monthly.Average.Amount}} +**Monthly Average:** {{with .Monthly.Average.Amount}}${{.}}{{end}} -**Daily Average:** ${{.Cost.Daily.Average.Amount}} +**Daily Average:** {{with .Daily.Average.Amount}}${{.}}{{end}} diff --git a/cmd/templates/project.get.md.tmpl b/cmd/templates/project.get.md.tmpl index 8a5d316..fbf0b84 100644 --- a/cmd/templates/project.get.md.tmpl +++ b/cmd/templates/project.get.md.tmpl @@ -10,6 +10,6 @@ {{end}} ## Cost -**Monthly Average:** ${{.Cost.Monthly.Average.Amount}} +**Monthly Average:** {{with .Monthly.Average.Amount}}${{.}}{{end}} -**Daily Average:** ${{.Cost.Daily.Average.Amount}} +**Daily Average:** {{with .Daily.Average.Amount}}${{.}}{{end}} diff --git a/cmd/version.go b/cmd/version.go index b2a743b..f8c82d6 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,10 +1,13 @@ package cmd import ( + "context" "fmt" + "github.com/massdriver-cloud/mass/pkg/api" "github.com/massdriver-cloud/mass/pkg/prettylogs" "github.com/massdriver-cloud/mass/pkg/version" + "github.com/massdriver-cloud/massdriver-sdk-go/massdriver/client" "github.com/spf13/cobra" ) @@ -21,15 +24,24 @@ func NewCmdVersion() *cobra.Command { } func runVersion(cmd *cobra.Command, args []string) { - latestVersion, err := version.GetLatestVersion() + massVersionColor := prettylogs.Green(version.MassVersion()) + fmt.Printf("🧰 CLI version: %v (git SHA: %v)\n", massVersionColor, version.MassGitSHA()) + + // Best-effort: check whether a newer CLI is available (does not affect exit code). + if latestVersion, err := version.GetLatestVersion(); err == nil { + if isOld, _ := version.CheckForNewerVersionAvailable(latestVersion); isOld { + fmt.Printf("⬆️ A newer version of the CLI is available, you can download it here: %v\n", version.LatestReleaseURL) + } + } + + // Best-effort: if we can authenticate, show the Massdriver server version too. + ctx := context.Background() + mdClient, err := client.New() if err != nil { - fmt.Printf("Could not check for newer version, skipping. url:%s error:%s", version.LatestReleaseURL, err.Error()) return } - isOld, _ := version.CheckForNewerVersionAvailable(latestVersion) - if isOld { - fmt.Printf("A newer version of the CLI is available, you can download it here: %v\n", version.LatestReleaseURL) + + if server, err := api.GetServer(ctx, mdClient); err == nil && server != nil && server.Version != "" { + fmt.Printf("🌐 Server version: %v\n", prettylogs.Green(server.Version)) } - massVersionColor := prettylogs.Green(version.MassVersion()) - fmt.Printf("Mass CLI version: %v (git SHA: %v) \n", massVersionColor, version.MassGitSHA()) } diff --git a/pkg/api/cost.go b/pkg/api/cost.go index 7f43e74..e1c87cc 100644 --- a/pkg/api/cost.go +++ b/pkg/api/cost.go @@ -1,14 +1,18 @@ package api type Cost struct { - Monthly *CostType `json:"monthly"` - Daily *CostType `json:"daily"` + Monthly Summary `json:"monthly"` + Daily Summary `json:"daily"` } -type CostType struct { - Average *CostSummary `json:"average"` +// Summary of costs over a time period. +type Summary struct { + Previous CostSample `json:"previous"` + Average CostSample `json:"average"` } -type CostSummary struct { - Amount float64 `json:"amount"` +// A single cost measurement. Fields may be null when no cost data exists. +type CostSample struct { + Amount *float64 `json:"amount"` + Currency *string `json:"currency"` } diff --git a/pkg/api/environment.go b/pkg/api/environment.go index 1984dd0..b32b941 100644 --- a/pkg/api/environment.go +++ b/pkg/api/environment.go @@ -16,7 +16,7 @@ type Environment struct { Name string `json:"name"` Slug string `json:"slug"` Description string `json:"description,omitempty"` - Cost *Cost `json:"cost,omitempty" mapstructure:"cost,omitempty"` + Cost Cost `json:"cost" mapstructure:"cost"` Packages []Package `json:"packages,omitempty" mapstructure:"packages,omitempty"` Project *Project `json:"project,omitempty" mapstructure:"project,omitempty"` } diff --git a/pkg/api/environment_test.go b/pkg/api/environment_test.go index bc9074c..6ad6a14 100644 --- a/pkg/api/environment_test.go +++ b/pkg/api/environment_test.go @@ -9,21 +9,23 @@ import ( "github.com/massdriver-cloud/massdriver-sdk-go/massdriver/client" ) +func floatPtr(v float64) *float64 { return &v } + func TestGetEnvironment(t *testing.T) { want := api.Environment{ ID: "env-uuid1", Name: "Test Environment", Slug: "env", Description: "This is a test environment", - Cost: &api.Cost{ - Daily: &api.CostType{ - Average: &api.CostSummary{ - Amount: 10.0, + Cost: api.Cost{ + Daily: api.Summary{ + Average: api.CostSample{ + Amount: floatPtr(10.0), }, }, - Monthly: &api.CostType{ - Average: &api.CostSummary{ - Amount: 300.0, + Monthly: api.Summary{ + Average: api.CostSample{ + Amount: floatPtr(300.0), }, }, }, @@ -150,15 +152,15 @@ func TestGetEnvironmentsByPackage(t *testing.T) { Name: "Test Environment 1", Slug: "env1", Description: "First test environment", - Cost: &api.Cost{ - Daily: &api.CostType{ - Average: &api.CostSummary{ - Amount: 5.0, + Cost: api.Cost{ + Daily: api.Summary{ + Average: api.CostSample{ + Amount: floatPtr(5.0), }, }, - Monthly: &api.CostType{ - Average: &api.CostSummary{ - Amount: 150.0, + Monthly: api.Summary{ + Average: api.CostSample{ + Amount: floatPtr(150.0), }, }, }, @@ -172,15 +174,15 @@ func TestGetEnvironmentsByPackage(t *testing.T) { Name: "Test Environment 2", Slug: "env2", Description: "Second test environment", - Cost: &api.Cost{ - Daily: &api.CostType{ - Average: &api.CostSummary{ - Amount: 8.0, + Cost: api.Cost{ + Daily: api.Summary{ + Average: api.CostSample{ + Amount: floatPtr(8.0), }, }, - Monthly: &api.CostType{ - Average: &api.CostSummary{ - Amount: 240.0, + Monthly: api.Summary{ + Average: api.CostSample{ + Amount: floatPtr(240.0), }, }, }, diff --git a/pkg/api/genqlient.graphql b/pkg/api/genqlient.graphql index 6f03b3d..dfabe1a 100644 --- a/pkg/api/genqlient.graphql +++ b/pkg/api/genqlient.graphql @@ -221,11 +221,13 @@ query getEnvironmentById($organizationId: ID!, $id: ID!) { cost { monthly { average { + # @genqlient(pointer: true) amount } } daily { average { + # @genqlient(pointer: true) amount } } @@ -277,11 +279,13 @@ query getEnvironmentsByProject($organizationId: ID!, $projectId: ID!) { cost { monthly { average { + # @genqlient(pointer: true) amount } } daily { average { + # @genqlient(pointer: true) amount } } @@ -528,11 +532,13 @@ query getProjects($organizationId: ID!){ cost{ monthly{ average{ + # @genqlient(pointer: true) amount } } daily{ average{ + # @genqlient(pointer: true) amount } } diff --git a/pkg/api/project.go b/pkg/api/project.go index 859dd8b..f6ef721 100644 --- a/pkg/api/project.go +++ b/pkg/api/project.go @@ -16,7 +16,7 @@ type Project struct { Slug string `json:"slug"` Description string `json:"description"` DefaultParams map[string]any `json:"defaultParams"` - Cost *Cost `json:"cost,omitempty"` + Cost Cost `json:"cost"` Environments []Environment `json:"environments"` } diff --git a/pkg/api/schema.graphql b/pkg/api/schema.graphql index fd9c264..876ac28 100644 --- a/pkg/api/schema.graphql +++ b/pkg/api/schema.graphql @@ -438,12 +438,6 @@ type RootMutationType { id: ID! ): BundlePayload - "Assign a credential to be used to retrieve cloud costs" - assignCloudCostCredential(organizationId: ID!, artifactId: ID!): CloudCostCredentialPayload - - "Dismisses a credential to be used to retrieve cloud costs" - dismissCloudCostCredential(organizationId: ID!, artifactId: ID!): CloudCostCredentialPayload - "Enqueues a package for deployment" deployPackage( organizationId: ID! @@ -489,11 +483,17 @@ type RootMutationType { unlinkManifests(organizationId: ID!, linkId: ID!): LinkPayload - "Enable an integration for an organization" - enableIntegration(organizationId: ID!, integrationTypeId: String!, config: JSON!, auth: JSON!): IntegrationPayload + "Create an integration for an organization" + createIntegration(organizationId: ID!, input: CreateIntegrationInput!): IntegrationPayload + + "Enable an integration" + enableIntegration(organizationId: ID!, id: ID!): IntegrationPayload + + "Disable an integration" + disableIntegration(organizationId: ID!, id: ID!): IntegrationPayload - "Disable an integration for an organization" - disableIntegration(organizationId: ID!, integrationConfigId: ID!): IntegrationPayload + "Delete an integration" + deleteIntegration(organizationId: ID!, id: ID!): IntegrationPayload "Create an environment" createEnvironment(organizationId: ID!, projectId: ID!, name: String!, slug: String!, description: String): EnvironmentPayload @@ -512,7 +512,29 @@ type RootMutationType { updateEnvironment(organizationId: ID!, id: ID!, name: String!, description: String): EnvironmentPayload "Removes an environment from a project. This will fail if infrastructure is still provisioned in the environment." - deleteEnvironment(organizationId: ID!, id: ID!): EnvironmentPayload + deleteEnvironment(organizationId: ID!, id: ID!, orphanForks: Boolean): EnvironmentPayload + + "Forks an environment, creating a new environment with optional copying of secrets, env defaults, and remote references." + forkEnvironment( + organizationId: ID! + + "The ID of the environment to fork from" + parentId: ID! + + "Input containing the new environment details and options for what to copy from the parent" + input: ForkEnvironmentInput! + ): EnvironmentPayload + + "Merges a forked environment back into its parent. By default copies params, with options for secrets, remote references, and env defaults." + mergeEnvironment( + organizationId: ID! + + "The ID of the forked environment to merge" + id: ID! + + "Input containing options for what to copy from the fork back to the parent environment" + input: MergeEnvironmentInput + ): EnvironmentPayload "Connect an environment as the default environment type for a given environment" createEnvironmentConnection(organizationId: ID!, artifactId: ID!, environmentId: ID!): EnvironmentConnectionPayload @@ -1002,7 +1024,7 @@ type Project { defaultParams: JSON @deprecated(reason: "Default params are being deprecated for preview environments in favor of inherit\/override PEs from specific environments. This field will be removed in a future release.") "Cloud provider costs for this project" - cost: Cost + cost: Cost! } type ProjectPayload { @@ -1081,6 +1103,18 @@ type Dimension { value: String! } +"A flattened artifact payload property summary with display name, path, and value. Sensitive fields are masked as [SENSITIVE]." +type ArtifactPropertySummary { + "Display name for the property summary (e.g., 'Database: Hostname', 'Database: Port')" + name: String! + + "Full path to the value in the artifact payload (e.g., '.database.hostname', '.database.port')" + path: String! + + "The scalar value. Sensitive fields (marked with $md.sensitive: true) are masked as '[SENSITIVE]'" + value: JSON +} + "An alarm is a condition that triggers a notification. It is defined by a metric, a comparison operator, a threshold, and a period." type Alarm { "Unique identifier for the alarm." @@ -1226,11 +1260,14 @@ type Package { "Artifacts provisioned by this package" artifacts: [Artifact] + "Flattened list of artifact payload property summaries suitable for display in a table. Sensitive fields are automatically masked." + propertySummaries: [ArtifactPropertySummary] + "Artifacts from a remote source like another project or a resource not managed by massdriver" remoteReferences: [RemoteReference] "Cloud provider costs for this package" - cost: Cost + cost: Cost! } type PackagePayload { @@ -1664,6 +1701,37 @@ input PreviewEnvironmentInput { ciContext: JSON! } +input ForkEnvironmentInput { + "Name for the new forked environment" + name: String! + + "Slug for the new forked environment" + slug: String! + + "Optional description for the new forked environment" + description: String + + "Whether to copy secrets from the parent environment to the fork" + copySecrets: Boolean + + "Whether to copy environment defaults (target connections) from the parent environment to the fork" + copyEnvDefaults: Boolean + + "Whether to copy remote references from the parent environment to the fork" + copyRemoteReferences: Boolean +} + +input MergeEnvironmentInput { + "Whether to copy secrets from the fork back to the parent environment" + copySecrets: Boolean + + "Whether to copy environment defaults (target connections) from the fork back to the parent environment" + copyEnvDefaults: Boolean + + "Whether to copy remote references from the fork back to the parent environment" + copyRemoteReferences: Boolean +} + type Environment { id: ID @@ -1673,6 +1741,8 @@ type Environment { description: String + parent: Environment + deletable: EnvironmentDeletionLifecycle! createdAt: DateTime @@ -1690,8 +1760,8 @@ type Environment { defaultConnections: [DefaultEnvironmentConnection] - "Cloud provider costs for this target" - cost: Cost + "Cloud provider costs for this environment" + cost: Cost! } type EnvironmentPayload { @@ -1708,13 +1778,17 @@ type EnvironmentPayload { enum IntegrationStatus { DISABLED ENABLED + ENABLING + DISABLING } type IntegrationType { name: String! id: String! + description: String configSchema: JSON! authSchema: JSON! + docs: String! } type IntegrationConfig { @@ -1723,9 +1797,9 @@ type IntegrationConfig { integrationType: String! config: JSON! status: IntegrationStatus! - resources: [String!]! createdAt: DateTime! updatedAt: DateTime! + nextRunAt: DateTime } type IntegrationPayload { @@ -1739,6 +1813,18 @@ type IntegrationPayload { result: IntegrationConfig } +"Input for creating an integration configuration" +input CreateIntegrationInput { + "The integration type identifier (e.g., 'aws-cost-and-usage-reports')" + integrationTypeId: String! + + "Integration-specific configuration. Must conform to the integration type's config_schema." + config: JSON! + + "Authentication credentials. Must conform to the integration type's auth_schema." + auth: JSON! +} + "A connection between two nodes in the diagram" type Link { "Unique identifier for the link" @@ -2049,28 +2135,28 @@ type RecentDeployment { "Cost information for a resource" type Cost { "Monthly cost summary" - monthly: Summary + monthly: Summary! "Daily cost summary" - daily: Summary + daily: Summary! } "Summary of costs over a time period" type Summary { - "Previous period's cost sample" + "Previous period's cost sample (amount\/currency may be null)" previous: CostSample! - "Average cost sample for the period" + "Average cost sample for the period (amount\/currency may be null)" average: CostSample! } "A single cost measurement" type CostSample { - "The cost amount" - amount: Float! + "The cost amount (null if no data available)" + amount: Float - "The currency code (e.g. USD)" - currency: String! + "The currency code, e.g. USD (null if no data available)" + currency: String } type ContainerRepositoryAuth { @@ -2105,39 +2191,6 @@ type Connection { updatedAt: DateTime } -"Indicates whether the artifact is used to fetch cloud cost data" -enum CloudCostStatus { - "Currently used to fetch cloud cost data" - ACTIVE - - "Currently using the credential to build dependencies for fetching cloud costs" - PENDING - - "Attempted to create cloud cost dependencies but failed to do so" - FAILED - - "Artifact does not support fetching cloud cost data" - UNSUPPORTED - - "Eligable to be used for fetching cloud costs but is not in use" - INACTIVE -} - -type CloudCostCredential { - status: CloudCostStatus! -} - -type CloudCostCredentialPayload { - "Indicates if the mutation completed successfully or not." - successful: Boolean! - - "A list of failed validations. May be blank or null if mutation succeeded." - messages: [ValidationMessage] - - "The object created\/updated\/deleted by the mutation. May be null if mutation failed." - result: CloudCostCredential -} - input Cursor { "Maximum number of items to return" limit: Int @@ -2845,7 +2898,7 @@ type Artifact { field: String "Artifact specs for backward compatibility." - specs: JSON @deprecated(reason: "Use payload instead") + specs: JSON @deprecated(reason: "Use payload['specs'] instead if applicable") "Complete artifact payload containing all artifact data. Fields marked with $md.sensitive in the artifact definition will be masked as [SENSITIVE]. Use downloadArtifact to retrieve unmasked values. See https:\/\/docs.massdriver.cloud\/json-schema-cheat-sheet\/massdriver-annotations for more details." payload: JSON @@ -2876,8 +2929,6 @@ type Artifact { "Packages that remotely reference this artifact" referencedBy: [Package]! - - cloudCostStatus: CloudCostStatus! } type ArtifactPayload { diff --git a/pkg/api/zz_generated.go b/pkg/api/zz_generated.go index fde7f65..2a0ebc2 100644 --- a/pkg/api/zz_generated.go +++ b/pkg/api/zz_generated.go @@ -2644,7 +2644,7 @@ type getEnvironmentByIdEnvironment struct { Name string `json:"name"` Slug string `json:"slug"` Description string `json:"description"` - // Cloud provider costs for this target + // Cloud provider costs for this environment Cost getEnvironmentByIdEnvironmentCost `json:"cost"` Packages []getEnvironmentByIdEnvironmentPackagesPackage `json:"packages"` Project getEnvironmentByIdEnvironmentProject `json:"project"` @@ -2701,7 +2701,7 @@ func (v *getEnvironmentByIdEnvironmentCost) GetDaily() getEnvironmentByIdEnviron // // Summary of costs over a time period type getEnvironmentByIdEnvironmentCostDailySummary struct { - // Average cost sample for the period + // Average cost sample for the period (amount/currency may be null) Average getEnvironmentByIdEnvironmentCostDailySummaryAverageCostSample `json:"average"` } @@ -2715,12 +2715,12 @@ func (v *getEnvironmentByIdEnvironmentCostDailySummary) GetAverage() getEnvironm // // A single cost measurement type getEnvironmentByIdEnvironmentCostDailySummaryAverageCostSample struct { - // The cost amount - Amount float64 `json:"amount"` + // The cost amount (null if no data available) + Amount *float64 `json:"amount"` } // GetAmount returns getEnvironmentByIdEnvironmentCostDailySummaryAverageCostSample.Amount, and is useful for accessing the field via an interface. -func (v *getEnvironmentByIdEnvironmentCostDailySummaryAverageCostSample) GetAmount() float64 { +func (v *getEnvironmentByIdEnvironmentCostDailySummaryAverageCostSample) GetAmount() *float64 { return v.Amount } @@ -2729,7 +2729,7 @@ func (v *getEnvironmentByIdEnvironmentCostDailySummaryAverageCostSample) GetAmou // // Summary of costs over a time period type getEnvironmentByIdEnvironmentCostMonthlySummary struct { - // Average cost sample for the period + // Average cost sample for the period (amount/currency may be null) Average getEnvironmentByIdEnvironmentCostMonthlySummaryAverageCostSample `json:"average"` } @@ -2743,12 +2743,12 @@ func (v *getEnvironmentByIdEnvironmentCostMonthlySummary) GetAverage() getEnviro // // A single cost measurement type getEnvironmentByIdEnvironmentCostMonthlySummaryAverageCostSample struct { - // The cost amount - Amount float64 `json:"amount"` + // The cost amount (null if no data available) + Amount *float64 `json:"amount"` } // GetAmount returns getEnvironmentByIdEnvironmentCostMonthlySummaryAverageCostSample.Amount, and is useful for accessing the field via an interface. -func (v *getEnvironmentByIdEnvironmentCostMonthlySummaryAverageCostSample) GetAmount() float64 { +func (v *getEnvironmentByIdEnvironmentCostMonthlySummaryAverageCostSample) GetAmount() *float64 { return v.Amount } @@ -3103,7 +3103,7 @@ type getEnvironmentsByProjectProjectEnvironmentsEnvironment struct { Name string `json:"name"` Slug string `json:"slug"` Description string `json:"description"` - // Cloud provider costs for this target + // Cloud provider costs for this environment Cost getEnvironmentsByProjectProjectEnvironmentsEnvironmentCost `json:"cost"` Packages []getEnvironmentsByProjectProjectEnvironmentsEnvironmentPackagesPackage `json:"packages"` Project getEnvironmentsByProjectProjectEnvironmentsEnvironmentProject `json:"project"` @@ -3164,7 +3164,7 @@ func (v *getEnvironmentsByProjectProjectEnvironmentsEnvironmentCost) GetDaily() // // Summary of costs over a time period type getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostDailySummary struct { - // Average cost sample for the period + // Average cost sample for the period (amount/currency may be null) Average getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostDailySummaryAverageCostSample `json:"average"` } @@ -3178,12 +3178,12 @@ func (v *getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostDailySummary) // // A single cost measurement type getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostDailySummaryAverageCostSample struct { - // The cost amount - Amount float64 `json:"amount"` + // The cost amount (null if no data available) + Amount *float64 `json:"amount"` } // GetAmount returns getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostDailySummaryAverageCostSample.Amount, and is useful for accessing the field via an interface. -func (v *getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostDailySummaryAverageCostSample) GetAmount() float64 { +func (v *getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostDailySummaryAverageCostSample) GetAmount() *float64 { return v.Amount } @@ -3192,7 +3192,7 @@ func (v *getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostDailySummaryA // // Summary of costs over a time period type getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostMonthlySummary struct { - // Average cost sample for the period + // Average cost sample for the period (amount/currency may be null) Average getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostMonthlySummaryAverageCostSample `json:"average"` } @@ -3206,12 +3206,12 @@ func (v *getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostMonthlySummar // // A single cost measurement type getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostMonthlySummaryAverageCostSample struct { - // The cost amount - Amount float64 `json:"amount"` + // The cost amount (null if no data available) + Amount *float64 `json:"amount"` } // GetAmount returns getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostMonthlySummaryAverageCostSample.Amount, and is useful for accessing the field via an interface. -func (v *getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostMonthlySummaryAverageCostSample) GetAmount() float64 { +func (v *getEnvironmentsByProjectProjectEnvironmentsEnvironmentCostMonthlySummaryAverageCostSample) GetAmount() *float64 { return v.Amount } @@ -4242,7 +4242,7 @@ func (v *getProjectsProjectsProjectCost) GetDaily() getProjectsProjectsProjectCo // // Summary of costs over a time period type getProjectsProjectsProjectCostDailySummary struct { - // Average cost sample for the period + // Average cost sample for the period (amount/currency may be null) Average getProjectsProjectsProjectCostDailySummaryAverageCostSample `json:"average"` } @@ -4256,12 +4256,12 @@ func (v *getProjectsProjectsProjectCostDailySummary) GetAverage() getProjectsPro // // A single cost measurement type getProjectsProjectsProjectCostDailySummaryAverageCostSample struct { - // The cost amount - Amount float64 `json:"amount"` + // The cost amount (null if no data available) + Amount *float64 `json:"amount"` } // GetAmount returns getProjectsProjectsProjectCostDailySummaryAverageCostSample.Amount, and is useful for accessing the field via an interface. -func (v *getProjectsProjectsProjectCostDailySummaryAverageCostSample) GetAmount() float64 { +func (v *getProjectsProjectsProjectCostDailySummaryAverageCostSample) GetAmount() *float64 { return v.Amount } @@ -4270,7 +4270,7 @@ func (v *getProjectsProjectsProjectCostDailySummaryAverageCostSample) GetAmount( // // Summary of costs over a time period type getProjectsProjectsProjectCostMonthlySummary struct { - // Average cost sample for the period + // Average cost sample for the period (amount/currency may be null) Average getProjectsProjectsProjectCostMonthlySummaryAverageCostSample `json:"average"` } @@ -4284,12 +4284,12 @@ func (v *getProjectsProjectsProjectCostMonthlySummary) GetAverage() getProjectsP // // A single cost measurement type getProjectsProjectsProjectCostMonthlySummaryAverageCostSample struct { - // The cost amount - Amount float64 `json:"amount"` + // The cost amount (null if no data available) + Amount *float64 `json:"amount"` } // GetAmount returns getProjectsProjectsProjectCostMonthlySummaryAverageCostSample.Amount, and is useful for accessing the field via an interface. -func (v *getProjectsProjectsProjectCostMonthlySummaryAverageCostSample) GetAmount() float64 { +func (v *getProjectsProjectsProjectCostMonthlySummaryAverageCostSample) GetAmount() *float64 { return v.Amount }