Skip to content

feat: add GetDynamicDashboard to DashboardService - #441

Merged
NickJosevski merged 5 commits into
mainfrom
nj/dashboard-dynamic
Aug 12, 2026
Merged

feat: add GetDynamicDashboard to DashboardService#441
NickJosevski merged 5 commits into
mainfrom
nj/dashboard-dynamic

Conversation

@NickJosevski

@NickJosevski NickJosevski commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Problem

DashboardService is wired up in the client with both its Dashboard and DashboardDynamic links, but carries no methods, so the dynamic dashboard is unreachable through the SDK.

This is the data behind the legacy octo list-latestdeployments command. The new CLI has no equivalent, which is OctopusDeploy/cli#305.

Change

GetDynamicDashboard(query) returns the release currently deployed to each environment, optionally narrowed by project and environment, and optionally including the deployment preceding the current one. A zero-valued query returns everything the caller can see.

Adds a Dashboard response type holding the items plus the reference data needed to resolve the IDs they carry — Projects, ProjectGroups, Environments and Tenants. ProjectLimit is a *int so callers can tell an uncapped dashboard from one capped at zero.

Completes DashboardItem, which was missing five fields the server returns: Id and Links (via a resources.Resource embed), PendingInterruptionTypes, HasPendingPreconditions and PendingPreconditionTypes. PendingInterruptionTypes is []interruptions.InterruptionType. PendingPreconditionTypes is []string, because server side it is an open string set rather than a fixed enum.

Path expansion is split into an unexported getDynamicDashboardPath so the query-to-URL mapping is unit testable without HTTP. This differs from sibling services that inline GetURITemplate().Expand(...), because the dynamic dashboard has its own link rather than living under the service's URI template.

GetDashboard for the non-dynamic endpoint is deliberately left unimplemented; tracked in #442.

Server behaviour, as measured

Verified by request against a live server. Counts are from that instance.

Request Items IsFiltered
no parameters 10 false
projects=<id> 3 true
projects=<name> 0 true
projects=<id>,<id> (comma-joined) 4 true
projects=<id>&projects=<id> (repeated) 4 true
projects=Projects-999999 0 true
projects=<id>,Projects-999999 3 true
projects= (empty) 10 false
projects=<lowercased id> 3 true
environments=<id> 1 true
environments=<name> 0 true
includePrevious=true 18 (8 flagged IsPrevious) false
includePrevious=1 or garbage HTTP 400

What this establishes:

  • Filtering matches IDs only. A name is not rejected, it matches nothing, so a name-filtered call returns an empty dashboard indistinguishable from nothing being deployed. Documented on the query fields and the method, and pinned by an e2e test so a future server accepting names shows up as a failure.
  • Unknown and malformed IDs behave the same way — silently dropped. Mixing a valid ID with an invalid one returns the valid one's items only.
  • ID matching is case-insensitive; an empty parameter value is treated as absent.
  • Comma-joined and repeated list parameters are equivalent, so the RFC 6570 expansion this uses is correct.
  • Previous deployments arrive inside Items flagged with IsPrevious, not in a separate collection. There is no PreviousItems on this response.
  • IsFiltered reflects project and environment narrowing only; includePrevious does not set it.
  • Booleans are validated strictly while IDs are not, which is why a bad ID is quieter than a bad flag.

Tests

Unit tests cover construction, table-driven URI expansion, and deserialisation pinned against a capture from a real server.

E2e tests exercise the service against a live server: unfiltered reference-data resolution (every item's project and environment must resolve, and every item must carry Id and Links), filtering by project and environment ID, the empty result a name produces, unknown IDs, and includePrevious. Fixtures are discovered from the server rather than hardcoded, so they run on any instance.

One of them decodes the live response with DisallowUnknownFields, so a field the server adds or renames fails a test instead of silently arriving as a zero value. That check is what caught Environments dropping Links — it had been modelled as resources.ReferenceDataItem, which carries only Id and Name; it is now a DashboardEnvironment.

go build ./..., go vet ./pkg/dashboard/... and go test ./pkg/dashboard/... are clean.

🤖 Generated with Claude Code

Comment thread pkg/dashboard/dashboard.go Outdated
// dynamic dashboard endpoints: the deployments themselves in Items, plus the
// reference data needed to resolve the IDs those items carry.
type Dashboard struct {
Items []*DashboardItem `json:"Items"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check that DashboardItem is correct. When I did this change recently, it was missing some properties iirc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The missing ones are there now, I did more testing on local instance, and it came up with more test cases

Id, Links, PendingInterruptionTypes, HasPendingPreconditions, PendingPreconditionTypes

Comment thread pkg/dashboard/dashboard.go
DashboardService had no methods, so the dynamic dashboard endpoint was
unreachable from the SDK. DashboardDynamicQuery and DashboardItem already
existed.

Adds GetDynamicDashboard and the Dashboard response type it returns.
ProjectLimit is a pointer so callers can tell an uncapped dashboard from
one capped at zero.

Backs OctopusDeploy/cli#305.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@sathvikkumar-octo sathvikkumar-octo left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable.

Did a quick smoke test and I think everything needs to be IDs and not names and that tracks in the server handler for this

Comment thread pkg/dashboard/dashboard.go
NickJosevski and others added 4 commits August 11, 2026 21:57
…n IDs

DashboardItem was missing five fields the server returns: Id and Links
(added by embedding resources.Resource), PendingInterruptionTypes,
HasPendingPreconditions and PendingPreconditionTypes.

The dynamic dashboard matches projects and environments on ID only. A name
matches nothing and returns an empty dashboard rather than an error, so note
that on the query fields and the method.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Environments used resources.ReferenceDataItem, which has only Id and Name,
so the Links the server returns per environment were dropped. Replaced with
DashboardEnvironment, matching the other reference types on this response.

Adds e2e coverage exercising the service against a live server: unfiltered
reference-data resolution, filtering by project and environment ID, the
empty result a name produces, includePrevious, and a strict decode that
fails if the server returns a field the SDK does not model.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These tests read whatever the target instance holds rather than creating a
deployment, so they failed against a CI instance with an empty dashboard.
Anything needing an actual deployment now skips instead of asserting on an
empty collection. The strict-decode test still runs, covering the envelope
and logging that item fields went uncovered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PendingInterruptionTypes is a fixed five-value enum server side, so type it
rather than passing strings. PendingPreconditionTypes stays []string: server
side it is an open string set, not an enum.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@NickJosevski
NickJosevski merged commit b263a38 into main Aug 12, 2026
6 checks passed
@NickJosevski
NickJosevski deleted the nj/dashboard-dynamic branch August 12, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants