summaryrefslogtreecommitdiffstats
path: root/modules/migration/file_format.go
diff options
context:
space:
mode:
Diffstat (limited to 'modules/migration/file_format.go')
-rw-r--r--modules/migration/file_format.go112
1 files changed, 112 insertions, 0 deletions
diff --git a/modules/migration/file_format.go b/modules/migration/file_format.go
new file mode 100644
index 0000000000..30e1d256cd
--- /dev/null
+++ b/modules/migration/file_format.go
@@ -0,0 +1,112 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migration
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+
+ "github.com/santhosh-tekuri/jsonschema/v5"
+ "gopkg.in/yaml.v2"
+)
+
+// Load project data from file, with optional validation
+func Load(filename string, data interface{}, validation bool) error {
+ isJSON := strings.HasSuffix(filename, ".json")
+
+ bs, err := os.ReadFile(filename)
+ if err != nil {
+ return err
+ }
+
+ if validation {
+ err := validate(bs, data, isJSON)
+ if err != nil {
+ return err
+ }
+ }
+ return unmarshal(bs, data, isJSON)
+}
+
+func unmarshal(bs []byte, data interface{}, isJSON bool) error {
+ if isJSON {
+ return json.Unmarshal(bs, data)
+ }
+ return yaml.Unmarshal(bs, data)
+}
+
+func getSchema(filename string) (*jsonschema.Schema, error) {
+ c := jsonschema.NewCompiler()
+ c.LoadURL = openSchema
+ return c.Compile(filename)
+}
+
+func validate(bs []byte, datatype interface{}, isJSON bool) error {
+ var v interface{}
+ err := unmarshal(bs, &v, isJSON)
+ if err != nil {
+ return err
+ }
+ if !isJSON {
+ v, err = toStringKeys(v)
+ if err != nil {
+ return err
+ }
+ }
+
+ var schemaFilename string
+ switch datatype := datatype.(type) {
+ case *[]*Issue:
+ schemaFilename = "issue.json"
+ case *[]*Milestone:
+ schemaFilename = "milestone.json"
+ default:
+ return fmt.Errorf("file_format:validate: %T has not a validation implemented", datatype)
+ }
+
+ sch, err := getSchema(schemaFilename)
+ if err != nil {
+ return err
+ }
+ err = sch.Validate(v)
+ if err != nil {
+ log.Error("migration validation with %s failed for\n%s", schemaFilename, string(bs))
+ }
+ return err
+}
+
+func toStringKeys(val interface{}) (interface{}, error) {
+ var err error
+ switch val := val.(type) {
+ case map[interface{}]interface{}:
+ m := make(map[string]interface{})
+ for k, v := range val {
+ k, ok := k.(string)
+ if !ok {
+ return nil, fmt.Errorf("found non-string key %T %s", k, k)
+ }
+ m[k], err = toStringKeys(v)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return m, nil
+ case []interface{}:
+ l := make([]interface{}, len(val))
+ for i, v := range val {
+ l[i], err = toStringKeys(v)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return l, nil
+ default:
+ return val, nil
+ }
+}