blob: 117463ebc9866e6ec91778bc8b6675686a2ab793 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed 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 diag
import (
"encoding/json"
"fmt"
"strconv"
"strings"
)
import (
"istio.io/api/analysis/v1alpha1"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/config/resource"
"github.com/apache/dubbo-go-pixiu/pkg/url"
)
// MessageType is a type of diagnostic message
type MessageType struct {
// The level of the message.
level Level
// The error code of the message
code string
// TODO: Make this localizable
template string
}
// Level returns the level of the MessageType
func (m *MessageType) Level() Level { return m.level }
// Code returns the code of the MessageType
func (m *MessageType) Code() string { return m.code }
// Template returns the message template used by the MessageType
func (m *MessageType) Template() string { return m.template }
// Message is a specific diagnostic message
// TODO: Implement using Analysis message API
type Message struct {
Type *MessageType
// The Parameters to the message
Parameters []interface{}
// Resource is the underlying resource instance associated with the
// message, or nil if no resource is associated with it.
Resource *resource.Instance
// DocRef is an optional reference tracker for the documentation URL
DocRef string
// Line is the line number of the error place in the message
Line int
}
// Unstructured returns this message as a JSON-style unstructured map
func (m *Message) Unstructured(includeOrigin bool) map[string]interface{} {
result := make(map[string]interface{})
result["code"] = m.Type.Code()
result["level"] = m.Type.Level().String()
if includeOrigin && m.Resource != nil {
result["origin"] = m.Resource.Origin.FriendlyName()
if m.Resource.Origin.Reference() != nil {
loc := m.Resource.Origin.Reference().String()
if m.Line != 0 {
loc = m.ReplaceLine(loc)
}
result["reference"] = loc
}
}
result["message"] = fmt.Sprintf(m.Type.Template(), m.Parameters...)
docQueryString := ""
if m.DocRef != "" {
docQueryString = fmt.Sprintf("?ref=%s", m.DocRef)
}
result["documentationUrl"] = fmt.Sprintf("%s/%s/%s", url.ConfigAnalysis, strings.ToLower(m.Type.Code()), docQueryString)
return result
}
func (m *Message) AnalysisMessageBase() *v1alpha1.AnalysisMessageBase {
docQueryString := ""
if m.DocRef != "" {
docQueryString = fmt.Sprintf("?ref=%s", m.DocRef)
}
docURL := fmt.Sprintf("%s/%s/%s", url.ConfigAnalysis, strings.ToLower(m.Type.Code()), docQueryString)
return &v1alpha1.AnalysisMessageBase{
DocumentationUrl: docURL,
Level: v1alpha1.AnalysisMessageBase_Level(v1alpha1.AnalysisMessageBase_Level_value[strings.ToUpper(m.Type.Level().String())]),
Type: &v1alpha1.AnalysisMessageBase_Type{
Code: m.Type.Code(),
},
}
}
// UnstructuredAnalysisMessageBase returns this message as a JSON-style unstructured map in AnalaysisMessageBase
// TODO(jasonwzm): Remove once message implements AnalysisMessageBase
func (m *Message) UnstructuredAnalysisMessageBase() map[string]interface{} {
mb := m.AnalysisMessageBase()
var r map[string]interface{}
j, err := json.Marshal(mb)
if err != nil {
return r
}
json.Unmarshal(j, &r) // nolint: errcheck
return r
}
// Origin returns the origin of the message
func (m *Message) Origin() string {
origin := ""
if m.Resource != nil {
loc := ""
if m.Resource.Origin.Reference() != nil {
loc = " " + m.Resource.Origin.Reference().String()
if m.Line != 0 {
loc = m.ReplaceLine(loc)
}
}
origin = " (" + m.Resource.Origin.FriendlyName() + loc + ")"
}
return origin
}
// String implements io.Stringer
func (m *Message) String() string {
return fmt.Sprintf("%v [%v]%s %s",
m.Type.Level(), m.Type.Code(), m.Origin(),
fmt.Sprintf(m.Type.Template(), m.Parameters...))
}
// MarshalJSON satisfies the Marshaler interface
func (m *Message) MarshalJSON() ([]byte, error) {
return json.Marshal(m.Unstructured(true))
}
// NewMessageType returns a new MessageType instance.
func NewMessageType(level Level, code, template string) *MessageType {
return &MessageType{
level: level,
code: code,
template: template,
}
}
// NewMessage returns a new Message instance from an existing type.
func NewMessage(mt *MessageType, r *resource.Instance, p ...interface{}) Message {
return Message{
Type: mt,
Resource: r,
Parameters: p,
}
}
// ReplaceLine replaces the line number from the input String method of Reference to the line number from Message
func (m Message) ReplaceLine(l string) string {
colonSep := strings.Split(l, ":")
if len(colonSep) < 2 {
return l
}
_, err := strconv.Atoi(strings.TrimSpace(colonSep[len(colonSep)-1]))
if err == nil {
colonSep[len(colonSep)-1] = fmt.Sprintf("%d", m.Line)
}
return strings.Join(colonSep, ":")
}