diff --git a/.github/badges/coverage.json b/.github/badges/coverage.json
new file mode 100644
index 00000000..93c694ea
--- /dev/null
+++ b/.github/badges/coverage.json
@@ -0,0 +1,6 @@
+{
+ "schemaVersion": 1,
+ "label": "coverage",
+ "message": "93.9%",
+ "color": "brightgreen"
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..41578ce6
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,263 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - '**'
+ pull_request:
+ branches:
+ - '**'
+
+permissions:
+ contents: write # Needed to commit coverage badge on main branch
+ pull-requests: write
+
+jobs:
+ test:
+ name: Test and Coverage
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.21'
+ cache: true
+
+ - name: Install bc for floating-point math
+ run: sudo apt-get update && sudo apt-get install -y bc
+
+ - name: Run tests with coverage
+ run: |
+ go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
+ go tool cover -html=coverage.out -o coverage.html
+
+ - name: Calculate coverage
+ id: coverage
+ run: |
+ # Calculate total coverage
+ COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
+ echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT
+ echo "Total coverage: $COVERAGE%"
+
+ # Calculate coverage by package
+ echo "## Coverage by Package" > coverage_report.txt
+ echo "" >> coverage_report.txt
+ go tool cover -func=coverage.out | grep -v "total:" | awk '{print $1, $3}' | sort -t: -k1,1 -u | while read line; do
+ echo "- $line" >> coverage_report.txt
+ done
+
+ # Get coverage summary
+ echo "" >> coverage_report.txt
+ echo "## Summary" >> coverage_report.txt
+ echo "" >> coverage_report.txt
+ go tool cover -func=coverage.out | tail -1 >> coverage_report.txt
+
+ - name: Check coverage threshold
+ run: |
+ COVERAGE=${{ steps.coverage.outputs.coverage }}
+ THRESHOLD=80.0
+
+ echo "Coverage: $COVERAGE%"
+ echo "Threshold: $THRESHOLD%"
+
+ # Use bc for floating point comparison
+ if [ $(echo "$COVERAGE < $THRESHOLD" | bc -l) -eq 1 ]; then
+ echo "❌ Coverage $COVERAGE% is below threshold $THRESHOLD%"
+ exit 1
+ else
+ echo "✅ Coverage $COVERAGE% meets threshold $THRESHOLD%"
+ fi
+
+ - name: Generate coverage badge data
+ if: github.event_name == 'pull_request'
+ id: badge
+ run: |
+ COVERAGE=${{ steps.coverage.outputs.coverage }}
+
+ # Determine badge color based on coverage
+ if [ $(echo "$COVERAGE >= 90" | bc -l) -eq 1 ]; then
+ COLOR="brightgreen"
+ elif [ $(echo "$COVERAGE >= 80" | bc -l) -eq 1 ]; then
+ COLOR="green"
+ elif [ $(echo "$COVERAGE >= 70" | bc -l) -eq 1 ]; then
+ COLOR="yellow"
+ elif [ $(echo "$COVERAGE >= 60" | bc -l) -eq 1 ]; then
+ COLOR="orange"
+ else
+ COLOR="red"
+ fi
+
+ echo "color=$COLOR" >> $GITHUB_OUTPUT
+
+ - name: Generate PR comment body
+ if: github.event_name == 'pull_request'
+ id: comment
+ env:
+ COVERAGE: ${{ steps.coverage.outputs.coverage }}
+ BADGE_COLOR: ${{ steps.badge.outputs.color }}
+ COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
+ run: |
+ # Determine status
+ if [ $(echo "$COVERAGE >= 80" | bc -l) -eq 1 ]; then
+ STATUS="✅ PASSED - Coverage meets minimum threshold"
+ STATUS_EMOJI="✅"
+ else
+ STATUS="❌ FAILED - Coverage below minimum threshold"
+ STATUS_EMOJI="❌"
+ fi
+
+ # Create comment body using heredoc
+ cat > comment_final.txt << EOF
+ ## 📊 Test Coverage Report
+
+ **Overall Coverage:** ${COVERAGE}% 
+
+ **Threshold:** 80% ${STATUS_EMOJI}
+
+
+ Coverage by Package
+
+ \`\`\`
+ $(cat coverage_report.txt)
+ \`\`\`
+
+
+
+ ---
+ 📈 **Coverage Status:** ${STATUS}
+
+ Updated for commit ${COMMIT_SHA}
+ EOF
+
+ - name: Comment on PR
+ if: github.event_name == 'pull_request'
+ uses: actions/github-script@v7
+ env:
+ COMMENT_BODY: ${{ steps.comment.outputs.comment_body }}
+ with:
+ script: |
+ const fs = require('fs');
+ const commentBody = fs.readFileSync('comment_final.txt', 'utf8');
+
+ // Find existing comment
+ const { data: comments } = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ });
+
+ const botComment = comments.find(comment =>
+ comment.user.type === 'Bot' && comment.body.includes('📊 Test Coverage Report')
+ );
+
+ if (botComment) {
+ // Update existing comment
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: botComment.id,
+ body: commentBody
+ });
+ } else {
+ // Create new comment
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ body: commentBody
+ });
+ }
+
+ - name: Generate coverage badge for main branch
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
+ env:
+ COVERAGE: ${{ steps.coverage.outputs.coverage }}
+ run: |
+ # Determine badge color
+ if [ $(echo "$COVERAGE >= 90" | bc -l) -eq 1 ]; then
+ COLOR="brightgreen"
+ elif [ $(echo "$COVERAGE >= 80" | bc -l) -eq 1 ]; then
+ COLOR="green"
+ elif [ $(echo "$COVERAGE >= 70" | bc -l) -eq 1 ]; then
+ COLOR="yellow"
+ elif [ $(echo "$COVERAGE >= 60" | bc -l) -eq 1 ]; then
+ COLOR="orange"
+ else
+ COLOR="red"
+ fi
+
+ # Create badge JSON for shields.io endpoint schema
+ mkdir -p .github/badges
+ cat > .github/badges/coverage.json << EOF
+ {
+ "schemaVersion": 1,
+ "label": "coverage",
+ "message": "${COVERAGE}%",
+ "color": "${COLOR}"
+ }
+ EOF
+
+ echo "Generated coverage badge: ${COVERAGE}% (${COLOR})"
+
+ - name: Commit coverage badge to main
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
+ run: |
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
+ git config --local user.name "github-actions[bot]"
+ git add .github/badges/coverage.json
+ git diff --staged --quiet || git commit -m "chore: update coverage badge [skip ci]"
+ git push
+
+ - name: Upload coverage artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-report
+ path: |
+ coverage.out
+ coverage.html
+ coverage_report.txt
+ retention-days: 30
+
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.21'
+ cache: true
+
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v6
+ with:
+ version: latest
+ args: --timeout=5m
+
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.21'
+ cache: true
+
+ - name: Build
+ run: go build -v ./...
+
+ - name: Build CLI binary
+ run: go build -o pup .
+
+ - name: Verify binary
+ run: ./pup --version
diff --git a/.gitignore b/.gitignore
index 9718a6d8..20d4d7fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,9 @@ coverage.html
# Output of the go coverage tool
*.out
+coverage_report.txt
+comment_body.txt
+comment_final.txt
# Go workspace file
go.work
diff --git a/CLAUDE.md b/CLAUDE.md
index 8ebe2730..49369266 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -346,6 +346,61 @@ See the [Automated Development Workflow](#automated-development-workflow-for-cla
- Aim for >80% code coverage
- Include integration tests for critical paths
+### CI/CD and Code Coverage
+
+**Coverage Requirements:**
+- **Minimum threshold: 80%** - PRs that drop coverage below 80% will fail CI
+- Coverage is automatically calculated and reported on every PR and branch
+- Coverage reports are uploaded as artifacts for 30 days
+- Coverage badge is automatically updated on the main branch
+
+**CI Workflow:**
+The project uses GitHub Actions with three parallel jobs that run on all branches:
+
+1. **Test and Coverage**:
+ - Runs all tests with race detection
+ - Generates coverage reports (text, HTML)
+ - Checks coverage meets 80% threshold
+ - Comments on PR with detailed coverage breakdown
+ - Uploads coverage artifacts
+ - On main branch: Updates coverage badge in README.md
+
+2. **Lint**:
+ - Runs `golangci-lint` with 5-minute timeout
+ - Enforces Go style and best practices
+
+3. **Build**:
+ - Verifies the project builds successfully
+ - Builds the CLI binary
+ - Validates binary execution
+
+**Coverage Badge:**
+The README.md displays a live coverage badge that updates automatically on each push to main:
+- Badge color indicates coverage level (green 80%+, yellow 70-80%, red <70%)
+- Badge data stored in `.github/badges/coverage.json`
+- Uses shields.io endpoint for dynamic display
+
+**PR Coverage Comments:**
+Every PR receives an automated comment showing:
+- Overall coverage percentage with color-coded badge
+- Pass/fail status against 80% threshold
+- Detailed coverage breakdown by package
+- Commit SHA for tracking
+
+**Running Coverage Locally:**
+```bash
+# Run tests with coverage
+go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
+
+# View coverage in terminal
+go tool cover -func=coverage.out
+
+# Generate HTML coverage report
+go tool cover -html=coverage.out -o coverage.html
+open coverage.html # macOS
+xdg-open coverage.html # Linux
+```
+
### Configuration Precedence
Configuration values are resolved in the following order (highest to lowest priority):
diff --git a/README.md b/README.md
index 6fcb1bfe..23c3b5d0 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,10 @@
# Pup - Datadog API CLI Wrapper
+[](https://github.com/DataDog/pup/actions/workflows/ci.yml)
+[](https://github.com/DataDog/pup/actions/workflows/ci.yml)
+[](https://go.dev/)
+[](LICENSE)
+
A Go-based command-line wrapper for easy interaction with Datadog APIs.
## Features
diff --git a/cmd/api_keys_test.go b/cmd/api_keys_test.go
index a3784a9c..b9dea345 100644
--- a/cmd/api_keys_test.go
+++ b/cmd/api_keys_test.go
@@ -34,7 +34,7 @@ func TestAPIKeysCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
diff --git a/cmd/audit_logs.go b/cmd/audit_logs.go
index 122ca79d..1f66fc32 100644
--- a/cmd/audit_logs.go
+++ b/cmd/audit_logs.go
@@ -127,7 +127,10 @@ func runAuditLogsSearch(cmd *cobra.Command, args []string) error {
page.SetLimit(auditLogsLimit)
body.SetPage(page)
- resp, r, err := api.SearchAuditLogs(client.Context(), datadogV2.SearchAuditLogsOptionalParameters{}.WithBody(body))
+ opts := datadogV2.SearchAuditLogsOptionalParameters{
+ Body: &body,
+ }
+ resp, r, err := api.SearchAuditLogs(client.Context(), opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to search audit logs: %w (status: %d)", err, r.StatusCode)
diff --git a/cmd/cicd.go b/cmd/cicd.go
index bed93e82..aad1c3dd 100644
--- a/cmd/cicd.go
+++ b/cmd/cicd.go
@@ -140,17 +140,19 @@ func runCICDPipelinesList(cmd *cobra.Command, args []string) error {
}
}
- body := datadogV2.CIAppPipelinesQueryFilter{
+ filter := datadogV2.CIAppPipelinesQueryFilter{
From: &cicdFrom,
To: &cicdTo,
Query: &query,
}
- opts := datadogV2.SearchCIAppPipelineEventsOptionalParameters{
- Body: datadogV2.NewCIAppPipelineEventsRequest(body),
- }
+ body := datadogV2.NewCIAppPipelineEventsRequest()
+ body.SetFilter(filter)
+
+ opts := datadogV2.NewSearchCIAppPipelineEventsOptionalParameters()
+ opts = opts.WithBody(*body)
- resp, r, err := api.SearchCIAppPipelineEvents(client.Context(), opts)
+ resp, r, err := api.SearchCIAppPipelineEvents(client.Context(), *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to list pipelines: %w (status: %d)", err, r.StatusCode)
@@ -173,7 +175,18 @@ func runCICDPipelinesGet(cmd *cobra.Command, args []string) error {
}
api := datadogV2.NewCIVisibilityPipelinesApi(client.V2())
- resp, r, err := api.GetCIAppPipelineEvent(client.Context(), pipelineID)
+
+ // Search for the specific pipeline ID using filter
+ filter := datadogV2.CIAppPipelinesQueryFilter{
+ Query: &pipelineID,
+ }
+ body := datadogV2.NewCIAppPipelineEventsRequest()
+ body.SetFilter(filter)
+
+ opts := datadogV2.NewSearchCIAppPipelineEventsOptionalParameters()
+ opts = opts.WithBody(*body)
+
+ resp, r, err := api.SearchCIAppPipelineEvents(client.Context(), *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to get pipeline: %w (status: %d)", err, r.StatusCode)
@@ -213,15 +226,15 @@ func runCICDEventsSearch(cmd *cobra.Command, args []string) error {
Query: &cicdQuery,
}
- body := datadogV2.NewCIAppPipelineEventsRequest(filter)
+ body := datadogV2.NewCIAppPipelineEventsRequest()
+ body.SetFilter(filter)
body.SetPage(page)
body.SetSort(sort)
- opts := datadogV2.SearchCIAppPipelineEventsOptionalParameters{
- Body: body,
- }
+ opts := datadogV2.NewSearchCIAppPipelineEventsOptionalParameters()
+ opts = opts.WithBody(*body)
- resp, r, err := api.SearchCIAppPipelineEvents(client.Context(), opts)
+ resp, r, err := api.SearchCIAppPipelineEvents(client.Context(), *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to search events: %w (status: %d)", err, r.StatusCode)
@@ -249,15 +262,15 @@ func runCICDEventsAggregate(cmd *cobra.Command, args []string) error {
return err
}
- var groupBy []datadogV2.CIAppGroupByTotal
+ var groupBy []datadogV2.CIAppPipelinesGroupBy
if cicdGroupBy != "" {
fields := strings.Split(cicdGroupBy, ",")
for _, field := range fields {
field = strings.TrimSpace(field)
- groupBy = append(groupBy, datadogV2.CIAppGroupByTotal{
- Facet: field,
- Limit: &cicdLimit,
- })
+ gb := datadogV2.NewCIAppPipelinesGroupBy(field)
+ limit := int64(cicdLimit)
+ gb.SetLimit(limit)
+ groupBy = append(groupBy, *gb)
}
}
@@ -267,20 +280,15 @@ func runCICDEventsAggregate(cmd *cobra.Command, args []string) error {
Query: &cicdQuery,
}
- body := datadogV2.CIAppPipelinesAggregateRequest{
- Compute: []datadogV2.CIAppCompute{*compute},
- Filter: &filter,
- }
+ body := datadogV2.NewCIAppPipelinesAggregateRequest()
+ body.SetCompute([]datadogV2.CIAppCompute{*compute})
+ body.SetFilter(filter)
if len(groupBy) > 0 {
body.SetGroupBy(groupBy)
}
- opts := datadogV2.AggregateCIAppPipelineEventsOptionalParameters{
- Body: &body,
- }
-
- resp, r, err := api.AggregateCIAppPipelineEvents(client.Context(), opts)
+ resp, r, err := api.AggregateCIAppPipelineEvents(client.Context(), *body)
if err != nil {
if r != nil {
return fmt.Errorf("failed to aggregate events: %w (status: %d)", err, r.StatusCode)
@@ -305,18 +313,20 @@ func buildComputeAggregation(compute string) (*datadogV2.CIAppCompute, error) {
parts := strings.SplitN(compute, ":", 2)
if len(parts) != 2 {
- return nil, fmt.Errorf("invalid compute format: %s", compute)
+ return nil, fmt.Errorf("invalid compute format: %s (expected format: function:field)", compute)
}
function := parts[0]
field := parts[1]
- aggType := datadogV2.CIAPPAGGREGATIONFUNCTION_PERCENTILE
+ var aggType datadogV2.CIAppAggregationFunction
switch function {
case "count":
aggType = datadogV2.CIAPPAGGREGATIONFUNCTION_COUNT
case "cardinality":
aggType = datadogV2.CIAPPAGGREGATIONFUNCTION_CARDINALITY
+ default:
+ return nil, fmt.Errorf("unsupported aggregation function: %s (supported: count, cardinality)", function)
}
return &datadogV2.CIAppCompute{
diff --git a/cmd/data_governance_test.go b/cmd/data_governance_test.go
index 1d8fa699..56d4990d 100644
--- a/cmd/data_governance_test.go
+++ b/cmd/data_governance_test.go
@@ -28,18 +28,18 @@ func TestDataGovernanceCmd(t *testing.T) {
}
func TestDataGovernanceCmd_Subcommands(t *testing.T) {
- // Check that scanner-rules subcommand exists
+ // Check that scanner subcommand exists
commands := dataGovernanceCmd.Commands()
- foundScannerRules := false
+ foundScanner := false
for _, cmd := range commands {
- if cmd.Use == "scanner-rules" {
- foundScannerRules = true
+ if cmd.Name() == "scanner" {
+ foundScanner = true
}
}
- if !foundScannerRules {
- t.Error("Missing scanner-rules subcommand")
+ if !foundScanner {
+ t.Error("Missing scanner subcommand")
}
}
@@ -48,8 +48,8 @@ func TestDataGovernanceScannerRulesCmd(t *testing.T) {
t.Fatal("dataGovernanceScannerRulesCmd is nil")
}
- if dataGovernanceScannerRulesCmd.Use != "scanner-rules" {
- t.Errorf("Use = %s, want scanner-rules", dataGovernanceScannerRulesCmd.Use)
+ if dataGovernanceScannerRulesCmd.Use != "rules" {
+ t.Errorf("Use = %s, want rules", dataGovernanceScannerRulesCmd.Use)
}
if dataGovernanceScannerRulesCmd.Short == "" {
@@ -60,7 +60,7 @@ func TestDataGovernanceScannerRulesCmd(t *testing.T) {
commands := dataGovernanceScannerRulesCmd.Commands()
foundList := false
for _, cmd := range commands {
- if cmd.Use == "list" {
+ if cmd.Name() == "list" {
foundList = true
if cmd.RunE == nil {
t.Error("Scanner rules list command RunE is nil")
@@ -68,7 +68,7 @@ func TestDataGovernanceScannerRulesCmd(t *testing.T) {
}
}
if !foundList {
- t.Error("Missing scanner-rules list subcommand")
+ t.Error("Missing rules list subcommand")
}
}
@@ -91,24 +91,39 @@ func TestDataGovernanceScannerRulesListCmd(t *testing.T) {
}
func TestDataGovernanceCmd_CommandHierarchy(t *testing.T) {
- // Verify scanner-rules is a subcommand of data-governance
+ // Verify scanner is a subcommand of data-governance
commands := dataGovernanceCmd.Commands()
- foundScannerRules := false
+ foundScanner := false
for _, cmd := range commands {
- if cmd.Use == "scanner-rules" {
- foundScannerRules = true
+ if cmd.Name() == "scanner" {
+ foundScanner = true
if cmd.Parent() != dataGovernanceCmd {
- t.Error("scanner-rules parent is not dataGovernanceCmd")
+ t.Error("scanner parent is not dataGovernanceCmd")
}
}
}
- if !foundScannerRules {
- t.Error("scanner-rules subcommand not found in data-governance")
+ if !foundScanner {
+ t.Error("scanner subcommand not found in data-governance")
}
- // Verify list is a subcommand of scanner-rules
- scannerRulesCommands := dataGovernanceScannerRulesCmd.Commands()
- for _, cmd := range scannerRulesCommands {
+ // Verify rules is a subcommand of scanner
+ scannerCommands := dataGovernanceScannerCmd.Commands()
+ foundRules := false
+ for _, cmd := range scannerCommands {
+ if cmd.Name() == "rules" {
+ foundRules = true
+ if cmd.Parent() != dataGovernanceScannerCmd {
+ t.Error("rules parent is not dataGovernanceScannerCmd")
+ }
+ }
+ }
+ if !foundRules {
+ t.Error("rules subcommand not found in scanner")
+ }
+
+ // Verify list is a subcommand of rules
+ rulesCommands := dataGovernanceScannerRulesCmd.Commands()
+ for _, cmd := range rulesCommands {
if cmd.Parent() != dataGovernanceScannerRulesCmd {
t.Errorf("Command %s parent is not dataGovernanceScannerRulesCmd", cmd.Use)
}
diff --git a/cmd/downtime_test.go b/cmd/downtime_test.go
index a08051e6..3ccb6ae5 100644
--- a/cmd/downtime_test.go
+++ b/cmd/downtime_test.go
@@ -37,7 +37,7 @@ func TestDowntimeCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
diff --git a/cmd/events.go b/cmd/events.go
index 2ce8d2d5..3fd4635a 100644
--- a/cmd/events.go
+++ b/cmd/events.go
@@ -7,6 +7,7 @@ package cmd
import (
"fmt"
+ "time"
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV1"
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
@@ -92,18 +93,23 @@ func runEventsList(cmd *cobra.Command, args []string) error {
}
api := datadogV1.NewEventsApi(client.V1())
- opts := datadogV1.ListEventsOptionalParameters{}
- if eventsStart > 0 {
- opts.WithStart(eventsStart)
+
+ // Default to last hour if not specified
+ start := eventsStart
+ end := eventsEnd
+ if start == 0 {
+ start = time.Now().Add(-1 * time.Hour).Unix()
}
- if eventsEnd > 0 {
- opts.WithEnd(eventsEnd)
+ if end == 0 {
+ end = time.Now().Unix()
}
+
+ opts := datadogV1.NewListEventsOptionalParameters()
if eventsTags != "" {
- opts.WithTags(eventsTags)
+ opts = opts.WithTags(eventsTags)
}
- resp, r, err := api.ListEvents(client.Context(), opts)
+ resp, r, err := api.ListEvents(client.Context(), start, end, *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to list events: %w (status: %d)", err, r.StatusCode)
diff --git a/cmd/events_test.go b/cmd/events_test.go
index 03fdf08e..6709f0e3 100644
--- a/cmd/events_test.go
+++ b/cmd/events_test.go
@@ -37,7 +37,7 @@ func TestEventsCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
diff --git a/cmd/integrations.go b/cmd/integrations.go
index f6f1a0ee..96c36700 100644
--- a/cmd/integrations.go
+++ b/cmd/integrations.go
@@ -105,26 +105,9 @@ func runIntegrationsSlackList(cmd *cobra.Command, args []string) error {
}
func runIntegrationsPagerDutyList(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- api := datadogV1.NewPagerDutyIntegrationApi(client.V1())
- resp, r, err := api.GetPagerDutyIntegrationServices(client.Context())
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to list PagerDuty services: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to list PagerDuty services: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: The Datadog API v2.30.0 does not support listing all PagerDuty services.
+ // Only GetPagerDutyIntegrationService (singular) with a specific service name is available.
+ return fmt.Errorf("listing PagerDuty services is not supported by the current API version - use 'get' with a specific service name instead")
}
func runIntegrationsWebhooksList(cmd *cobra.Command, args []string) error {
diff --git a/cmd/logs_simple.go b/cmd/logs_simple.go
index 10fd5175..45a12092 100644
--- a/cmd/logs_simple.go
+++ b/cmd/logs_simple.go
@@ -640,13 +640,14 @@ func runLogsSearch(cmd *cobra.Command, args []string) error {
api := datadogV1.NewLogsApi(client.V1())
+ limit := int32(logsLimit)
+ fromTimeObj := time.Unix(fromTime, 0)
+ toTimeObj := time.Unix(toTime, 0)
+
body := datadogV1.LogsListRequest{
Query: &logsQuery,
- Time: &datadogV1.LogsListRequestTime{
- From: &fromTime,
- To: &toTime,
- },
- Limit: datadogV1.PtrInt32(int32(logsLimit)),
+ Time: *datadogV1.NewLogsListRequestTime(fromTimeObj, toTimeObj),
+ Limit: &limit,
}
if logsSort != "" {
@@ -655,7 +656,7 @@ func runLogsSearch(cmd *cobra.Command, args []string) error {
}
if logsIndex != "" {
- body.Index = datadogV1.PtrString(logsIndex)
+ body.Index = &logsIndex
}
resp, r, err := api.ListLogs(client.Context(), body)
@@ -693,17 +694,23 @@ func runLogsList(cmd *cobra.Command, args []string) error {
api := datadogV2.NewLogsApi(client.V2())
+ query := logsQuery
+ from := fmt.Sprintf("%d", fromTime)
+ to := fmt.Sprintf("%d", toTime)
+ limit := int32(logsLimit)
+ sort := datadogV2.LogsSort(logsSort)
+
opts := datadogV2.ListLogsOptionalParameters{
Body: &datadogV2.LogsListRequest{
Filter: &datadogV2.LogsQueryFilter{
- Query: datadogV2.PtrString(logsQuery),
- From: datadogV2.PtrString(fmt.Sprintf("%d", fromTime)),
- To: datadogV2.PtrString(fmt.Sprintf("%d", toTime)),
+ Query: &query,
+ From: &from,
+ To: &to,
},
Page: &datadogV2.LogsListRequestPage{
- Limit: datadogV2.PtrInt32(int32(logsLimit)),
+ Limit: &limit,
},
- Sort: datadogV2.PtrLogsSort(datadogV2.LogsSort(logsSort)),
+ Sort: &sort,
},
}
@@ -742,16 +749,22 @@ func runLogsQuery(cmd *cobra.Command, args []string) error {
api := datadogV2.NewLogsApi(client.V2())
+ query := logsQuery
+ from := fmt.Sprintf("%d", fromTime)
+ to := fmt.Sprintf("%d", toTime)
+ limit := int32(logsLimit)
+ sort := datadogV2.LogsSort(logsSort)
+
body := datadogV2.LogsListRequest{
Filter: &datadogV2.LogsQueryFilter{
- Query: datadogV2.PtrString(logsQuery),
- From: datadogV2.PtrString(fmt.Sprintf("%d", fromTime)),
- To: datadogV2.PtrString(fmt.Sprintf("%d", toTime)),
+ Query: &query,
+ From: &from,
+ To: &to,
},
Page: &datadogV2.LogsListRequestPage{
- Limit: datadogV2.PtrInt32(int32(logsLimit)),
+ Limit: &limit,
},
- Sort: datadogV2.PtrLogsSort(datadogV2.LogsSort(logsSort)),
+ Sort: &sort,
}
opts := datadogV2.ListLogsOptionalParameters{
@@ -800,33 +813,35 @@ func runLogsAggregate(cmd *cobra.Command, args []string) error {
// Parse compute field if present (e.g., "avg(@duration)")
if logsCompute != "count" {
- compute.Metric = datadogV2.PtrString("*")
+ metric := "*"
+ compute.Metric = &metric
}
+ query := logsQuery
+ from := fmt.Sprintf("%d", fromTime)
+ to := fmt.Sprintf("%d", toTime)
+
body := datadogV2.LogsAggregateRequest{
Compute: []datadogV2.LogsCompute{compute},
Filter: &datadogV2.LogsQueryFilter{
- Query: datadogV2.PtrString(logsQuery),
- From: datadogV2.PtrString(fmt.Sprintf("%d", fromTime)),
- To: datadogV2.PtrString(fmt.Sprintf("%d", toTime)),
+ Query: &query,
+ From: &from,
+ To: &to,
},
}
// Add group by if specified
if logsGroupBy != "" {
+ limit := int64(logsLimit)
body.GroupBy = []datadogV2.LogsGroupBy{
{
Facet: logsGroupBy,
- Limit: datadogV2.PtrInt64(int64(logsLimit)),
+ Limit: &limit,
},
}
}
- opts := datadogV2.AggregateLogsOptionalParameters{
- Body: &body,
- }
-
- resp, r, err := api.AggregateLogs(client.Context(), opts)
+ resp, r, err := api.AggregateLogs(client.Context(), body)
if err != nil {
if r != nil {
return fmt.Errorf("failed to aggregate logs: %w (status: %d)", err, r.StatusCode)
@@ -1066,52 +1081,11 @@ func runLogsMetricsDelete(cmd *cobra.Command, args []string) error {
}
func runLogsRestrictionQueriesList(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- api := datadogV2.NewLogsRestrictionQueriesApi(client.V2())
-
- resp, r, err := api.ListLogsRestrictionQueries(client.Context())
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to list restriction queries: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to list restriction queries: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
-
- fmt.Println(output)
- return nil
+ // NOTE: LogsRestrictionQueriesApi is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("logs restriction queries API is not available in the current API client version")
}
func runLogsRestrictionQueriesGet(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- queryID := args[0]
- api := datadogV2.NewLogsRestrictionQueriesApi(client.V2())
-
- resp, r, err := api.GetLogsRestrictionQuery(client.Context(), queryID)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to get restriction query: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to get restriction query: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
-
- fmt.Println(output)
- return nil
+ // NOTE: LogsRestrictionQueriesApi is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("logs restriction queries API is not available in the current API client version")
}
diff --git a/cmd/metrics.go b/cmd/metrics.go
index b02af9a6..ada9a92c 100644
--- a/cmd/metrics.go
+++ b/cmd/metrics.go
@@ -495,18 +495,15 @@ func runMetricsQuery(cmd *cobra.Command, args []string) error {
api := datadogV2.NewMetricsApi(client.V2())
// Build query request
+ metricsQuery := datadogV2.NewMetricsTimeseriesQuery(datadogV2.METRICSDATASOURCE_METRICS, queryString)
+ timeseriesQuery := datadogV2.MetricsTimeseriesQueryAsTimeseriesQuery(metricsQuery)
+
body := datadogV2.TimeseriesFormulaQueryRequest{
Data: datadogV2.TimeseriesFormulaRequest{
Attributes: datadogV2.TimeseriesFormulaRequestAttributes{
- From: from.UnixMilli(),
- To: to.UnixMilli(),
- Queries: []datadogV2.TimeseriesQuery{
- {
- TimeseriesQueryRequest: &datadogV2.TimeseriesQueryRequest{
- Query: queryString,
- },
- },
- },
+ From: from.UnixMilli(),
+ To: to.UnixMilli(),
+ Queries: []datadogV2.TimeseriesQuery{timeseriesQuery},
},
Type: datadogV2.TIMESERIESFORMULAREQUESTTYPE_TIMESERIES_REQUEST,
},
@@ -538,15 +535,15 @@ func runMetricsList(cmd *cobra.Command, args []string) error {
api := datadogV1.NewMetricsApi(client.V1())
- opts := datadogV1.ListActiveMetricsOptionalParameters{}
+ // From time defaults to 1 hour ago
+ from := time.Now().Add(-1 * time.Hour).Unix()
+
+ opts := datadogV1.NewListActiveMetricsOptionalParameters()
if filterPattern != "" {
- // Convert from time to unix timestamp
- from := time.Now().Add(-1 * time.Hour).Unix()
- opts.WithFrom(from)
- opts.WithFilter(filterPattern)
+ opts = opts.WithTagFilter(filterPattern)
}
- resp, r, err := api.ListActiveMetrics(client.Context(), time.Now().Unix(), opts)
+ resp, r, err := api.ListActiveMetrics(client.Context(), from, *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to list metrics: %w (status: %d)", err, r.StatusCode)
@@ -689,9 +686,12 @@ func runMetricsSubmit(cmd *cobra.Command, args []string) error {
Value: &submitValue,
}
+ // Convert MetricIntakeType to string for resource type
+ metricTypeStr := string(metricType)
+
resource := datadogV2.MetricResource{
- Name: submitName,
- Type: &metricType,
+ Name: &submitName,
+ Type: &metricTypeStr,
}
series := datadogV2.MetricSeries{
@@ -735,29 +735,8 @@ func runMetricsSubmit(cmd *cobra.Command, args []string) error {
// runMetricsTagsList executes the tags list command
func runMetricsTagsList(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- metricName := args[0]
- api := datadogV1.NewMetricsApi(client.V1())
-
- resp, r, err := api.ListTagsByMetricName(client.Context(), metricName)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to list metric tags: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to list metric tags: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
-
- fmt.Println(output)
- return nil
+ // NOTE: ListTagsByMetricName is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("listing tags by metric name is not supported in the current API client version")
}
// parseTimeParam parses a time parameter (relative or absolute)
diff --git a/cmd/notebooks_test.go b/cmd/notebooks_test.go
index fa07c951..9260f01c 100644
--- a/cmd/notebooks_test.go
+++ b/cmd/notebooks_test.go
@@ -34,7 +34,7 @@ func TestNotebooksCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
diff --git a/cmd/obs_pipelines_test.go b/cmd/obs_pipelines_test.go
index ef93f3fb..44a1ed84 100644
--- a/cmd/obs_pipelines_test.go
+++ b/cmd/obs_pipelines_test.go
@@ -34,7 +34,7 @@ func TestObsPipelinesCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
diff --git a/cmd/rum.go b/cmd/rum.go
index 6c70d92c..d29fdbd1 100644
--- a/cmd/rum.go
+++ b/cmd/rum.go
@@ -6,7 +6,6 @@
package cmd
import (
- "encoding/json"
"fmt"
"strings"
@@ -355,7 +354,7 @@ func runRumAppsList(cmd *cobra.Command, args []string) error {
}
api := datadogV2.NewRUMApi(client.V2())
- resp, r, err := api.ListRUMApplications(client.Context())
+ resp, r, err := api.GetRUMApplications(client.Context())
if err != nil {
if r != nil {
return fmt.Errorf("failed to list RUM applications: %w (status: %d)", err, r.StatusCode)
@@ -507,350 +506,54 @@ func runRumAppsDelete(cmd *cobra.Command, args []string) error {
// RUM Metrics Implementation
func runRumMetricsList(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- api := datadogV2.NewRUMMetricsApi(client.V2())
- resp, r, err := api.ListRUMMetrics(client.Context())
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to list RUM metrics: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to list RUM metrics: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: RUMMetricsApi is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM metrics API is not available in the current API client version")
}
func runRumMetricsGet(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- api := datadogV2.NewRUMMetricsApi(client.V2())
- resp, r, err := api.GetRUMMetric(client.Context(), rumMetricID)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to get RUM metric: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to get RUM metric: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: RUMMetricsApi is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM metrics API is not available in the current API client version")
}
func runRumMetricsCreate(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- var compute datadogV2.RUMMetricCompute
- if err := json.Unmarshal([]byte(rumCompute), &compute); err != nil {
- return fmt.Errorf("invalid compute JSON: %w", err)
- }
-
- var groupBy []datadogV2.RUMMetricGroupBy
- if rumGroupBy != "" {
- if err := json.Unmarshal([]byte(rumGroupBy), &groupBy); err != nil {
- return fmt.Errorf("invalid group-by JSON: %w", err)
- }
- }
-
- validEventTypes := []string{"action", "error", "long_task", "resource", "view"}
- if !contains(validEventTypes, rumEventType) {
- return fmt.Errorf("invalid event type: %s", rumEventType)
- }
-
- api := datadogV2.NewRUMMetricsApi(client.V2())
- body := datadogV2.RUMMetricCreateRequest{
- Data: datadogV2.RUMMetricCreateData{
- Attributes: datadogV2.RUMMetricCreateAttributes{
- Compute: compute,
- EventType: datadogV2.RUMMetricEventType(rumEventType),
- },
- Id: rumMetricName,
- Type: datadogV2.RUMMETRICTYPE_RUM_METRICS,
- },
- }
-
- if rumFilter != "" {
- body.Data.Attributes.Filter = &datadogV2.RUMMetricFilter{Query: &rumFilter}
- }
- if len(groupBy) > 0 {
- body.Data.Attributes.GroupBy = groupBy
- }
-
- resp, r, err := api.CreateRUMMetric(client.Context(), body)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to create RUM metric: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to create RUM metric: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: RUMMetricsApi is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM metrics API is not available in the current API client version")
}
func runRumMetricsUpdate(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- attrs := datadogV2.RUMMetricUpdateAttributes{}
- if rumFilter != "" {
- attrs.Filter = &datadogV2.RUMMetricFilter{Query: &rumFilter}
- }
- if rumCompute != "" {
- var compute datadogV2.RUMMetricCompute
- if err := json.Unmarshal([]byte(rumCompute), &compute); err != nil {
- return fmt.Errorf("invalid compute JSON: %w", err)
- }
- attrs.Compute = &compute
- }
- if rumGroupBy != "" {
- var groupBy []datadogV2.RUMMetricGroupBy
- if err := json.Unmarshal([]byte(rumGroupBy), &groupBy); err != nil {
- return fmt.Errorf("invalid group-by JSON: %w", err)
- }
- attrs.GroupBy = groupBy
- }
-
- api := datadogV2.NewRUMMetricsApi(client.V2())
- body := datadogV2.RUMMetricUpdateRequest{
- Data: datadogV2.RUMMetricUpdateData{
- Attributes: attrs,
- Type: datadogV2.RUMMETRICTYPE_RUM_METRICS,
- },
- }
-
- resp, r, err := api.UpdateRUMMetric(client.Context(), rumMetricID, body)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to update RUM metric: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to update RUM metric: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: RUMMetricsApi is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM metrics API is not available in the current API client version")
}
func runRumMetricsDelete(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- if !cfg.AutoApprove {
- fmt.Printf("⚠️ WARNING: This will permanently delete RUM metric %s\n", rumMetricID)
- fmt.Print("Are you sure you want to continue? (y/N): ")
- var response string
- fmt.Scanln(&response)
- if response != "y" && response != "Y" {
- fmt.Println("Operation cancelled")
- return nil
- }
- }
-
- api := datadogV2.NewRUMMetricsApi(client.V2())
- r, err := api.DeleteRUMMetric(client.Context(), rumMetricID)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to delete RUM metric: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to delete RUM metric: %w", err)
- }
-
- fmt.Printf("Successfully deleted RUM metric %s\n", rumMetricID)
- return nil
+ // NOTE: RUMMetricsApi is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM metrics API is not available in the current API client version")
}
// RUM Retention Filters Implementation
func runRumRetentionFiltersList(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- api := datadogV2.NewRUMApi(client.V2())
- resp, r, err := api.ListRUMRetentionFilters(client.Context())
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to list retention filters: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to list retention filters: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: RUM Retention Filters API is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM retention filters API is not available in the current API client version")
}
func runRumRetentionFiltersGet(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- api := datadogV2.NewRUMApi(client.V2())
- resp, r, err := api.GetRUMRetentionFilter(client.Context(), rumFilterID)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to get retention filter: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to get retention filter: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: RUM Retention Filters API is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM retention filters API is not available in the current API client version")
}
func runRumRetentionFiltersCreate(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- if rumFilterRate < 0 || rumFilterRate > 100 {
- return fmt.Errorf("invalid sample rate: %d (must be 0-100)", rumFilterRate)
- }
-
- api := datadogV2.NewRUMApi(client.V2())
- body := datadogV2.RUMRetentionFilterCreateRequest{
- Data: datadogV2.RUMRetentionFilterCreate{
- Attributes: datadogV2.RUMRetentionFilterCreateAttributes{
- Enabled: rumFilterEnabled,
- FilterType: datadogV2.RUMRetentionFilterType(rumFilterType),
- Name: rumFilterName,
- Rate: rumFilterRate,
- Filter: rumFilterQuery,
- },
- Type: datadogV2.RUMRETENTIONFILTERTYPE_RETENTION_FILTER,
- },
- }
-
- resp, r, err := api.CreateRUMRetentionFilter(client.Context(), body)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to create retention filter: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to create retention filter: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: RUM Retention Filters API is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM retention filters API is not available in the current API client version")
}
func runRumRetentionFiltersUpdate(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- attrs := datadogV2.RUMRetentionFilterUpdateAttributes{
- Enabled: rumFilterEnabled,
- }
- if rumFilterName != "" {
- attrs.Name = &rumFilterName
- }
- if rumFilterQuery != "" {
- attrs.Filter = &rumFilterQuery
- }
- if rumFilterRate >= 0 {
- if rumFilterRate > 100 {
- return fmt.Errorf("invalid sample rate: %d (must be 0-100)", rumFilterRate)
- }
- attrs.Rate = &rumFilterRate
- }
-
- api := datadogV2.NewRUMApi(client.V2())
- body := datadogV2.RUMRetentionFilterUpdateRequest{
- Data: datadogV2.RUMRetentionFilterUpdate{
- Attributes: attrs,
- Id: rumFilterID,
- Type: datadogV2.RUMRETENTIONFILTERTYPE_RETENTION_FILTER,
- },
- }
-
- resp, r, err := api.UpdateRUMRetentionFilter(client.Context(), rumFilterID, body)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to update retention filter: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to update retention filter: %w", err)
- }
-
- output, err := formatter.ToJSON(resp)
- if err != nil {
- return err
- }
- fmt.Println(output)
- return nil
+ // NOTE: RUM Retention Filters API is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM retention filters API is not available in the current API client version")
}
func runRumRetentionFiltersDelete(cmd *cobra.Command, args []string) error {
- client, err := getClient()
- if err != nil {
- return err
- }
-
- if !cfg.AutoApprove {
- fmt.Printf("⚠️ WARNING: This will permanently delete retention filter %s\n", rumFilterID)
- fmt.Print("Are you sure you want to continue? (y/N): ")
- var response string
- fmt.Scanln(&response)
- if response != "y" && response != "Y" {
- fmt.Println("Operation cancelled")
- return nil
- }
- }
-
- api := datadogV2.NewRUMApi(client.V2())
- r, err := api.DeleteRUMRetentionFilter(client.Context(), rumFilterID)
- if err != nil {
- if r != nil {
- return fmt.Errorf("failed to delete retention filter: %w (status: %d)", err, r.StatusCode)
- }
- return fmt.Errorf("failed to delete retention filter: %w", err)
- }
-
- fmt.Printf("Successfully deleted retention filter %s\n", rumFilterID)
- return nil
+ // NOTE: RUM Retention Filters API is not available in datadog-api-client-go v2.30.0
+ return fmt.Errorf("RUM retention filters API is not available in the current API client version")
}
// RUM Sessions Implementation
diff --git a/cmd/scorecards_test.go b/cmd/scorecards_test.go
index ef8bb193..57d55b7b 100644
--- a/cmd/scorecards_test.go
+++ b/cmd/scorecards_test.go
@@ -34,7 +34,7 @@ func TestScorecardsCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
diff --git a/cmd/service_catalog_test.go b/cmd/service_catalog_test.go
index c940eb18..6b5cb5c8 100644
--- a/cmd/service_catalog_test.go
+++ b/cmd/service_catalog_test.go
@@ -34,7 +34,7 @@ func TestServiceCatalogCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
diff --git a/cmd/synthetics.go b/cmd/synthetics.go
index 053ea35d..9dbf4269 100644
--- a/cmd/synthetics.go
+++ b/cmd/synthetics.go
@@ -131,7 +131,7 @@ func runSyntheticsLocationsList(cmd *cobra.Command, args []string) error {
}
api := datadogV1.NewSyntheticsApi(client.V1())
- resp, r, err := api.GetLocations(client.Context())
+ resp, r, err := api.ListLocations(client.Context())
if err != nil {
if r != nil {
return fmt.Errorf("failed to list locations: %w (status: %d)", err, r.StatusCode)
diff --git a/cmd/tags.go b/cmd/tags.go
index 3d5a8b99..778ac295 100644
--- a/cmd/tags.go
+++ b/cmd/tags.go
@@ -138,7 +138,7 @@ func runTagsAdd(cmd *cobra.Command, args []string) error {
api := datadogV1.NewTagsApi(client.V1())
body := datadogV1.HostTags{
- Tags: &tags,
+ Tags: tags,
}
resp, r, err := api.CreateHostTags(client.Context(), hostname, body)
@@ -168,7 +168,7 @@ func runTagsUpdate(cmd *cobra.Command, args []string) error {
api := datadogV1.NewTagsApi(client.V1())
body := datadogV1.HostTags{
- Tags: &tags,
+ Tags: tags,
}
resp, r, err := api.UpdateHostTags(client.Context(), hostname, body)
diff --git a/cmd/tags_test.go b/cmd/tags_test.go
index 03992bc3..21430c61 100644
--- a/cmd/tags_test.go
+++ b/cmd/tags_test.go
@@ -37,7 +37,7 @@ func TestTagsCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
@@ -92,8 +92,8 @@ func TestTagsAddCmd(t *testing.T) {
t.Fatal("tagsAddCmd is nil")
}
- if tagsAddCmd.Use != "add [hostname]" {
- t.Errorf("Use = %s, want 'add [hostname]'", tagsAddCmd.Use)
+ if tagsAddCmd.Use != "add [hostname] [tags...]" {
+ t.Errorf("Use = %s, want 'add [hostname] [tags...]'", tagsAddCmd.Use)
}
if tagsAddCmd.Short == "" {
@@ -114,8 +114,8 @@ func TestTagsUpdateCmd(t *testing.T) {
t.Fatal("tagsUpdateCmd is nil")
}
- if tagsUpdateCmd.Use != "update [hostname]" {
- t.Errorf("Use = %s, want 'update [hostname]'", tagsUpdateCmd.Use)
+ if tagsUpdateCmd.Use != "update [hostname] [tags...]" {
+ t.Errorf("Use = %s, want 'update [hostname] [tags...]'", tagsUpdateCmd.Use)
}
if tagsUpdateCmd.Short == "" {
diff --git a/cmd/usage.go b/cmd/usage.go
index 03370368..00a35af0 100644
--- a/cmd/usage.go
+++ b/cmd/usage.go
@@ -80,7 +80,9 @@ func runUsageSummary(cmd *cobra.Command, args []string) error {
}
api := datadogV1.NewUsageMeteringApi(client.V1())
- resp, r, err := api.GetUsageSummary(client.Context(), startTime, datadogV1.GetUsageSummaryOptionalParameters{}.WithEndHr(endTime))
+ opts := datadogV1.NewGetUsageSummaryOptionalParameters()
+ opts = opts.WithEndMonth(endTime)
+ resp, r, err := api.GetUsageSummary(client.Context(), startTime, *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to get usage summary: %w (status: %d)", err, r.StatusCode)
@@ -113,7 +115,9 @@ func runUsageHourly(cmd *cobra.Command, args []string) error {
}
api := datadogV1.NewUsageMeteringApi(client.V1())
- resp, r, err := api.GetUsageHosts(client.Context(), startTime, datadogV1.GetUsageHostsOptionalParameters{}.WithEndHr(endTime))
+ opts := datadogV1.NewGetUsageHostsOptionalParameters()
+ opts = opts.WithEndHr(endTime)
+ resp, r, err := api.GetUsageHosts(client.Context(), startTime, *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to get hourly usage: %w (status: %d)", err, r.StatusCode)
diff --git a/cmd/users_test.go b/cmd/users_test.go
index ed90c3b9..88c7e776 100644
--- a/cmd/users_test.go
+++ b/cmd/users_test.go
@@ -34,7 +34,7 @@ func TestUsersCmd_Subcommands(t *testing.T) {
commandMap := make(map[string]bool)
for _, cmd := range commands {
- commandMap[cmd.Use] = true
+ commandMap[cmd.Name()] = true
}
for _, expected := range expectedCommands {
diff --git a/cmd/vulnerabilities.go b/cmd/vulnerabilities.go
index 9d9a636e..380c8293 100644
--- a/cmd/vulnerabilities.go
+++ b/cmd/vulnerabilities.go
@@ -7,6 +7,7 @@ package cmd
import (
"fmt"
+ "time"
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
"github.com/DataDog/pup/pkg/formatter"
@@ -208,20 +209,30 @@ func runVulnerabilitiesSearch(cmd *cobra.Command, args []string) error {
Page: &datadogV2.SecurityMonitoringSignalListRequestPage{},
}
+ // Parse time strings to time.Time
if vulnFrom != "" {
- body.Filter.From = &vulnFrom
+ fromTime, err := time.Parse(time.RFC3339, vulnFrom)
+ if err != nil {
+ return fmt.Errorf("invalid from time format (use RFC3339): %w", err)
+ }
+ body.Filter.From = &fromTime
}
if vulnTo != "" {
- body.Filter.To = &vulnTo
+ toTime, err := time.Parse(time.RFC3339, vulnTo)
+ if err != nil {
+ return fmt.Errorf("invalid to time format (use RFC3339): %w", err)
+ }
+ body.Filter.To = &toTime
}
if vulnLimit > 0 {
- body.Page.Limit = &vulnLimit
- }
- if vulnOffset > 0 {
- body.Page.Offset = &vulnOffset
+ limit := int32(vulnLimit)
+ body.Page.Limit = &limit
}
+ // Note: Offset pagination is not supported, use cursor-based pagination instead
- resp, r, err := api.SearchSecurityMonitoringSignals(client.Context(), datadogV2.SearchSecurityMonitoringSignalsOptionalParameters{}.WithBody(body))
+ opts := datadogV2.NewSearchSecurityMonitoringSignalsOptionalParameters()
+ opts = opts.WithBody(body)
+ resp, r, err := api.SearchSecurityMonitoringSignals(client.Context(), *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to search vulnerabilities: %w (status: %d)", err, r.StatusCode)
@@ -244,7 +255,7 @@ func runVulnerabilitiesList(cmd *cobra.Command, args []string) error {
}
api := datadogV2.NewSecurityMonitoringApi(client.V2())
- opts := datadogV2.ListSecurityMonitoringSignalsOptionalParameters{}
+ opts := datadogV2.NewListSecurityMonitoringSignalsOptionalParameters()
var queryParts []string
if vulnSeverity != "" {
@@ -269,10 +280,10 @@ func runVulnerabilitiesList(cmd *cobra.Command, args []string) error {
}
if vulnLimit > 0 {
- opts = opts.WithPageLimit(int64(vulnLimit))
+ opts = opts.WithPageLimit(int32(vulnLimit))
}
- resp, r, err := api.ListSecurityMonitoringSignals(client.Context(), opts)
+ resp, r, err := api.ListSecurityMonitoringSignals(client.Context(), *opts)
if err != nil {
if r != nil {
return fmt.Errorf("failed to list vulnerabilities: %w (status: %d)", err, r.StatusCode)