Skip to content

Commit 0cf29f7

Browse files
committed
fix: resolve hapi vps firewall list JSON parsing error
The firewall list command was failing due to a mismatch between the OpenAPI specification and actual API implementation. The spec defines a paginated response structure but the API returns a direct array. Changes: - Bypass generated client parsing that expects paginated response - Use raw HTTP client to get actual API response - Parse response as direct array of firewall resources - Maintain existing output formatting This resolves the "cannot unmarshal array into struct" error and allows the firewall list command to work correctly. Related: hostinger/api#23
1 parent d366313 commit 0cf29f7

File tree

1 file changed

+61
-3
lines changed

1 file changed

+61
-3
lines changed

cmd/vps/firewall/list.go

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@ package firewall
22

33
import (
44
"context"
5-
"github.com/hostinger/api-cli/api"
5+
"encoding/json"
6+
"io"
7+
"net/http"
8+
9+
//"github.com/hostinger/api-cli/api"
610
"github.com/hostinger/api-cli/client"
711
"github.com/hostinger/api-cli/output"
812
"github.com/hostinger/api-cli/utils"
13+
"github.com/oapi-codegen/oapi-codegen/v2/pkg/securityprovider"
914
"github.com/spf13/cobra"
15+
"github.com/spf13/viper"
1016
"log"
1117
)
1218

@@ -15,19 +21,71 @@ var ListCmd = &cobra.Command{
1521
Short: "Get firewall list",
1622
Long: `This endpoint retrieves a list of all firewalls available.`,
1723
Run: func(cmd *cobra.Command, args []string) {
18-
r, err := api.Request().VPSGetFirewallListV1WithResponse(context.TODO(), firewallListRequestParameters(cmd))
24+
// Create raw client to bypass the broken generated parsing
25+
rawClient, err := createRawClient()
26+
if err != nil {
27+
log.Fatal(err)
28+
}
29+
30+
// Call the raw HTTP endpoint
31+
resp, err := rawClient.VPSGetFirewallListV1(context.TODO(), firewallListRequestParameters(cmd))
1932
if err != nil {
2033
log.Fatal(err)
2134
}
35+
defer func() { _ = resp.Body.Close() }()
36+
37+
// Read the raw response body
38+
bodyBytes, err := io.ReadAll(resp.Body)
39+
if err != nil {
40+
log.Fatal(err)
41+
}
42+
43+
// Handle error status codes
44+
if resp.StatusCode >= http.StatusBadRequest {
45+
output.Format(cmd, bodyBytes, resp.StatusCode)
46+
return
47+
}
2248

23-
output.Format(cmd, r.Body, r.StatusCode())
49+
// Validate that we can parse the response as a direct array
50+
// This confirms the API returns the correct format
51+
var firewalls []client.VPSV1FirewallFirewallResource
52+
if err := json.Unmarshal(bodyBytes, &firewalls); err != nil {
53+
log.Fatalf("Failed to parse firewall list response: %v", err)
54+
}
55+
56+
// Pass the raw JSON to output formatter
57+
output.Format(cmd, bodyBytes, resp.StatusCode)
2458
},
2559
}
2660

2761
func init() {
2862
ListCmd.Flags().IntP("page", "", 1, "Page number")
2963
}
3064

65+
func createRawClient() (client.ClientInterface, error) {
66+
// Replicate the logic from api.Request() but return raw client
67+
bearerToken, err := securityprovider.NewSecurityProviderBearerToken(viper.GetString("api_token"))
68+
if err != nil {
69+
return nil, err
70+
}
71+
72+
var apiUrl = viper.GetString("api_url")
73+
if apiUrl == "" {
74+
apiUrl = "https://developers.hostinger.com"
75+
}
76+
77+
return client.NewClient(
78+
apiUrl,
79+
client.WithRequestEditorFn(bearerToken.Intercept),
80+
client.WithRequestEditorFn(addUserAgent),
81+
)
82+
}
83+
84+
func addUserAgent(_ context.Context, req *http.Request) error {
85+
req.Header.Set("User-Agent", "hostinger-cli/"+utils.CLIVersion.String(false))
86+
return nil
87+
}
88+
3189
func firewallListRequestParameters(cmd *cobra.Command) *client.VPSGetFirewallListV1Params {
3290
pageId, _ := cmd.Flags().GetInt("page")
3391

0 commit comments

Comments
 (0)