2022-01-26 12:45:51 +03:00
// Copyright 2022 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2022-01-26 12:45:51 +03:00
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
2023-07-04 21:36:08 +03:00
func Load ( filename string , data any , validation bool ) error {
2022-01-26 12:45:51 +03:00
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 )
}
2023-07-04 21:36:08 +03:00
func unmarshal ( bs [ ] byte , data any , isJSON bool ) error {
2022-01-26 12:45:51 +03:00
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 )
}
2023-07-04 21:36:08 +03:00
func validate ( bs [ ] byte , datatype any , isJSON bool ) error {
var v any
2022-01-26 12:45:51 +03:00
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 {
2023-01-27 15:56:00 +03:00
log . Error ( "migration validation with %s failed:\n%#v" , schemaFilename , err )
2022-01-26 12:45:51 +03:00
}
return err
}
2023-07-04 21:36:08 +03:00
func toStringKeys ( val any ) ( any , error ) {
2022-01-26 12:45:51 +03:00
var err error
switch val := val . ( type ) {
2023-07-04 21:36:08 +03:00
case map [ string ] any :
m := make ( map [ string ] any )
2022-01-26 12:45:51 +03:00
for k , v := range val {
m [ k ] , err = toStringKeys ( v )
if err != nil {
return nil , err
}
}
return m , nil
2023-07-04 21:36:08 +03:00
case [ ] any :
l := make ( [ ] any , len ( val ) )
2022-01-26 12:45:51 +03:00
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
}
}