Skip to content

Commit 9bc05ac

Browse files
committed
refactor: consolidate brick validation into AppDescriptor and update related tests
1 parent 84f1256 commit 9bc05ac

File tree

6 files changed

+51
-55
lines changed

6 files changed

+51
-55
lines changed

internal/api/handlers/app_start.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func HandleAppStart(
4747
return
4848
}
4949

50-
appLoaded, err := app.Load(id.ToPath().String())
50+
app, err := app.Load(id.ToPath().String())
5151
if err != nil {
5252
slog.Error("Unable to parse the app.yaml", slog.String("error", err.Error()), slog.String("path", id.String()))
5353
render.EncodeResponse(w, http.StatusInternalServerError, models.ErrorResponse{Details: "unable to find the app"})
@@ -62,23 +62,14 @@ func HandleAppStart(
6262
}
6363
defer sseStream.Close()
6464

65-
err = app.ValidateBricks(appLoaded.Descriptor, bricksIndex)
66-
if err != nil {
67-
sseStream.SendError(render.SSEErrorData{
68-
Code: render.BadRequestErr,
69-
Message: err.Error(),
70-
})
71-
return
72-
}
73-
7465
type progress struct {
7566
Name string `json:"name"`
7667
Progress float32 `json:"progress"`
7768
}
7869
type log struct {
7970
Message string `json:"message"`
8071
}
81-
for item := range orchestrator.StartApp(r.Context(), dockerCli, provisioner, modelsIndex, bricksIndex, appLoaded, cfg, staticStore) {
72+
for item := range orchestrator.StartApp(r.Context(), dockerCli, provisioner, modelsIndex, bricksIndex, app, cfg, staticStore) {
8273
switch item.GetType() {
8374
case orchestrator.ProgressType:
8475
sseStream.Send(render.SSEEvent{Type: "progress", Data: progress(*item.GetProgress())})

internal/orchestrator/app/parser.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"io"
2222

2323
emoji "github.com/Andrew-M-C/go.emoji"
24+
"github.com/arduino/arduino-app-cli/internal/orchestrator/bricksindex"
2425
"github.com/arduino/go-paths-helper"
2526
"github.com/goccy/go-yaml"
2627
"github.com/goccy/go-yaml/ast"
@@ -138,6 +139,38 @@ func (a *AppDescriptor) IsValid() error {
138139
return allErrors
139140
}
140141

142+
// ValidateBricks checks that all bricks referenced in the given AppDescriptor exist in the provided BricksIndex,
143+
// and that all required variables for each brick are present and valid. It returns an error if any brick is missing,
144+
// if any variable referenced by the app does not exist in the corresponding brick, or if any required variable is missing.
145+
// If the index is nil, validation is skipped and nil is returned.
146+
func (a *AppDescriptor) ValidateBricks(index *bricksindex.BricksIndex) error {
147+
if index == nil {
148+
return nil
149+
}
150+
for _, appBrick := range a.Bricks {
151+
indexBrick, found := index.FindBrickByID(appBrick.ID)
152+
if !found {
153+
return fmt.Errorf("brick %q not found", appBrick.ID)
154+
}
155+
156+
for appBrickName := range appBrick.Variables {
157+
_, exist := indexBrick.GetVariable(appBrickName)
158+
if !exist {
159+
return fmt.Errorf("variable %q does not exist on brick %q", appBrickName, indexBrick.ID)
160+
}
161+
}
162+
163+
for _, indexBrickVariable := range indexBrick.Variables {
164+
if indexBrickVariable.IsRequired() {
165+
if _, exist := appBrick.Variables[indexBrickVariable.Name]; !exist {
166+
return fmt.Errorf("variable %q is required by brick %q", indexBrickVariable.Name, indexBrick.ID)
167+
}
168+
}
169+
}
170+
}
171+
return nil
172+
}
173+
141174
func isSingleEmoji(s string) bool {
142175
emojis := 0
143176
for it := emoji.IterateChars(s); it.Next(); {

internal/orchestrator/app/validator.go

Lines changed: 0 additions & 39 deletions
This file was deleted.

internal/orchestrator/app/validator_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/arduino/arduino-app-cli/internal/orchestrator/bricksindex"
1313
)
1414

15-
func TestValidateBricksOnAppDescriptor(t *testing.T) {
15+
func TestValidateAppDescriptorBricks(t *testing.T) {
1616
bricksIndex, err := bricksindex.GenerateBricksIndexFromFile(paths.New("testdata/validator"))
1717
require.Nil(t, err)
1818

@@ -50,11 +50,10 @@ func TestValidateBricksOnAppDescriptor(t *testing.T) {
5050

5151
for _, tc := range testCases {
5252
t.Run(tc.name, func(t *testing.T) {
53-
app, err := ParseDescriptorFile(paths.New("testdata/validator/" + tc.filename))
53+
appDescriptor, err := ParseDescriptorFile(paths.New("testdata/validator/" + tc.filename))
5454
require.NoError(t, err)
5555

56-
err = ValidateBricks(app, bricksIndex)
57-
56+
appDescriptor.ValidateBricks(bricksIndex)
5857
if tc.expectedErr == nil {
5958
assert.NoError(t, err)
6059
} else {

internal/orchestrator/orchestrator.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ func StartApp(
122122
ctx, cancel := context.WithCancel(ctx)
123123
defer cancel()
124124

125+
err := app.Descriptor.ValidateBricks(bricksIndex)
126+
if err != nil {
127+
yield(StreamMessage{error: err})
128+
return
129+
}
130+
125131
running, err := getRunningApp(ctx, docker.Client())
126132
if err != nil {
127133
yield(StreamMessage{error: err})
@@ -446,6 +452,13 @@ func RestartApp(
446452
return func(yield func(StreamMessage) bool) {
447453
ctx, cancel := context.WithCancel(ctx)
448454
defer cancel()
455+
456+
err := appToStart.Descriptor.ValidateBricks(bricksIndex)
457+
if err != nil {
458+
yield(StreamMessage{error: err})
459+
return
460+
}
461+
449462
runningApp, err := getRunningApp(ctx, docker.Client())
450463
if err != nil {
451464
yield(StreamMessage{error: err})

internal/render/sse.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ type SSEErrCode string
3030

3131
const (
3232
InternalServiceErr SSEErrCode = "INTERNAL_SERVER_ERROR"
33-
BadRequestErr SSEErrCode = "BAD_REQUEST_ERROR"
3433
)
3534

3635
type SSEErrorData struct {

0 commit comments

Comments
 (0)