2022-01-26 12:45:51 +03:00
// 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"
2022-11-21 11:36:59 +03:00
"time"
2022-01-26 12:45:51 +03:00
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"github.com/santhosh-tekuri/jsonschema/v5"
2022-11-21 11:36:59 +03:00
"gopkg.in/yaml.v3"
2022-01-26 12:45:51 +03:00
)
// 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 ) {
2022-11-21 11:36:59 +03:00
case map [ string ] interface { } :
2022-01-26 12:45:51 +03:00
m := make ( map [ string ] interface { } )
for k , v := range val {
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
2022-11-21 11:36:59 +03:00
case time . Time :
return val . Format ( time . RFC3339 ) , nil
2022-01-26 12:45:51 +03:00
default :
return val , nil
}
}