Skip to content

Commit

Permalink
Unexport Format strings
Browse files Browse the repository at this point in the history
With the addition of the escaping term, format strings now have many permutations and doing direct string comparisons of formats is not reliable.
Instead, users should call FormatType and compare the result against the possible enum values.

Signed-off-by: Owen Williams <owen.williams@grafana.com>
  • Loading branch information
ywwg committed Feb 14, 2024
1 parent 773d566 commit 05d7387
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 44 deletions.
14 changes: 7 additions & 7 deletions expfmt/decode.go
Expand Up @@ -45,29 +45,29 @@ func ResponseFormat(h http.Header) Format {

mediatype, params, err := mime.ParseMediaType(ct)
if err != nil {
return FmtUnknown
return fmtUnknown
}

const textType = "text/plain"

switch mediatype {
case ProtoType:
if p, ok := params["proto"]; ok && p != ProtoProtocol {
return FmtUnknown
return fmtUnknown
}
if e, ok := params["encoding"]; ok && e != "delimited" {
return FmtUnknown
return fmtUnknown
}
return FmtProtoDelim
return fmtProtoDelim

case textType:
if v, ok := params["version"]; ok && v != TextVersion {
return FmtUnknown
return fmtUnknown
}
return FmtText
return fmtText
}

return FmtUnknown
return fmtUnknown
}

// NewDecoder returns a new decoder based on the given input format.
Expand Down
14 changes: 7 additions & 7 deletions expfmt/decode_test.go
Expand Up @@ -421,27 +421,27 @@ func testDiscriminatorHTTPHeader(t testing.TB) {
}{
{
input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`},
output: FmtProtoDelim,
output: fmtProtoDelim,
},
{
input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="illegal"; encoding="delimited"`},
output: FmtUnknown,
output: fmtUnknown,
},
{
input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="illegal"`},
output: FmtUnknown,
output: fmtUnknown,
},
{
input: map[string]string{"Content-Type": `text/plain; version=0.0.4`},
output: FmtText,
output: fmtText,
},
{
input: map[string]string{"Content-Type": `text/plain`},
output: FmtText,
output: fmtText,
},
{
input: map[string]string{"Content-Type": `text/plain; version=0.0.3`},
output: FmtUnknown,
output: fmtUnknown,
},
}

Expand Down Expand Up @@ -547,7 +547,7 @@ func TestTextDecoderWithBufioReader(t *testing.T) {

var decoded bool
r := bufio.NewReader(strings.NewReader(example))
dec := NewDecoder(r, FmtText)
dec := NewDecoder(r, fmtText)
for {
var mf dto.MetricFamily
if err := dec.Decode(&mf); err != nil {
Expand Down
24 changes: 12 additions & 12 deletions expfmt/encode.go
Expand Up @@ -76,18 +76,18 @@ func Negotiate(h http.Header) Format {
if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
switch ac.Params["encoding"] {
case "delimited":
return FmtProtoDelim + escapingScheme
return fmtProtoDelim + escapingScheme
case "text":
return FmtProtoText + escapingScheme
return fmtProtoText + escapingScheme
case "compact-text":
return FmtProtoCompact + escapingScheme
return fmtProtoCompact + escapingScheme
}
}
if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
return FmtText + escapingScheme
return fmtText + escapingScheme
}
}
return FmtText + escapingScheme
return fmtText + escapingScheme
}

// NegotiateIncludingOpenMetrics works like Negotiate but includes
Expand All @@ -109,26 +109,26 @@ func NegotiateIncludingOpenMetrics(h http.Header) Format {
if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
switch ac.Params["encoding"] {
case "delimited":
return FmtProtoDelim + escapingScheme
return fmtProtoDelim + escapingScheme
case "text":
return FmtProtoText + escapingScheme
return fmtProtoText + escapingScheme
case "compact-text":
return FmtProtoCompact + escapingScheme
return fmtProtoCompact + escapingScheme
}
}
if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
return FmtText + escapingScheme
return fmtText + escapingScheme
}
if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion_0_0_1 || ver == OpenMetricsVersion_1_0_0 || ver == "") {
switch ver {
case OpenMetricsVersion_1_0_0:
return FmtOpenMetrics_1_0_0 + escapingScheme
return fmtOpenMetrics_1_0_0 + escapingScheme
default:
return FmtOpenMetrics_0_0_1 + escapingScheme
return fmtOpenMetrics_0_0_1 + escapingScheme
}
}
}
return FmtText + escapingScheme
return fmtText + escapingScheme
}

// NewEncoder returns a new encoder based on content type negotiation. All
Expand Down
16 changes: 8 additions & 8 deletions expfmt/encode_test.go
Expand Up @@ -201,7 +201,7 @@ func TestNegotiateOpenMetrics(t *testing.T) {

func TestEncode(t *testing.T) {
var buff bytes.Buffer
delimEncoder := NewEncoder(&buff, FmtProtoDelim)
delimEncoder := NewEncoder(&buff, fmtProtoDelim)
metric := &dto.MetricFamily{
Name: proto.String("foo_metric"),
Type: dto.MetricType_UNTYPED.Enum(),
Expand All @@ -226,7 +226,7 @@ func TestEncode(t *testing.T) {

buff.Reset()

compactEncoder := NewEncoder(&buff, FmtProtoCompact)
compactEncoder := NewEncoder(&buff, fmtProtoCompact)
err = compactEncoder.Encode(metric)
if err != nil {
t.Errorf("unexpected error during encode: %s", err.Error())
Expand All @@ -239,7 +239,7 @@ func TestEncode(t *testing.T) {

buff.Reset()

protoTextEncoder := NewEncoder(&buff, FmtProtoText)
protoTextEncoder := NewEncoder(&buff, fmtProtoText)
err = protoTextEncoder.Encode(metric)
if err != nil {
t.Errorf("unexpected error during encode: %s", err.Error())
Expand All @@ -252,7 +252,7 @@ func TestEncode(t *testing.T) {

buff.Reset()

textEncoder := NewEncoder(&buff, FmtText)
textEncoder := NewEncoder(&buff, fmtText)
err = textEncoder.Encode(metric)
if err != nil {
t.Errorf("unexpected error during encode: %s", err.Error())
Expand All @@ -273,7 +273,7 @@ func TestEncode(t *testing.T) {

func TestEscapedEncode(t *testing.T) {
var buff bytes.Buffer
delimEncoder := NewEncoder(&buff, FmtProtoDelim+"; escaping=underscores")
delimEncoder := NewEncoder(&buff, fmtProtoDelim+"; escaping=underscores")
metric := &dto.MetricFamily{
Name: proto.String("foo.metric"),
Type: dto.MetricType_UNTYPED.Enum(),
Expand Down Expand Up @@ -309,7 +309,7 @@ func TestEscapedEncode(t *testing.T) {

buff.Reset()

compactEncoder := NewEncoder(&buff, FmtProtoCompact)
compactEncoder := NewEncoder(&buff, fmtProtoCompact)
err = compactEncoder.Encode(metric)
if err != nil {
t.Errorf("unexpected error during encode: %s", err.Error())
Expand All @@ -322,7 +322,7 @@ func TestEscapedEncode(t *testing.T) {

buff.Reset()

protoTextEncoder := NewEncoder(&buff, FmtProtoText)
protoTextEncoder := NewEncoder(&buff, fmtProtoText)
err = protoTextEncoder.Encode(metric)
if err != nil {
t.Errorf("unexpected error during encode: %s", err.Error())
Expand All @@ -335,7 +335,7 @@ func TestEscapedEncode(t *testing.T) {

buff.Reset()

textEncoder := NewEncoder(&buff, FmtText)
textEncoder := NewEncoder(&buff, fmtText)
err = textEncoder.Encode(metric)
if err != nil {
t.Errorf("unexpected error during encode: %s", err.Error())
Expand Down
41 changes: 31 additions & 10 deletions expfmt/expfmt.go
Expand Up @@ -34,20 +34,21 @@ const (
TextVersion = "0.0.4"
ProtoType = `application/vnd.google.protobuf`
ProtoProtocol = `io.prometheus.client.MetricFamily`
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
protoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
OpenMetricsType = `application/openmetrics-text`
OpenMetricsVersion_0_0_1 = "0.0.1"
OpenMetricsVersion_1_0_0 = "1.0.0"

// The Content-Type values for the different wire protocols. Do not do direct
// comparisons to these constants, instead use the comparison functions.
FmtUnknown Format = `<unknown>`
FmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8`
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
FmtProtoText Format = ProtoFmt + ` encoding=text`
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
FmtOpenMetrics_1_0_0 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_1_0_0 + `; charset=utf-8`
FmtOpenMetrics_0_0_1 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_0_0_1 + `; charset=utf-8`
// The Content-Type values for the different wire protocols. Note that these
// values are now unexported. If code was relying on comparisons to these
// constants, instead use FormatType().
fmtUnknown Format = `<unknown>`
fmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8`
fmtProtoDelim Format = protoFmt + ` encoding=delimited`
fmtProtoText Format = protoFmt + ` encoding=text`
fmtProtoCompact Format = protoFmt + ` encoding=compact-text`
fmtOpenMetrics_1_0_0 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_1_0_0 + `; charset=utf-8`
fmtOpenMetrics_0_0_1 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_0_0_1 + `; charset=utf-8`
)

const (
Expand All @@ -70,6 +71,26 @@ const (
TypeOpenMetrics
)

// NewFormat generates a new Format from the type provided. Mostly used for
// tests, most Formats should be generated as part of content negotiation in
// encode.go.
func NewFormat(t FormatType) Format {
switch t {
case TypeProtoCompact:
return fmtProtoCompact
case TypeProtoDelim:
return fmtProtoDelim
case TypeProtoText:
return fmtProtoText
case TypeTextPlain:
return fmtText
case TypeOpenMetrics:
return fmtOpenMetrics_1_0_0
default:
return fmtUnknown
}
}

// FormatType deduces an overall FormatType for the given format.
func (f Format) FormatType() FormatType {
toks := strings.Split(string(f), ";")
Expand Down

0 comments on commit 05d7387

Please sign in to comment.