Skip to content

Commit adb8c3c

Browse files
✨ Added modules and helpers for carrying out operations on YAML and JSON (#872)
<!-- Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved. SPDX-License-Identifier: Apache-2.0 --> ### Description <!-- Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved. SPDX-License-Identifier: Apache-2.0 --> ### Description - added validation rules for paths - added standard ways for serialising JSON and YAML content/files - added a standard way to validate a JSON or YAML file against a JSON Schema ### Test Coverage <!-- Please put an `x` in the correct box e.g. `[x]` to indicate the testing coverage of this change. --> - [x] This change is covered by existing or additional automated tests. - [ ] Manual testing has been performed (and evidence provided) as automated testing was not feasible. - [ ] Additional tests are not required for this change (e.g. documentation update). --------- Co-authored-by: Josh <josh.jennings@arm.com>
1 parent cca5737 commit adb8c3c

59 files changed

Lines changed: 5817 additions & 174 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

changes/20260521144643.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:zap: add helpers for supporting `json` marshalling with better performance than `encoding/json`

changes/20260521162032.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:sparkles: add helpers for serialising yaml

changes/20260521183044.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:sparkles: add helpers to validate files against a JSON schema in `jsonschema`

changes/20260521184825.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:sparkles: `[filesystem]` Added validation rules for checking that a file path has the required extension

utils/filesystem/filepath.go

Lines changed: 0 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
package filesystem
22

33
import (
4-
"io/fs"
54
"path"
65
"path/filepath"
76
"strings"
8-
"syscall"
9-
10-
validation "github.com/go-ozzo/ozzo-validation/v4"
117

128
"github.com/ARM-software/golang-utils/utils/commonerrors"
139
"github.com/ARM-software/golang-utils/utils/platform"
@@ -282,103 +278,3 @@ func EvalSymlinks(fs FS, pathWithSymlinks string) (populatedPath string, err err
282278
func EndsWithPathSeparator(fs FS, filePath string) bool {
283279
return strings.HasSuffix(filePath, "/") || strings.HasSuffix(filePath, string(fs.PathSeparator()))
284280
}
285-
286-
// NewPathValidationRule returns a validation rule to use in configuration.
287-
// The rule checks whether a string is a valid not empty path.
288-
// `when` describes whether the rule is enforced or not
289-
func NewPathValidationRule(filesystem FS, when bool) validation.Rule {
290-
return &pathValidationRule{condition: when, filesystem: filesystem}
291-
}
292-
293-
// NewOSPathValidationRule returns a validation rule to use in configuration.
294-
// The rule checks whether a string is a valid path for the Operating System's filesystem.
295-
// `when` describes whether the rule is enforced or not
296-
func NewOSPathValidationRule(when bool) validation.Rule {
297-
return NewPathValidationRule(GetGlobalFileSystem(), when)
298-
}
299-
300-
type pathValidationRule struct {
301-
condition bool
302-
filesystem FS
303-
}
304-
305-
func (r *pathValidationRule) Validate(value interface{}) error {
306-
err := validation.Required.When(r.condition).Validate(value)
307-
if err != nil {
308-
return commonerrors.WrapErrorf(commonerrors.ErrUndefined, err, "path [%v] is required", value)
309-
}
310-
if !r.condition {
311-
return nil
312-
}
313-
pathString, err := validation.EnsureString(value)
314-
if err != nil {
315-
return commonerrors.WrapErrorf(commonerrors.ErrInvalid, err, "path [%v] must be a string", value)
316-
}
317-
pathString = strings.TrimSpace(pathString)
318-
// This check is here because it validates the path on any platform (it is a cross-platform check)
319-
// Indeed if the path exists, then it can only be valid.
320-
if r.filesystem.Exists(pathString) {
321-
return nil
322-
}
323-
324-
// Inspired from https://github.com/go-playground/validator/blob/84254aeb5a59e615ec0b66ab53b988bc0677f55e/baked_in.go#L1604 and https://stackoverflow.com/questions/35231846/golang-check-if-string-is-valid-path
325-
if pathString == "" {
326-
return commonerrors.Newf(commonerrors.ErrUndefined, "the path [%v] is empty", value)
327-
}
328-
// This check is to catch errors on Linux. It does not work as well on Windows.
329-
if _, err := r.filesystem.Stat(pathString); err != nil {
330-
switch t := err.(type) {
331-
case *fs.PathError:
332-
if t.Err == syscall.EINVAL {
333-
return commonerrors.WrapErrorf(commonerrors.ErrInvalid, err, "the path [%v] has invalid characters", value)
334-
}
335-
default:
336-
// make the linter happy
337-
}
338-
}
339-
// The following case is not caught on Windows by the check above.
340-
if strings.Contains(pathString, "\n") {
341-
return commonerrors.Newf(commonerrors.ErrInvalid, "the path [%v] has carriage returns characters", value)
342-
}
343-
344-
// TODO add platform validation checks: e.g. https://learn.microsoft.com/en-gb/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN on windows
345-
346-
return nil
347-
}
348-
349-
// NewPathExistRule returns a validation rule to use in configuration.
350-
// The rule checks whether a string is a valid not empty path and actually exists.
351-
// `when` describes whether the rule is enforced or not.
352-
func NewPathExistRule(filesystem FS, when bool) validation.Rule {
353-
return &pathExistValidationRule{filesystem: filesystem, condition: when}
354-
}
355-
356-
// NewOSPathExistRule returns a validation rule to use in configuration.
357-
// The rule checks whether a string is a valid path for the Operating system's filesystem and actually exists.
358-
// `when` describes whether the rule is enforced or not.
359-
func NewOSPathExistRule(when bool) validation.Rule {
360-
return NewPathExistRule(GetGlobalFileSystem(), when)
361-
}
362-
363-
type pathExistValidationRule struct {
364-
condition bool
365-
filesystem FS
366-
}
367-
368-
func (r *pathExistValidationRule) Validate(value interface{}) error {
369-
err := NewPathValidationRule(r.filesystem, r.condition).Validate(value)
370-
if err != nil {
371-
return err
372-
}
373-
if !r.condition {
374-
return nil
375-
}
376-
path, err := validation.EnsureString(value)
377-
if err != nil {
378-
return commonerrors.WrapErrorf(commonerrors.ErrInvalid, err, "path [%v] must be a string", value)
379-
}
380-
if !r.filesystem.Exists(path) {
381-
err = commonerrors.Newf(commonerrors.ErrNotFound, "path [%v] does not exist", path)
382-
}
383-
return err
384-
}

utils/filesystem/filepath_test.go

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"github.com/stretchr/testify/assert"
1212
"github.com/stretchr/testify/require"
1313

14-
"github.com/ARM-software/golang-utils/utils/commonerrors"
15-
"github.com/ARM-software/golang-utils/utils/commonerrors/errortest"
1614
"github.com/ARM-software/golang-utils/utils/platform"
1715
)
1816

@@ -219,72 +217,6 @@ func TestEndsWithPathSeparator(t *testing.T) {
219217
}
220218
}
221219

222-
func TestNewPathExistRule(t *testing.T) {
223-
t.Run("disable", func(t *testing.T) {
224-
err := NewOSPathExistRule(false).Validate(faker.URL())
225-
require.NoError(t, err)
226-
})
227-
t.Run("happy existing path", func(t *testing.T) {
228-
require.NoError(t, NewOSPathExistRule(true).Validate(TempDirectory()))
229-
testDir, err := TempDirInTempDir("test-path-rule-")
230-
require.NoError(t, err)
231-
defer func() { _ = Rm(testDir) }()
232-
require.NoError(t, NewOSPathExistRule(true).Validate(testDir))
233-
testFile, err := TouchTempFile(testDir, "test-file*.test")
234-
require.NoError(t, err)
235-
require.NoError(t, NewOSPathExistRule(true).Validate(testFile))
236-
})
237-
t.Run("non-existent path but valid", func(t *testing.T) {
238-
err := NewOSPathExistRule(true).Validate(strings.ReplaceAll(faker.Sentence(), " ", "/"))
239-
require.Error(t, err)
240-
errortest.AssertError(t, err, commonerrors.ErrNotFound)
241-
err = NewOSPathValidationRule(true).Validate(strings.ReplaceAll(faker.Sentence(), " ", "/"))
242-
require.NoError(t, err)
243-
err = NewOSPathExistRule(true).Validate(faker.URL())
244-
require.Error(t, err)
245-
errortest.AssertError(t, err, commonerrors.ErrNotFound)
246-
err = NewOSPathValidationRule(true).Validate(faker.URL())
247-
require.NoError(t, err)
248-
})
249-
250-
t.Run("invalid paths", func(t *testing.T) {
251-
tests := []struct {
252-
entry any
253-
expectedError []error
254-
}{
255-
{
256-
entry: nil,
257-
expectedError: []error{commonerrors.ErrUndefined, commonerrors.ErrInvalid},
258-
},
259-
{
260-
entry: " ",
261-
expectedError: []error{commonerrors.ErrUndefined, commonerrors.ErrInvalid},
262-
},
263-
{
264-
entry: 123,
265-
expectedError: []error{commonerrors.ErrInvalid},
266-
},
267-
{
268-
entry: fmt.Sprintf("%v\n%v\n%v", faker.Paragraph(), faker.Paragraph(), faker.Sentence()),
269-
expectedError: []error{commonerrors.ErrInvalid},
270-
},
271-
}
272-
for i := range tests {
273-
test := tests[i]
274-
t.Run(fmt.Sprintf("%v", test.entry), func(t *testing.T) {
275-
err := NewOSPathValidationRule(true).Validate(test.entry)
276-
require.Error(t, err)
277-
errortest.AssertError(t, err, test.expectedError...)
278-
err = NewOSPathExistRule(true).Validate(test.entry)
279-
require.Error(t, err)
280-
errortest.AssertError(t, err, test.expectedError...)
281-
})
282-
}
283-
284-
})
285-
286-
}
287-
288220
func TestFilePathJoin(t *testing.T) {
289221
embedFS, err := NewEmbedFileSystem(&testContent)
290222
require.NoError(t, err)

0 commit comments

Comments
 (0)