Skip to content

Commit 1fbc6f5

Browse files
authored
Merge pull request #104 from mattrent/fixes
Various fixes
2 parents a7389f9 + f1ac6c8 commit 1fbc6f5

17 files changed

Lines changed: 344 additions & 43 deletions

File tree

.licenserc.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ header:
2727
- '**/task/**'
2828
- '**/vendor/**'
2929
- '**/*.wasm'
30+
- 'test/fixtures/**'
3031
- '**/.gitkeep'
3132
- 'workspace.code-workspace'
3233
- CODEOWNERS

cmd/fl/app/app.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ func ParseCMD(version string) (*kong.Context, error) {
5858

5959
flConfig := client.Config{Host: "http://localhost:4000"}
6060
flClient, err := client.NewClient(http.DefaultClient, flConfig)
61+
validator := client.InputValidator{}
6162
if err != nil {
6263
return nil, err
6364
}
64-
fnSvc := &client.FnService{Client: flClient}
65-
modSvc := &client.ModService{Client: flClient}
65+
fnSvc := &client.FnService{Client: flClient, InputValidatorHandler: &validator}
66+
modSvc := &client.ModService{Client: flClient, InputValidatorHandler: &validator}
6667

6768
kong_ctx := kong.Parse(&cli,
6869
kong.Name("fl"),

internal/command/fn/build.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ package fn
1616

1717
import (
1818
"context"
19+
"errors"
1920
"fmt"
21+
"os"
2022
"path/filepath"
2123

2224
"github.com/docker/docker/client"
@@ -40,7 +42,13 @@ func (b *Build) Run(ctx context.Context, builder build.DockerBuilder, logger log
4042
if err := logger.StopSpinner(setupBuilder(builder, b.Language, b.Destination)); err != nil {
4143
return err
4244
}
43-
_ = logger.StartSpinner(fmt.Sprintf("Pulling %s builder image (%s) 📦", langNames[b.Language], pkg.FLBuilderImages[b.Language]))
45+
46+
_ = logger.StartSpinner(fmt.Sprintf("Checking directory %s files for language %s... 🔍", b.Source, b.Language))
47+
if err := logger.StopSpinner(checkMustContainFiles(b.Language, b.Source)); err != nil {
48+
return err
49+
}
50+
51+
_ = logger.StartSpinner(fmt.Sprintf("Pulling %s builder image (%s) 📦", pkg.SupportedLanguages[b.Language].Name, pkg.SupportedLanguages[b.Language].BuilderImage))
4452
if err := logger.StopSpinner(builder.PullBuilderImage(ctx)); err != nil {
4553
return err
4654
}
@@ -56,11 +64,6 @@ func (b *Build) Run(ctx context.Context, builder build.DockerBuilder, logger log
5664
return nil
5765
}
5866

59-
var langNames = map[string]string{
60-
"js": "Javascript",
61-
"rust": "Rust",
62-
}
63-
6467
func setupBuilder(builder build.DockerBuilder, lang, out string) error {
6568
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.41"))
6669
if err != nil {
@@ -77,3 +80,18 @@ func setupBuilder(builder build.DockerBuilder, lang, out string) error {
7780
}
7881
return nil
7982
}
83+
84+
func checkMustContainFiles(lang, source string) error {
85+
language, ok := pkg.SupportedLanguages[lang]
86+
if !ok {
87+
return errors.New("unsupported language")
88+
}
89+
for _, f := range language.MustContainFiles {
90+
path := filepath.Join(source, f)
91+
_, err := os.Stat(path)
92+
if err != nil && os.IsNotExist(err) {
93+
return fmt.Errorf("necessary file %s not found in path %s", f, source)
94+
}
95+
}
96+
return nil
97+
}

internal/command/fn/build_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func TestFnBuild(t *testing.T) {
7171
Language: testLanguage,
7272
}
7373

74-
output := genTestOutput(testFn, pkg.FLBuilderImages[testLanguage])
74+
output := genTestOutput(testFn, pkg.SupportedLanguages[testLanguage].BuilderImage, testDir)
7575

7676
mockBuilder.On("Setup", mock.Anything, testLanguage, testOutDir).Return(nil).Once()
7777
mockBuilder.On("PullBuilderImage", ctx).Return(nil).Once()
@@ -136,16 +136,18 @@ func TestFnBuild(t *testing.T) {
136136
})
137137
}
138138

139-
func genTestOutput(name, image string) string {
139+
func genTestOutput(name, image, source string) string {
140140
return fmt.Sprintf(`Building %s into a wasm binary...
141141
142142
Setting up...
143143
done
144+
Checking directory %s files for language js... 🔍
145+
done
144146
Pulling Javascript builder image (%s) 📦
145147
done
146148
Building source... 🛠️
147149
done
148150
149151
Successfully built %s.wasm 🥳🥳
150-
`, name, image, name)
152+
`, name, source, image, name)
151153
}

internal/command/fn/create.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ func (c *Create) Run(ctx context.Context, builder build.DockerBuilder, fnHandler
4545
if err := setupBuilder(builder, c.Language, dest); err != nil {
4646
return logger.StopSpinner(err)
4747
}
48+
if err := checkMustContainFiles(c.Language, c.Source); err != nil {
49+
return logger.StopSpinner(err)
50+
}
4851
if err := builder.PullBuilderImage(ctx); err != nil {
4952
return logger.StopSpinner(err)
5053
}

pkg/build/wasm.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ func (b *WasmBuilder) Setup(flDocker docker.DockerClient, language string, dest
5555
return err
5656
}
5757

58-
image, exists := pkg.FLBuilderImages[language]
58+
lang, exists := pkg.SupportedLanguages[language]
5959
if !exists {
6060
return errors.New("no corresponding builder image found for the given language")
6161
}
62-
b.builderImg = image
62+
b.builderImg = lang.BuilderImage
6363

6464
containerName, exists := builderNames[language]
6565
if !exists {

pkg/client/fn_service.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,20 @@ type FnHandler interface {
3030

3131
type FnService struct {
3232
*Client
33+
InputValidatorHandler
3334
}
3435

3536
var _ FnHandler = &FnService{}
3637

3738
func (fn *FnService) Invoke(ctx context.Context, fnName string, fnMod string, fnArgs map[string]interface{}) (openapi.InvokeResult, error) {
39+
40+
if err := fn.InputValidatorHandler.ValidateName(fnName, "function"); err != nil {
41+
return *openapi.NewInvokeResult(), err
42+
}
43+
if err := fn.InputValidatorHandler.ValidateName(fnMod, "mod"); err != nil {
44+
return *openapi.NewInvokeResult(), err
45+
}
46+
3847
apiService := fn.Client.ApiClient.FunctionsApi
3948
invokeInput := openapi.InvokeInput{
4049
Args: fnArgs,
@@ -49,20 +58,48 @@ func (fn *FnService) Invoke(ctx context.Context, fnName string, fnMod string, fn
4958
}
5059

5160
func (fn *FnService) Create(ctx context.Context, fnName string, fnMod string, code *os.File) error {
61+
62+
if err := fn.InputValidatorHandler.ValidateName(fnName, "function"); err != nil {
63+
return err
64+
}
65+
if err := fn.InputValidatorHandler.ValidateName(fnMod, "mod"); err != nil {
66+
return err
67+
}
68+
5269
apiService := fn.Client.ApiClient.FunctionsApi
5370
request := apiService.CreateFunction(ctx, fnMod).Name(fnName).Code(*code)
5471
_, err := request.Execute()
5572
return err
5673
}
5774

5875
func (fn *FnService) Delete(ctx context.Context, fnName string, fnMod string) error {
76+
77+
if err := fn.InputValidatorHandler.ValidateName(fnName, "function"); err != nil {
78+
return err
79+
}
80+
if err := fn.InputValidatorHandler.ValidateName(fnMod, "mod"); err != nil {
81+
return err
82+
}
83+
5984
apiService := fn.Client.ApiClient.FunctionsApi
6085
request := apiService.DeleteFunction(ctx, fnMod, fnName)
6186
_, err := request.Execute()
6287
return err
6388
}
6489

6590
func (fn *FnService) Update(ctx context.Context, fnName string, fnMod string, code *os.File, newName string) error {
91+
92+
if err := fn.InputValidatorHandler.ValidateName(fnName, "function"); err != nil {
93+
return err
94+
}
95+
if err := fn.InputValidatorHandler.ValidateName(newName, "new function"); err != nil {
96+
return err
97+
}
98+
99+
if err := fn.InputValidatorHandler.ValidateName(fnMod, "mod"); err != nil {
100+
return err
101+
}
102+
66103
apiService := fn.Client.ApiClient.FunctionsApi
67104
request := apiService.UpdateFunction(ctx, fnMod, fnName).Code(*code).Name(newName)
68105
_, err := request.Execute()

pkg/client/fn_service_test.go

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,19 @@ import (
2727
"github.com/stretchr/testify/assert"
2828
"github.com/stretchr/testify/require"
2929

30+
"github.com/funlessdev/fl-cli/test/mocks"
3031
openapi "github.com/funlessdev/fl-client-sdk-go"
3132
)
3233

3334
func TestFnInvoke(t *testing.T) {
34-
testFn := "test-fn"
35-
testNs := "test-ns"
35+
testFn := "test_fn"
36+
testNs := "test_ns"
3637
var testArgs map[string]interface{} = map[string]interface{}{"name": "Some name"}
3738

3839
testCtx := context.Background()
40+
mockValidator := mocks.NewInputValidatorHandler(t)
41+
mockValidator.On("ValidateName", testFn, "function").Return(nil)
42+
mockValidator.On("ValidateName", testNs, "mod").Return(nil)
3943

4044
t.Run("should send invoke request to server", func(t *testing.T) {
4145
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -49,13 +53,16 @@ func TestFnInvoke(t *testing.T) {
4953
defer server.Close()
5054

5155
c, _ := NewClient(http.DefaultClient, Config{Host: server.URL})
52-
svc := &FnService{Client: c}
56+
svc := &FnService{Client: c, InputValidatorHandler: mockValidator}
5357

5458
result, err := svc.Invoke(testCtx, testFn, testNs, testArgs)
5559

5660
require.NoError(t, err)
5761
expected := map[string]interface{}{"payload": "some result"}
5862
assert.Equal(t, expected, result.GetData())
63+
64+
mockValidator.AssertNumberOfCalls(t, "ValidateName", 2)
65+
mockValidator.AssertExpectations(t)
5966
})
6067

6168
t.Run("should return error if request encounters an HTTP error", func(t *testing.T) {
@@ -72,7 +79,7 @@ func TestFnInvoke(t *testing.T) {
7279
defer server.Close()
7380

7481
c, _ := NewClient(http.DefaultClient, Config{Host: server.URL})
75-
svc := &FnService{Client: c}
82+
svc := &FnService{Client: c, InputValidatorHandler: mockValidator}
7683

7784
_, err := svc.Invoke(testCtx, testFn, testNs, testArgs)
7885

@@ -83,12 +90,17 @@ func TestFnInvoke(t *testing.T) {
8390
}
8491

8592
func TestFnCreate(t *testing.T) {
86-
testFn := "test-fn"
87-
testNs := "test-ns"
93+
testFn := "test_fn"
94+
testNs := "test_ns"
8895
testSource, _ := filepath.Abs("../../test/fixtures/real.wasm")
8996
testCode, _ := os.Open(testSource)
9097

9198
testCtx := context.Background()
99+
100+
mockValidator := mocks.NewInputValidatorHandler(t)
101+
mockValidator.On("ValidateName", testFn, "function").Return(nil)
102+
mockValidator.On("ValidateName", testNs, "mod").Return(nil)
103+
92104
t.Run("should send create request to server", func(t *testing.T) {
93105
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
94106
assert.Equal(t, http.MethodPost, r.Method)
@@ -101,11 +113,14 @@ func TestFnCreate(t *testing.T) {
101113
defer server.Close()
102114

103115
c, _ := NewClient(http.DefaultClient, Config{Host: server.URL})
104-
svc := &FnService{Client: c}
116+
svc := &FnService{Client: c, InputValidatorHandler: mockValidator}
105117

106118
err := svc.Create(testCtx, testFn, testNs, testCode)
107119

108120
require.NoError(t, err)
121+
122+
mockValidator.AssertNumberOfCalls(t, "ValidateName", 2)
123+
mockValidator.AssertExpectations(t)
109124
})
110125

111126
t.Run("should return error if request encounters an HTTP error", func(t *testing.T) {
@@ -122,7 +137,7 @@ func TestFnCreate(t *testing.T) {
122137
defer server.Close()
123138

124139
c, _ := NewClient(http.DefaultClient, Config{Host: server.URL})
125-
svc := &FnService{Client: c}
140+
svc := &FnService{Client: c, InputValidatorHandler: mockValidator}
126141

127142
err := svc.Create(testCtx, testFn, testNs, testCode)
128143

@@ -133,11 +148,15 @@ func TestFnCreate(t *testing.T) {
133148
}
134149

135150
func TestFnDelete(t *testing.T) {
136-
testFn := "test-fn"
137-
testNs := "test-ns"
151+
testFn := "test_fn"
152+
testNs := "test_ns"
138153

139154
testCtx := context.Background()
140155

156+
mockValidator := mocks.NewInputValidatorHandler(t)
157+
mockValidator.On("ValidateName", testFn, "function").Return(nil)
158+
mockValidator.On("ValidateName", testNs, "mod").Return(nil)
159+
141160
t.Run("should send delete request to server", func(t *testing.T) {
142161
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
143162
assert.Equal(t, http.MethodDelete, r.Method)
@@ -150,11 +169,14 @@ func TestFnDelete(t *testing.T) {
150169
defer server.Close()
151170

152171
c, _ := NewClient(http.DefaultClient, Config{Host: server.URL})
153-
svc := &FnService{Client: c}
172+
svc := &FnService{Client: c, InputValidatorHandler: mockValidator}
154173

155174
err := svc.Delete(testCtx, testFn, testNs)
156175

157176
require.NoError(t, err)
177+
178+
mockValidator.AssertNumberOfCalls(t, "ValidateName", 2)
179+
mockValidator.AssertExpectations(t)
158180
})
159181

160182
t.Run("should return error if request encounters an HTTP error", func(t *testing.T) {
@@ -171,7 +193,7 @@ func TestFnDelete(t *testing.T) {
171193
defer server.Close()
172194

173195
c, _ := NewClient(http.DefaultClient, Config{Host: server.URL})
174-
svc := &FnService{Client: c}
196+
svc := &FnService{Client: c, InputValidatorHandler: mockValidator}
175197

176198
err := svc.Delete(testCtx, testFn, testNs)
177199

pkg/client/input_validator.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2022 Giuseppe De Palma, Matteo Trentin
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package client
16+
17+
import (
18+
"fmt"
19+
"regexp"
20+
)
21+
22+
type InputValidatorHandler interface {
23+
ValidateName(name, entity string) error
24+
}
25+
26+
type InputValidator struct{}
27+
28+
var _ InputValidatorHandler = &InputValidator{}
29+
30+
func (i *InputValidator) ValidateName(name, entity string) error {
31+
regex := regexp.MustCompile("^[_a-zA-Z0-9]+$")
32+
if regex.MatchString(name) {
33+
return nil
34+
} else {
35+
return fmt.Errorf("Invalid %s name", entity)
36+
}
37+
}

0 commit comments

Comments
 (0)