Skip to content

Commit 461b668

Browse files
commands: use the new endpoint to detect boards (#2974)
* commands: use the new endpoint to detect boards * commands: change cacheKey * commands: push always on top the `arduino` vendor
1 parent 3eecf20 commit 461b668

File tree

2 files changed

+75
-25
lines changed

2 files changed

+75
-25
lines changed

commands/service_board_identify.go

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"io"
2424
"net/http"
2525
"regexp"
26+
"slices"
2627
"sort"
2728
"strings"
2829
"time"
@@ -130,14 +131,14 @@ func identifyViaCloudAPI(ctx context.Context, props *properties.Map, settings *c
130131
}
131132

132133
var (
133-
vidPidURL = "https://builder.arduino.cc/v3/boards/byVidPid"
134+
vidPidURL = "https://api2.arduino.cc/boards/v1/boards"
134135
validVidPid = regexp.MustCompile(`0[xX][a-fA-F\d]{4}`)
135136
)
136137

137138
func cachedAPIByVidPid(ctx context.Context, vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
138139
var resp []*rpc.BoardListItem
139140

140-
cacheKey := fmt.Sprintf("cache.builder-api.v3/boards/byvid/pid/%s/%s", vid, pid)
141+
cacheKey := fmt.Sprintf("cache.api2.arduino.cc/boards/v1/boards?vid-pid=%s-%s", vid, pid)
141142
if cachedResp := inventory.Store.GetString(cacheKey + ".data"); cachedResp != "" {
142143
ts := inventory.Store.GetTime(cacheKey + ".ts")
143144
if time.Since(ts) < time.Hour*24 {
@@ -169,7 +170,7 @@ func apiByVidPid(ctx context.Context, vid, pid string, settings *configuration.S
169170
return nil, errors.New(i18n.Tr("Invalid pid value: '%s'", pid))
170171
}
171172

172-
url := fmt.Sprintf("%s/%s/%s", vidPidURL, vid, pid)
173+
url := fmt.Sprintf("%s?vid-pid=%s-%s", vidPidURL, vid, pid)
173174
req, _ := http.NewRequest("GET", url, nil)
174175
req.Header.Set("Content-Type", "application/json")
175176

@@ -198,20 +199,32 @@ func apiByVidPid(ctx context.Context, vid, pid string, settings *configuration.S
198199
return nil, err
199200
}
200201

201-
var dat map[string]interface{}
202+
type boardsResponse struct {
203+
Items []struct {
204+
Name string `json:"name"`
205+
FQBN string `json:"fqbn"`
206+
} `json:"items"`
207+
}
208+
var dat boardsResponse
202209
if err := json.Unmarshal(resp, &dat); err != nil {
203210
return nil, fmt.Errorf("%s: %w", i18n.Tr("error processing response from server"), err)
204211
}
205-
name, nameFound := dat["name"].(string)
206-
fqbn, fbqnFound := dat["fqbn"].(string)
207-
if !nameFound || !fbqnFound {
208-
return nil, errors.New(i18n.Tr("wrong format in server response"))
212+
213+
response := make([]*rpc.BoardListItem, len(dat.Items))
214+
for i, v := range dat.Items {
215+
if v.Name == "" || v.FQBN == "" {
216+
return nil, errors.New(i18n.Tr("wrong format in server response"))
217+
}
218+
response[i] = &rpc.BoardListItem{Name: v.Name, Fqbn: v.FQBN}
209219
}
210220

211-
return []*rpc.BoardListItem{
212-
{
213-
Name: name,
214-
Fqbn: fqbn,
215-
},
216-
}, nil
221+
// In case multiple platform matches the same vid-pid we put on top
222+
// the arduino ones
223+
slices.SortFunc(response, func(a, b *rpc.BoardListItem) int {
224+
if strings.HasPrefix(a.Fqbn, "arduino") {
225+
return -1
226+
}
227+
return 0
228+
})
229+
return response, nil
217230
}

commands/service_board_identify_test.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,32 +35,69 @@ func TestGetByVidPid(t *testing.T) {
3535
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3636
fmt.Fprintln(w, `
3737
{
38-
"architecture": "samd",
39-
"fqbn": "arduino:samd:mkr1000",
40-
"href": "/v3/boards/arduino:samd:mkr1000",
41-
"id": "mkr1000",
42-
"name": "Arduino/Genuino MKR1000",
43-
"package": "arduino",
44-
"plan": "create-free"
38+
"items": [
39+
{
40+
"fqbn": "arduino:avr:uno",
41+
"vendor": "arduino",
42+
"architecture": "avr",
43+
"board_id": "uno",
44+
"name": "Arduino Uno"
45+
}
46+
]
4547
}
4648
`)
4749
}))
4850
defer ts.Close()
4951

5052
vidPidURL = ts.URL
5153
settings := configuration.NewSettings()
52-
res, err := apiByVidPid(context.Background(), "0xf420", "0XF069", settings)
54+
res, err := apiByVidPid(context.Background(), "0x2341", "0x0043", settings)
5355
require.Nil(t, err)
5456
require.Len(t, res, 1)
55-
require.Equal(t, "Arduino/Genuino MKR1000", res[0].GetName())
56-
require.Equal(t, "arduino:samd:mkr1000", res[0].GetFqbn())
57+
require.Equal(t, "Arduino Uno", res[0].GetName())
58+
require.Equal(t, "arduino:avr:uno", res[0].GetFqbn())
5759

5860
// wrong vid (too long), wrong pid (not an hex value)
5961

6062
_, err = apiByVidPid(context.Background(), "0xfffff", "0xDEFG", settings)
6163
require.NotNil(t, err)
6264
}
6365

66+
func TestGetByVidPidPutArduinoOnTop(t *testing.T) {
67+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
68+
fmt.Fprintln(w, `
69+
{
70+
"items": [
71+
{
72+
"fqbn": "esp32:esp32:nano_nora",
73+
"vendor": "esp32",
74+
"architecture": "esp32",
75+
"board_id": "nano_nora",
76+
"name": "Arduino Nano ESP32"
77+
},
78+
{
79+
"fqbn": "arduino:esp32:nano_nora",
80+
"vendor": "arduino",
81+
"architecture": "esp32",
82+
"board_id": "nano_nora",
83+
"name": "Arduino Nano ESP32"
84+
}
85+
]
86+
}
87+
`)
88+
}))
89+
defer ts.Close()
90+
91+
vidPidURL = ts.URL
92+
settings := configuration.NewSettings()
93+
res, err := apiByVidPid(context.Background(), "0x2341", "0x0070", settings)
94+
require.Nil(t, err)
95+
require.Len(t, res, 2)
96+
require.Equal(t, "Arduino Nano ESP32", res[0].GetName())
97+
require.Equal(t, "arduino:esp32:nano_nora", res[0].GetFqbn())
98+
require.Equal(t, "esp32:esp32:nano_nora", res[1].GetFqbn())
99+
}
100+
64101
func TestGetByVidPidNotFound(t *testing.T) {
65102
settings := configuration.NewSettings()
66103

@@ -95,7 +132,7 @@ func TestGetByVidPidMalformedResponse(t *testing.T) {
95132
settings := configuration.NewSettings()
96133

97134
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
98-
fmt.Fprintln(w, "{}")
135+
fmt.Fprintln(w, `{"items":[{}]}`)
99136
}))
100137
defer ts.Close()
101138

0 commit comments

Comments
 (0)