| /* | 
 |  * Licensed to the Apache Software Foundation (ASF) under one or more | 
 |  * contributor license agreements.  See the NOTICE file distributed with | 
 |  * this work for additional information regarding copyright ownership. | 
 |  * The ASF licenses this file to You under the Apache License, Version 2.0 | 
 |  * (the "License"); you may not use this file except in compliance with | 
 |  * the License.  You may obtain a copy of the License at | 
 |  * | 
 |  *     http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | package rest | 
 |  | 
 | import ( | 
 | 	"encoding/json" | 
 | 	"fmt" | 
 | 	"net/url" | 
 | 	"strings" | 
 | ) | 
 |  | 
 | import ( | 
 | 	"github.com/pkg/errors" | 
 |  | 
 | 	"k8s.io/kube-openapi/pkg/validation/strfmt" | 
 | 	"k8s.io/kube-openapi/pkg/validation/validate" | 
 |  | 
 | 	"sigs.k8s.io/yaml" | 
 | ) | 
 |  | 
 | import ( | 
 | 	core_model "github.com/apache/dubbo-kubernetes/pkg/core/resources/model" | 
 | 	"github.com/apache/dubbo-kubernetes/pkg/core/resources/model/rest/v1alpha1" | 
 | 	"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry" | 
 | 	"github.com/apache/dubbo-kubernetes/pkg/core/validators" | 
 | ) | 
 |  | 
 | var YAML = &unmarshaler{unmarshalFn: func(bytes []byte, i interface{}) error { | 
 | 	return yaml.Unmarshal(bytes, i) | 
 | }} | 
 | var JSON = &unmarshaler{unmarshalFn: json.Unmarshal} | 
 |  | 
 | type unmarshaler struct { | 
 | 	unmarshalFn func([]byte, interface{}) error | 
 | } | 
 |  | 
 | type InvalidResourceError struct { | 
 | 	Reason string | 
 | } | 
 |  | 
 | func (e *InvalidResourceError) Error() string { | 
 | 	return e.Reason | 
 | } | 
 |  | 
 | func (e *InvalidResourceError) Is(target error) bool { | 
 | 	t, ok := target.(*InvalidResourceError) | 
 | 	if !ok { | 
 | 		return false | 
 | 	} | 
 | 	return t.Reason == e.Reason || t.Reason == "" | 
 | } | 
 |  | 
 | func (u *unmarshaler) UnmarshalCore(bytes []byte) (core_model.Resource, error) { | 
 | 	m := v1alpha1.ResourceMeta{} | 
 | 	if err := u.unmarshalFn(bytes, &m); err != nil { | 
 | 		return nil, &InvalidResourceError{Reason: fmt.Sprintf("invalid meta type: %q", err.Error())} | 
 | 	} | 
 | 	desc, err := registry.Global().DescriptorFor(core_model.ResourceType(m.Type)) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	restResource, err := u.Unmarshal(bytes, desc) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	coreRes, err := To.Core(restResource) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	return coreRes, nil | 
 | } | 
 |  | 
 | func (u *unmarshaler) Unmarshal(bytes []byte, desc core_model.ResourceTypeDescriptor) (Resource, error) { | 
 | 	resource := desc.NewObject() | 
 | 	restResource := From.Resource(resource) | 
 | 	if desc.IsPluginOriginated { | 
 | 		// desc.Schema is set only for new plugin originated policies | 
 | 		rawObj := map[string]interface{}{} | 
 | 		// Unfortunately to validate new policies we must first unmarshal into a rawObj | 
 | 		if err := u.unmarshalFn(bytes, &rawObj); err != nil { | 
 | 			return nil, &InvalidResourceError{Reason: fmt.Sprintf("invalid %s object: %q", desc.Name, err.Error())} | 
 | 		} | 
 | 		validator := validate.NewSchemaValidator(desc.Schema, nil, "", strfmt.Default) | 
 | 		res := validator.Validate(rawObj) | 
 | 		if !res.IsValid() { | 
 | 			return nil, toValidationError(res) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if err := u.unmarshalFn(bytes, restResource); err != nil { | 
 | 		return nil, &InvalidResourceError{Reason: fmt.Sprintf("invalid %s object: %q", desc.Name, err.Error())} | 
 | 	} | 
 |  | 
 | 	if err := core_model.Validate(resource); err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	return restResource, nil | 
 | } | 
 |  | 
 | func (u *unmarshaler) UnmarshalListToCore(b []byte, rs core_model.ResourceList) error { | 
 | 	rsr := &ResourceListReceiver{ | 
 | 		NewResource: rs.NewItem, | 
 | 	} | 
 | 	if err := u.unmarshalFn(b, rsr); err != nil { | 
 | 		return err | 
 | 	} | 
 | 	for _, ri := range rsr.ResourceList.Items { | 
 | 		r := rs.NewItem() | 
 | 		if err := r.SetSpec(ri.GetSpec()); err != nil { | 
 | 			return err | 
 | 		} | 
 | 		r.SetMeta(ri.GetMeta()) | 
 | 		_ = rs.AddItem(r) | 
 | 	} | 
 | 	if rsr.Next != nil { | 
 | 		uri, err := url.ParseRequestURI(*rsr.Next) | 
 | 		if err != nil { | 
 | 			return errors.Wrap(err, "invalid next URL from the server") | 
 | 		} | 
 | 		offset := uri.Query().Get("offset") | 
 | 		// we do not preserve here the size of the page, but since it is used in dubboctl | 
 | 		// user will rerun command with the page size of his choice | 
 | 		if offset != "" { | 
 | 			rs.GetPagination().SetNextOffset(offset) | 
 | 		} | 
 | 	} | 
 | 	rs.GetPagination().SetTotal(rsr.ResourceList.Total) | 
 | 	return nil | 
 | } | 
 |  | 
 | func toValidationError(res *validate.Result) *validators.ValidationError { | 
 | 	verr := &validators.ValidationError{} | 
 | 	for _, e := range res.Errors { | 
 | 		parts := strings.Split(e.Error(), " ") | 
 | 		if len(parts) > 1 && strings.HasPrefix(parts[0], "spec.") { | 
 | 			verr.AddViolation(parts[0], strings.Join(parts[1:], " ")) | 
 | 		} else { | 
 | 			verr.AddViolation("", e.Error()) | 
 | 		} | 
 | 	} | 
 | 	return verr | 
 | } |