Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1b200b1

Browse files
API: add "UsedByApps" to the details of a brick (#30)
The API returning the details of a brick show fill in the UsedByApps property.The App Lab uses this to show what examples or apps are using a given brick.* add useByApps field for brick details endpoint* partial test implementation* add test end2end* delete wrong tests* refactoring* make lint happy* code review fixes* fix error message---------Co-authored-by: mirkoCrobu <m.crobu@ext.arduino.cc>
1 parenta2f7219 commit1b200b1

File tree

7 files changed

+121
-13
lines changed

7 files changed

+121
-13
lines changed

‎cmd/arduino-app-cli/brick/bricks.go‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@ package brick
1717

1818
import (
1919
"github.com/spf13/cobra"
20+
21+
"github.com/arduino/arduino-app-cli/internal/orchestrator/config"
2022
)
2123

22-
funcNewBrickCmd()*cobra.Command {
24+
funcNewBrickCmd(cfg config.Configuration)*cobra.Command {
2325
appCmd:=&cobra.Command{
2426
Use:"brick",
2527
Short:"Manage Arduino Bricks",
2628
}
2729

2830
appCmd.AddCommand(newBricksListCmd())
29-
appCmd.AddCommand(newBricksDetailsCmd())
31+
appCmd.AddCommand(newBricksDetailsCmd(cfg))
3032

3133
returnappCmd
3234
}

‎cmd/arduino-app-cli/brick/details.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,23 @@ import (
2525
"github.com/arduino/arduino-app-cli/cmd/arduino-app-cli/internal/servicelocator"
2626
"github.com/arduino/arduino-app-cli/cmd/feedback"
2727
"github.com/arduino/arduino-app-cli/internal/orchestrator/bricks"
28+
"github.com/arduino/arduino-app-cli/internal/orchestrator/config"
2829
)
2930

30-
funcnewBricksDetailsCmd()*cobra.Command {
31+
funcnewBricksDetailsCmd(cfg config.Configuration)*cobra.Command {
3132
return&cobra.Command{
3233
Use:"details",
3334
Short:"Details of a specific brick",
3435
Args:cobra.ExactArgs(1),
3536
Run:func(cmd*cobra.Command,args []string) {
36-
bricksDetailsHandler(args[0])
37+
bricksDetailsHandler(args[0],cfg)
3738
},
3839
}
3940
}
4041

41-
funcbricksDetailsHandler(idstring) {
42-
res,err:=servicelocator.GetBrickService().BricksDetails(id)
42+
funcbricksDetailsHandler(idstring,cfg config.Configuration) {
43+
res,err:=servicelocator.GetBrickService().BricksDetails(id,servicelocator.GetAppIDProvider(),
44+
cfg)
4345
iferr!=nil {
4446
iferrors.Is(err,bricks.ErrBrickNotFound) {
4547
feedback.Fatal(err.Error(),feedback.ErrBadArgument)

‎cmd/arduino-app-cli/main.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func run(configuration cfg.Configuration) error {
7171

7272
rootCmd.AddCommand(
7373
app.NewAppCmd(configuration),
74-
brick.NewBrickCmd(),
74+
brick.NewBrickCmd(configuration),
7575
completion.NewCompletionCommand(),
7676
daemon.NewDaemonCmd(configuration,Version),
7777
properties.NewPropertiesCmd(configuration),

‎internal/api/api.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func NewHTTPRouter(
5656
mux.Handle("GET /v1/version",handlers.HandlerVersion(version))
5757
mux.Handle("GET /v1/config",handlers.HandleConfig(cfg))
5858
mux.Handle("GET /v1/bricks",handlers.HandleBrickList(brickService))
59-
mux.Handle("GET /v1/bricks/{brickID}",handlers.HandleBrickDetails(brickService))
59+
mux.Handle("GET /v1/bricks/{brickID}",handlers.HandleBrickDetails(brickService,idProvider,cfg))
6060

6161
mux.Handle("GET /v1/properties",handlers.HandlePropertyKeys(cfg))
6262
mux.Handle("GET /v1/properties/{key}",handlers.HandlePropertyGet(cfg))

‎internal/api/handlers/bricks.go‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/arduino/arduino-app-cli/internal/api/models"
2727
"github.com/arduino/arduino-app-cli/internal/orchestrator/app"
2828
"github.com/arduino/arduino-app-cli/internal/orchestrator/bricks"
29+
"github.com/arduino/arduino-app-cli/internal/orchestrator/config"
2930
"github.com/arduino/arduino-app-cli/internal/render"
3031
)
3132

@@ -153,14 +154,15 @@ func HandleBrickCreate(
153154
}
154155
}
155156

156-
funcHandleBrickDetails(brickService*bricks.Service) http.HandlerFunc {
157+
funcHandleBrickDetails(brickService*bricks.Service,idProvider*app.IDProvider,
158+
cfg config.Configuration) http.HandlerFunc {
157159
returnfunc(w http.ResponseWriter,r*http.Request) {
158160
id:=r.PathValue("brickID")
159161
ifid=="" {
160162
render.EncodeResponse(w,http.StatusBadRequest, models.ErrorResponse{Details:"id must be set"})
161163
return
162164
}
163-
res,err:=brickService.BricksDetails(id)
165+
res,err:=brickService.BricksDetails(id,idProvider,cfg)
164166
iferr!=nil {
165167
iferrors.Is(err,bricks.ErrBrickNotFound) {
166168
details:=fmt.Sprintf("brick with id %q not found",id)

‎internal/e2e/daemon/brick_test.go‎

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,44 @@ import (
2424

2525
"github.com/arduino/go-paths-helper"
2626
"github.com/stretchr/testify/require"
27+
"go.bug.st/f"
2728

2829
"github.com/arduino/arduino-app-cli/internal/api/models"
30+
"github.com/arduino/arduino-app-cli/internal/e2e/client"
2931
"github.com/arduino/arduino-app-cli/internal/orchestrator/bricksindex"
3032
"github.com/arduino/arduino-app-cli/internal/orchestrator/config"
3133
"github.com/arduino/arduino-app-cli/internal/store"
3234
)
3335

36+
funcsetupTestBrick(t*testing.T) (*client.CreateAppResp,*client.ClientWithResponses) {
37+
httpClient:=GetHttpclient(t)
38+
createResp,err:=httpClient.CreateAppWithResponse(
39+
t.Context(),
40+
&client.CreateAppParams{SkipSketch:f.Ptr(true)},
41+
client.CreateAppRequest{
42+
Icon:f.Ptr("💻"),
43+
Name:"test-app",
44+
Description:f.Ptr("My app description"),
45+
},
46+
func(ctx context.Context,req*http.Request)error {returnnil },
47+
)
48+
require.NoError(t,err)
49+
require.Equal(t,http.StatusCreated,createResp.StatusCode())
50+
require.NotNil(t,createResp.JSON201)
51+
52+
resp,err:=httpClient.UpsertAppBrickInstanceWithResponse(
53+
t.Context(),
54+
*createResp.JSON201.Id,
55+
ImageClassifactionBrickID,
56+
client.BrickCreateUpdateRequest{Model:f.Ptr("mobilenet-image-classification")},
57+
func(ctx context.Context,req*http.Request)error {returnnil },
58+
)
59+
require.NoError(t,err)
60+
require.Equal(t,http.StatusOK,resp.StatusCode())
61+
62+
returncreateResp,httpClient
63+
}
64+
3465
funcTestBricksList(t*testing.T) {
3566
httpClient:=GetHttpclient(t)
3667

@@ -56,8 +87,8 @@ func TestBricksList(t *testing.T) {
5687
}
5788

5889
funcTestBricksDetails(t*testing.T) {
90+
_,httpClient:=setupTestBrick(t)
5991

60-
httpClient:=GetHttpclient(t)
6192
t.Run("should return 404 Not Found for an invalid brick ID",func(t*testing.T) {
6293
invalidBrickID:="notvalidBrickId"
6394
varactualBody models.ErrorResponse
@@ -76,6 +107,14 @@ func TestBricksDetails(t *testing.T) {
76107
t.Run("should return 200 OK with full details for a valid brick ID",func(t*testing.T) {
77108
validBrickID:="arduino:image_classification"
78109

110+
expectedUsedByApps:= []client.AppReference{
111+
{
112+
Id:f.Ptr("dXNlcjp0ZXN0LWFwcA"),
113+
Name:f.Ptr("test-app"),
114+
Icon:f.Ptr("💻"),
115+
},
116+
}
117+
79118
response,err:=httpClient.GetBrickDetailsWithResponse(t.Context(),validBrickID,func(ctx context.Context,req*http.Request)error {returnnil })
80119
require.NoError(t,err)
81120
require.Equal(t,http.StatusOK,response.StatusCode(),"status code should be 200 ok")
@@ -92,6 +131,7 @@ func TestBricksDetails(t *testing.T) {
92131
require.Equal(t,"path to the model file",*(*response.JSON200.Variables)["EI_CLASSIFICATION_MODEL"].Description)
93132
require.Equal(t,false,*(*response.JSON200.Variables)["EI_CLASSIFICATION_MODEL"].Required)
94133
require.NotEmpty(t,*response.JSON200.Readme)
95-
require.Nil(t,response.JSON200.UsedByApps)
134+
require.NotNil(t,response.JSON200.UsedByApps,"UsedByApps should not be nil")
135+
require.Equal(t,expectedUsedByApps,*(response.JSON200.UsedByApps))
96136
})
97137
}

‎internal/orchestrator/bricks/bricks.go‎

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package bricks
1818
import (
1919
"errors"
2020
"fmt"
21+
"log/slog"
2122
"maps"
2223
"slices"
2324

@@ -26,6 +27,7 @@ import (
2627

2728
"github.com/arduino/arduino-app-cli/internal/orchestrator/app"
2829
"github.com/arduino/arduino-app-cli/internal/orchestrator/bricksindex"
30+
"github.com/arduino/arduino-app-cli/internal/orchestrator/config"
2931
"github.com/arduino/arduino-app-cli/internal/orchestrator/modelsindex"
3032
"github.com/arduino/arduino-app-cli/internal/store"
3133
)
@@ -125,7 +127,8 @@ func (s *Service) AppBrickInstanceDetails(a *app.ArduinoApp, brickID string) (Br
125127
},nil
126128
}
127129

128-
func (s*Service)BricksDetails(idstring) (BrickDetailsResult,error) {
130+
func (s*Service)BricksDetails(idstring,idProvider*app.IDProvider,
131+
cfg config.Configuration) (BrickDetailsResult,error) {
129132
brick,found:=s.bricksIndex.FindBrickByID(id)
130133
if!found {
131134
returnBrickDetailsResult{},ErrBrickNotFound
@@ -160,6 +163,11 @@ func (s *Service) BricksDetails(id string) (BrickDetailsResult, error) {
160163
}
161164
})
162165

166+
usedByApps,err:=getUsedByApps(cfg,brick.ID,idProvider)
167+
iferr!=nil {
168+
returnBrickDetailsResult{},fmt.Errorf("unable to get used by apps: %w",err)
169+
}
170+
163171
returnBrickDetailsResult{
164172
ID:id,
165173
Name:brick.Name,
@@ -171,9 +179,63 @@ func (s *Service) BricksDetails(id string) (BrickDetailsResult, error) {
171179
Readme:readme,
172180
ApiDocsPath:apiDocsPath,
173181
CodeExamples:codeExamples,
182+
UsedByApps:usedByApps,
174183
},nil
175184
}
176185

186+
funcgetUsedByApps(
187+
cfg config.Configuration,brickIdstring,idProvider*app.IDProvider) ([]AppReference,error) {
188+
var (
189+
pathsToExplore paths.PathList
190+
appPaths paths.PathList
191+
)
192+
pathsToExplore.Add(cfg.ExamplesDir())
193+
pathsToExplore.Add(cfg.AppsDir())
194+
usedByApps:= []AppReference{}
195+
196+
for_,p:=rangepathsToExplore {
197+
res,err:=p.ReadDirRecursiveFiltered(func(file*paths.Path)bool {
198+
iffile.Base()==".cache" {
199+
returnfalse
200+
}
201+
iffile.Join("app.yaml").NotExist()&&file.Join("app.yml").NotExist() {
202+
returntrue
203+
}
204+
returnfalse
205+
},paths.FilterDirectories(),paths.FilterOutNames("python","sketch",".cache"))
206+
iferr!=nil {
207+
slog.Error("unable to list apps",slog.String("error",err.Error()))
208+
returnusedByApps,err
209+
}
210+
appPaths.AddAllMissing(res)
211+
}
212+
213+
for_,file:=rangeappPaths {
214+
app,err:=app.Load(file.String())
215+
iferr!=nil {
216+
// we are not considering the broken apps
217+
slog.Warn("unable to parse app.yaml, skipping","path",file.String(),"error",err.Error())
218+
continue
219+
}
220+
221+
for_,b:=rangeapp.Descriptor.Bricks {
222+
ifb.ID==brickId {
223+
id,err:=idProvider.IDFromPath(app.FullPath)
224+
iferr!=nil {
225+
returnusedByApps,fmt.Errorf("failed to get app ID for %s: %w",app.FullPath,err)
226+
}
227+
usedByApps=append(usedByApps,AppReference{
228+
Name:app.Name,
229+
ID:id.String(),
230+
Icon:app.Descriptor.Icon,
231+
})
232+
break
233+
}
234+
}
235+
}
236+
returnusedByApps,nil
237+
}
238+
177239
typeBrickCreateUpdateRequeststruct {
178240
IDstring`json:"-"`
179241
Model*string`json:"model"`

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp