Skip to content

Conversation

@cgoetz-inovex
Copy link
Contributor

@cgoetz-inovex cgoetz-inovex commented Nov 19, 2025

Description

Issue: STACKITCLI-70

Checklist

  • Issue was linked above
  • Code format was applied: make fmt
  • Examples were added / adjusted (see e.g. here)
  • Docs are up-to-date: make generate-docs (will be checked by CI)
  • Unit tests got implemented or updated
  • Unit tests are passing: make test (will be checked by CI)
  • No linter issues: make lint (will be checked by CI)

@cgoetz-inovex cgoetz-inovex requested a review from a team as a code owner November 19, 2025 16:39
return cmd
}

var sortByFlagOptions = []string{"id", "created", "updated", "origin-url", "status"}
Copy link
Member

Choose a reason for hiding this comment

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

Please directly use directly the API values: "id" "updatedAt" "createdAt" "originUrl" "status" "originUrlRelated"

This was wrong in the Jira story.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe there's also a enum generated in the SDK which holds these values. Then this would be the way to go because it will be kept up-to-date in the future without any efforts on our side.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Searched for sortBy and originUrl etc. could not find an enum for this. The spec looks like it should generate an enum: https://github.com/stackitcloud/stackit-api-specifications/blob/83214868e6dbdc53d053cc07654542d8f34b5491/services/cdn/v1beta2/cdn.json#L1486

How could I start to investigate this further?


func outputResult(p *print.Printer, outputFormat string, distributions []cdn.Distribution) error {
if distributions == nil {
distributions = make([]cdn.Distribution, 0) // otherwise prints null in json output
Copy link
Member

Choose a reason for hiding this comment

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

That's a good catch indeed! Any chance we can solve this in a central place for all commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice idea, I'll investigate this a bit, don't know much about go reflection yet.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With reflection I'd add this in Printer.OutputResult():

	val := reflect.ValueOf(output)
	if val.IsNil() {
		// when passing a nil slice or map, JSON and YAML marshal to "null"
		// so we need to create an empty slice or map to have "[]" or "{}" output instead
		switch val.Kind() {
		case reflect.Slice:
			output = make([]any, 0)
		case reflect.Map:
			output = make(map[any]any)
		default:
			// do nothing
		}
	}

The linked document suggests type switches, which would not work here.

Another solution without reflection, that comes to mind, would be to introduce specialized OutputResult functions, like OutputResultSlice/Map.

Copy link
Member

Choose a reason for hiding this comment

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

👍

Could be an option, please feel free to create a Jira ticket and propose a solution 😊

cgoetz-inovex and others added 2 commits November 19, 2025 17:56
Co-authored-by: Ruben Hönle <Ruben.Hoenle@stackit.cloud>
Co-authored-by: Ruben Hönle <Ruben.Hoenle@stackit.cloud>
@rubenhoenle
Copy link
Member

Btw, please always add the Jira issue ID to your PR description if possible :)

table.SetHeader("ID", "REGIONS", "STATUS")
for i := range distributions {
d := &distributions[i]
joinedRegions := strings.Join(sdkUtils.EnumSliceToStringSlice(*d.Config.Regions), ", ")
Copy link
Member

Choose a reason for hiding this comment

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

Take care of the potential nil pointer reference here please

Copy link
Contributor Author

Choose a reason for hiding this comment

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

General question:
Is there a general approach? So should we check every pointer for nil?
The API spec for the response marks every step of distributions.config.regions as required.

I'll add nil checks but would be interested how our general approach is.


// Returns the flag's value as a []string.
// Returns nil if flag is not set, if its value can not be converted to []string, or if the flag does not exist.
func FlagToStringArrayValue(p *print.Printer, cmd *cobra.Command, flag string) []string {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
func FlagToStringArrayValue(p *print.Printer, cmd *cobra.Command, flag string) []string {
func FlagToStringSliceValue(p *print.Printer, cmd *cobra.Command, flag string) []string {

won't we call it slice in golang?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FlagToStringSliceValue already exists.
The difference is in the first line:

func FlagToStringSliceValue(p *print.Printer, cmd *cobra.Command, flag string) []string {
	value, err := cmd.Flags().GetStringSlice(flag)

vs.

func FlagToStringArrayValue(p *print.Printer, cmd *cobra.Command, flag string) []string {
	value, err := cmd.Flags().GetStringArray(flag)

--> the difference is which function on Flags is called.
from StringArray Docs:

StringArray defines a string flag with specified name, default value, and usage string. The return value is the address of a []string variable that stores the value of the flag. The value of each argument will not try to be separated by comma. Use a StringSlice for that.

I've declared:

	cmd.Flags().StringArray(flagHTTPGeofencing, []string{}, "Geofencing rules for HTTP backend in the format 'https://example.com US,DE'. URL and countries have to be quoted. Repeatable.")

As a string array. This allows us to configure multiple geofencing rules like this:

$ stackit .... \
--http-geofencing="https://foo.example.com DE,CH"
--http-geofencing="https://bar.example.com AT"

If I'd use StringSlice instead, the pflag library would try to split the first --http-geofencing flag into two values: https://foo.example.com DE and CH.

TLDR: naming is here consistent with the usage of pflag library.

@rubenhoenle
Copy link
Member

Something general: CDN is now GA (v1beta2 -> v1). The SDK adopted this change already: https://github.com/stackitcloud/stackit-sdk-go/blob/main/services/cdn/CHANGELOG.md#v190

I'm open for discussion if you want to directly integrate the GA version of the API within this PR or if you want to do it in a follow-up PR. In that case please create a Jira work item for it and bring it up in the refinement 😊

},
},
expected: `
ID │ REGIONS │ STATUS
Copy link
Member

Choose a reason for hiding this comment

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

some question I have in my mind here: Would it make sense maybe to create some abstraction for table outputs (or mocks) so we can properly test the table output without having to copy formatted tables into our unit tests.

Don't get me wrong, I absolutely (!!) love the idea that we will test the table output for all subcommands in the future, but these formatted tables copied into our unit test files could become a pain to maintain 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also thought about that, especially that the strings are padded with spaces is a bit painful.
For maintaining the current solution I used a breakpoint in the failure branch and evaluatedbuf.String() in the debugger, copying the result into the test and adding placeholders.

I'd add this point to our discussion to also test the run functions. There are a lot of possibilities how we could solve this. We could write a matcher library for this output or could go as far as the stuff mentioned in https://research.swtch.com/testing . Or something in between.

cgoetz-inovex and others added 2 commits December 5, 2025 15:44
Co-authored-by: Ruben Hönle <Ruben.Hoenle@stackit.cloud>
- replace single char names with more descriptive ones
- remove predefined mod functions
- deduplicate ParseOriginRequestHeaders and test it
- deduplicate ParseGeofencing and test it
- add test FlagTo* funcs
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.

7 participants