Skip to content

Commit 3f54506

Browse files
authored
[Bug] remove compatible models from brick instance list endpoint (arduino#120)
* remove compatible models from brick instance list endpoint * add unit and e2e test
1 parent c309a64 commit 3f54506

File tree

6 files changed

+255
-6
lines changed

6 files changed

+255
-6
lines changed

internal/api/docs/openapi.yaml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,7 @@ components:
11921192
properties:
11931193
bricks:
11941194
items:
1195-
$ref: '#/components/schemas/BrickInstance'
1195+
$ref: '#/components/schemas/BrickInstanceListItem'
11961196
nullable: true
11971197
type: array
11981198
type: object
@@ -1380,6 +1380,33 @@ components:
13801380
for backward compatibility.'
13811381
type: object
13821382
type: object
1383+
BrickInstanceListItem:
1384+
properties:
1385+
author:
1386+
type: string
1387+
category:
1388+
type: string
1389+
config_variables:
1390+
items:
1391+
$ref: '#/components/schemas/BrickConfigVariable'
1392+
type: array
1393+
id:
1394+
type: string
1395+
model:
1396+
type: string
1397+
name:
1398+
type: string
1399+
require_model:
1400+
type: boolean
1401+
status:
1402+
type: string
1403+
variables:
1404+
additionalProperties:
1405+
type: string
1406+
description: 'Deprecated: use config_variables instead. This field is kept
1407+
for backward compatibility.'
1408+
type: object
1409+
type: object
13831410
BrickListItem:
13841411
properties:
13851412
author:

internal/e2e/client/client.gen.go

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/e2e/daemon/bricks_instance_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,20 @@ func TestGetAppBrickInstances(t *testing.T) {
9797
var actualBody models.ErrorResponse
9898
createResp, httpClient := setupTestApp(t)
9999
t.Run("GetAppBrickInstances_Success", func(t *testing.T) {
100+
expectedVariables := map[string]string{
101+
"CUSTOM_MODEL_PATH": "/home/arduino/.arduino-bricks/ei-models",
102+
"EI_CLASSIFICATION_MODEL": "/models/ootb/ei/mobilenet-v2-224px.eim",
103+
}
104+
100105
brickInstances, err := httpClient.GetAppBrickInstancesWithResponse(t.Context(), *createResp.JSON201.Id, func(ctx context.Context, req *http.Request) error { return nil })
101106
require.NoError(t, err)
102107
require.Len(t, *brickInstances.JSON200.Bricks, 1)
103108
require.Equal(t, ImageClassifactionBrickID, *(*brickInstances.JSON200.Bricks)[0].Id)
104109
require.Equal(t, expectedConfigVariables, *(*brickInstances.JSON200.Bricks)[0].ConfigVariables)
110+
require.Equal(t, "Arduino", *(*brickInstances.JSON200.Bricks)[0].Author)
111+
require.Equal(t, "video", *(*brickInstances.JSON200.Bricks)[0].Category)
112+
require.True(t, *(*brickInstances.JSON200.Bricks)[0].RequireModel)
113+
require.Equal(t, expectedVariables, *(*brickInstances.JSON200.Bricks)[0].Variables)
105114

106115
})
107116

internal/orchestrator/bricks/bricks.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func (s *Service) List() (BrickListResult, error) {
7171
}
7272

7373
func (s *Service) AppBrickInstancesList(a *app.ArduinoApp) (AppBrickInstancesResult, error) {
74-
res := AppBrickInstancesResult{BrickInstances: make([]BrickInstance, len(a.Descriptor.Bricks))}
74+
res := AppBrickInstancesResult{BrickInstances: make([]BrickInstanceListItem, len(a.Descriptor.Bricks))}
7575
for i, brickInstance := range a.Descriptor.Bricks {
7676
brick, found := s.bricksIndex.FindBrickByID(brickInstance.ID)
7777
if !found {
@@ -80,7 +80,7 @@ func (s *Service) AppBrickInstancesList(a *app.ArduinoApp) (AppBrickInstancesRes
8080

8181
variablesMap, configVariables := getBrickConfigDetails(brick, brickInstance.Variables)
8282

83-
res.BrickInstances[i] = BrickInstance{
83+
res.BrickInstances[i] = BrickInstanceListItem{
8484
ID: brick.ID,
8585
Name: brick.Name,
8686
Author: "Arduino", // TODO: for now we only support our bricks

internal/orchestrator/bricks/bricks_test.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,3 +644,191 @@ func TestAppBrickInstanceModelsDetails(t *testing.T) {
644644
})
645645
}
646646
}
647+
648+
func TestAppBrickInstancesList(t *testing.T) {
649+
650+
bIndex := &bricksindex.BricksIndex{
651+
Bricks: []bricksindex.Brick{
652+
{
653+
ID: "arduino:weather_forecast",
654+
Name: "Weather Forecast",
655+
Category: "miscellaneous",
656+
RequireModel: false,
657+
Variables: []bricksindex.BrickVariable{},
658+
},
659+
{
660+
ID: "arduino:object_detection",
661+
Name: "Object Detection",
662+
Category: "video",
663+
ModelName: "yolox-object-detection",
664+
RequireModel: true,
665+
Variables: []bricksindex.BrickVariable{
666+
{Name: "CUSTOM_MODEL_PATH", DefaultValue: "/home/arduino/.arduino-bricks/ei-models", Description: "path to the custom model directory"},
667+
{Name: "EI_OBJ_DETECTION_MODEL", DefaultValue: "/models/ootb/ei/yolo-x-nano.eim", Description: "path to the model file"},
668+
},
669+
},
670+
{
671+
ID: "arduino:audio_classification",
672+
Name: "Audio Classification",
673+
Category: "audio",
674+
ModelName: "glass-breaking",
675+
RequireModel: true,
676+
Variables: []bricksindex.BrickVariable{
677+
{Name: "CUSTOM_MODEL_PATH", DefaultValue: "/home/arduino/.arduino-bricks/ei-models"},
678+
{Name: "EI_AUDIO_CLASSIFICATION_MODEL", DefaultValue: "/models/ootb/ei/glass-breaking.eim"},
679+
},
680+
},
681+
{
682+
ID: "arduino:streamlit_ui",
683+
Name: "WebUI - Streamlit",
684+
Category: "ui",
685+
RequireModel: false,
686+
Ports: []string{"7000", "8000"},
687+
},
688+
},
689+
}
690+
691+
svc := &Service{
692+
bricksIndex: bIndex,
693+
modelsIndex: &modelsindex.ModelsIndex{},
694+
}
695+
696+
tests := []struct {
697+
name string
698+
app *app.ArduinoApp
699+
expectedError string
700+
validate func(*testing.T, AppBrickInstancesResult)
701+
}{
702+
{
703+
name: "Error - Brick not found in Index",
704+
app: &app.ArduinoApp{
705+
Descriptor: app.AppDescriptor{
706+
Bricks: []app.Brick{
707+
{ID: "arduino:non_existent_brick"},
708+
},
709+
},
710+
},
711+
expectedError: "brick not found with id arduino:non_existent_brick",
712+
},
713+
{
714+
name: "Success - Empty App",
715+
app: &app.ArduinoApp{
716+
Descriptor: app.AppDescriptor{
717+
Bricks: []app.Brick{},
718+
},
719+
},
720+
validate: func(t *testing.T, res AppBrickInstancesResult) {
721+
require.Empty(t, res.BrickInstances)
722+
},
723+
},
724+
{
725+
name: "Success - Simple Brick",
726+
app: &app.ArduinoApp{
727+
Descriptor: app.AppDescriptor{
728+
Bricks: []app.Brick{
729+
{ID: "arduino:weather_forecast"},
730+
},
731+
},
732+
},
733+
validate: func(t *testing.T, res AppBrickInstancesResult) {
734+
require.Len(t, res.BrickInstances, 1)
735+
brick := res.BrickInstances[0]
736+
737+
require.Equal(t, "arduino:weather_forecast", brick.ID)
738+
require.Equal(t, "Weather Forecast", brick.Name)
739+
require.Equal(t, "miscellaneous", brick.Category)
740+
require.Equal(t, "installed", brick.Status)
741+
require.Equal(t, "Arduino", brick.Author)
742+
require.False(t, brick.RequireModel)
743+
require.Empty(t, brick.ModelID)
744+
},
745+
},
746+
{
747+
name: "Success - Brick with Model Configured",
748+
app: &app.ArduinoApp{
749+
Descriptor: app.AppDescriptor{
750+
Bricks: []app.Brick{
751+
{
752+
ID: "arduino:object_detection",
753+
Model: "face-detection", // default model overridden
754+
Variables: map[string]string{
755+
"CUSTOM_MODEL_PATH": "/custom/path",
756+
},
757+
},
758+
},
759+
},
760+
},
761+
validate: func(t *testing.T, res AppBrickInstancesResult) {
762+
require.Len(t, res.BrickInstances, 1)
763+
brick := res.BrickInstances[0]
764+
765+
require.Equal(t, "arduino:object_detection", brick.ID)
766+
require.Equal(t, "video", brick.Category)
767+
require.True(t, brick.RequireModel)
768+
require.Equal(t, "face-detection", brick.ModelID)
769+
770+
foundCustom := false
771+
for _, v := range brick.ConfigVariables {
772+
if v.Name == "CUSTOM_MODEL_PATH" {
773+
require.Equal(t, "/custom/path", v.Value)
774+
foundCustom = true
775+
}
776+
}
777+
require.True(t, foundCustom, "Variable CUSTOM_MODEL_PATH should be present and overridden")
778+
},
779+
},
780+
{
781+
name: "Success - Multiple Bricks",
782+
app: &app.ArduinoApp{
783+
Descriptor: app.AppDescriptor{
784+
Bricks: []app.Brick{
785+
{ID: "arduino:streamlit_ui"},
786+
{ID: "arduino:audio_classification", Model: "glass-breaking"},
787+
},
788+
},
789+
},
790+
validate: func(t *testing.T, res AppBrickInstancesResult) {
791+
require.Len(t, res.BrickInstances, 2)
792+
793+
// Brick 1: Streamlit UI
794+
b1 := res.BrickInstances[0]
795+
require.Equal(t, "arduino:streamlit_ui", b1.ID)
796+
require.Equal(t, "WebUI - Streamlit", b1.Name)
797+
require.Equal(t, "Arduino", b1.Author)
798+
require.Equal(t, "ui", b1.Category)
799+
require.Equal(t, "installed", b1.Status)
800+
require.Equal(t, "", b1.ModelID)
801+
require.Empty(t, b1.Variables)
802+
require.Empty(t, b1.ConfigVariables)
803+
require.False(t, b1.RequireModel)
804+
805+
// Brick 2: Audio Classification
806+
b2 := res.BrickInstances[1]
807+
require.Equal(t, "arduino:audio_classification", b2.ID)
808+
require.Equal(t, "audio", b2.Category)
809+
require.True(t, b2.RequireModel)
810+
require.Equal(t, "glass-breaking", b2.ModelID)
811+
require.Equal(t, 2, len(b2.ConfigVariables))
812+
require.Equal(t, "/home/arduino/.arduino-bricks/ei-models", b2.ConfigVariables[0].Value)
813+
require.Equal(t, "/models/ootb/ei/glass-breaking.eim", b2.ConfigVariables[1].Value)
814+
},
815+
},
816+
}
817+
818+
for _, tt := range tests {
819+
t.Run(tt.name, func(t *testing.T) {
820+
result, err := svc.AppBrickInstancesList(tt.app)
821+
822+
if tt.expectedError != "" {
823+
require.Error(t, err)
824+
require.Contains(t, err.Error(), tt.expectedError)
825+
return
826+
}
827+
828+
require.NoError(t, err)
829+
if tt.validate != nil {
830+
tt.validate(t, result)
831+
}
832+
})
833+
}
834+
}

internal/orchestrator/bricks/types.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,19 @@ type BrickListItem struct {
3030
}
3131

3232
type AppBrickInstancesResult struct {
33-
BrickInstances []BrickInstance `json:"bricks"`
33+
BrickInstances []BrickInstanceListItem `json:"bricks"`
34+
}
35+
type BrickInstanceListItem struct {
36+
ID string `json:"id"`
37+
Name string `json:"name"`
38+
Author string `json:"author"`
39+
Category string `json:"category"`
40+
Status string `json:"status"`
41+
Variables map[string]string `json:"variables,omitempty" description:"Deprecated: use config_variables instead. This field is kept for backward compatibility."`
42+
ConfigVariables []BrickConfigVariable `json:"config_variables,omitempty"`
43+
RequireModel bool `json:"require_model"`
44+
ModelID string `json:"model,omitempty"`
3445
}
35-
3646
type BrickInstance struct {
3747
ID string `json:"id"`
3848
Name string `json:"name"`

0 commit comments

Comments
 (0)