Skip to content

Commit 8f9f45a

Browse files
authored
Export MarshalPolicyConfigAsJSON for policies (#3898)
1 parent 9706401 commit 8f9f45a

File tree

2 files changed

+208
-198
lines changed

2 files changed

+208
-198
lines changed

private/bufpkg/bufpolicy/digest.go

Lines changed: 1 addition & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,12 @@ package bufpolicy
1717
import (
1818
"bytes"
1919
"encoding/hex"
20-
"encoding/json"
2120
"errors"
2221
"fmt"
23-
"slices"
2422
"strconv"
2523
"strings"
2624

27-
pluginoptionv1 "buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go/buf/plugin/option/v1"
28-
"buf.build/go/bufplugin/option"
29-
"buf.build/go/standard/xslices"
3025
"github.com/bufbuild/buf/private/bufpkg/bufcas"
31-
"github.com/bufbuild/buf/private/bufpkg/bufconfig"
3226
"github.com/bufbuild/buf/private/bufpkg/bufparse"
3327
"github.com/bufbuild/buf/private/pkg/syserror"
3428
)
@@ -221,7 +215,7 @@ func (*digest) isDigest() {}
221215

222216
// getO1Digest returns the O1 digest for the given PolicyConfig.
223217
func getO1Digest(policyConfig PolicyConfig) (Digest, error) {
224-
policyDataJSON, err := marshalStablePolicyConfig(policyConfig)
218+
policyDataJSON, err := marshalPolicyConfigAsJSON(policyConfig)
225219
if err != nil {
226220
return nil, err
227221
}
@@ -231,194 +225,3 @@ func getO1Digest(policyConfig PolicyConfig) (Digest, error) {
231225
}
232226
return NewDigest(DigestTypeO1, bufcasDigest)
233227
}
234-
235-
// marshalStablePolicyConfig marshals the given PolicyConfig to a stable JSON representation.
236-
//
237-
// This is used for the O1 digest and should not be used for other purposes.
238-
// It is a valid JSON encoding of the type buf.registry.policy.v1beta1.PolicyConfig.
239-
func marshalStablePolicyConfig(policyConfig PolicyConfig) ([]byte, error) {
240-
lintConfig := policyConfig.LintConfig()
241-
if lintConfig == nil {
242-
return nil, syserror.Newf("policyConfig.LintConfig() must not be nil")
243-
}
244-
breakingConfig := policyConfig.BreakingConfig()
245-
if breakingConfig == nil {
246-
return nil, syserror.Newf("policyConfig.BreakingConfig() must not be nil")
247-
}
248-
pluginConfigs, err := xslices.MapError(policyConfig.PluginConfigs(), func(pluginConfig bufconfig.PluginConfig) (*policyV1Beta1PolicyConfig_PluginConfig, error) {
249-
ref := pluginConfig.Ref()
250-
if ref == nil {
251-
return nil, fmt.Errorf("PluginConfig must have a non-nil Ref")
252-
}
253-
optionsConfig, err := optionsToOptionConfig(pluginConfig.Options())
254-
if err != nil {
255-
return nil, err
256-
}
257-
return &policyV1Beta1PolicyConfig_PluginConfig{
258-
Name: policyV1Beta1PolicyConfig_PluginConfig_Name{
259-
Owner: ref.FullName().Owner(),
260-
Plugin: ref.FullName().Name(),
261-
Ref: ref.Ref(),
262-
},
263-
Options: optionsConfig,
264-
Args: pluginConfig.Args(),
265-
}, nil
266-
})
267-
if err != nil {
268-
return nil, fmt.Errorf("failed converting PluginConfigs to PolicyConfig_CheckPluginConfig: %w", err)
269-
}
270-
slices.SortFunc(pluginConfigs, func(a, b *policyV1Beta1PolicyConfig_PluginConfig) int {
271-
// Sort by owner, plugin, and ref.
272-
return strings.Compare(
273-
fmt.Sprintf("%s/%s:%s", a.Name.Owner, a.Name.Plugin, a.Name.Ref),
274-
fmt.Sprintf("%s/%s:%s", b.Name.Owner, b.Name.Plugin, b.Name.Ref),
275-
)
276-
})
277-
config := policyV1Beta1PolicyConfig{
278-
Lint: &policyV1Beta1PolicyConfig_LintConfig{
279-
Use: lintConfig.UseIDsAndCategories(),
280-
Except: lintConfig.ExceptIDsAndCategories(),
281-
EnumZeroValueSuffix: lintConfig.EnumZeroValueSuffix(),
282-
RpcAllowSameRequestResponse: lintConfig.RPCAllowSameRequestResponse(),
283-
RpcAllowGoogleProtobufEmptyRequests: lintConfig.RPCAllowGoogleProtobufEmptyRequests(),
284-
RpcAllowGoogleProtobufEmptyResponses: lintConfig.RPCAllowGoogleProtobufEmptyResponses(),
285-
ServiceSuffix: lintConfig.ServiceSuffix(),
286-
},
287-
Breaking: &policyV1Beta1PolicyConfig_BreakingConfig{
288-
Use: breakingConfig.UseIDsAndCategories(),
289-
Except: breakingConfig.ExceptIDsAndCategories(),
290-
IgnoreUnstablePackages: breakingConfig.IgnoreUnstablePackages(),
291-
},
292-
Plugins: pluginConfigs,
293-
}
294-
return json.Marshal(config)
295-
}
296-
297-
// policyV1Beta1PolicyConfig is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.
298-
type policyV1Beta1PolicyConfig struct {
299-
Lint *policyV1Beta1PolicyConfig_LintConfig `json:"lint,omitempty"`
300-
Breaking *policyV1Beta1PolicyConfig_BreakingConfig `json:"breaking,omitempty"`
301-
Plugins []*policyV1Beta1PolicyConfig_PluginConfig `json:"plugins,omitempty"`
302-
}
303-
304-
// policyV1Beta1PolicyConfig_LintConfig is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.LintConfig.
305-
type policyV1Beta1PolicyConfig_LintConfig struct {
306-
Use []string `json:"use,omitempty"`
307-
Except []string `json:"except,omitempty"`
308-
EnumZeroValueSuffix string `json:"enumZeroValue_suffix,omitempty"`
309-
RpcAllowSameRequestResponse bool `json:"rpcAllowSame_request_response,omitempty"`
310-
RpcAllowGoogleProtobufEmptyRequests bool `json:"rpcAllowGoogleProtobufEmptyRequests,omitempty"`
311-
RpcAllowGoogleProtobufEmptyResponses bool `json:"rpcAllowGoogleProtobufEmptyResponses,omitempty"`
312-
ServiceSuffix string `json:"serviceSuffix,omitempty"`
313-
}
314-
315-
// policyV1Beta1PolicyConfig_BreakingConfig is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.BreakingConfig.
316-
type policyV1Beta1PolicyConfig_BreakingConfig struct {
317-
Use []string `json:"use,omitempty"`
318-
Except []string `json:"except,omitempty"`
319-
IgnoreUnstablePackages bool `json:"ignoreUnstablePackages,omitempty"`
320-
}
321-
322-
// policyV1Beta1PolicyConfig_PluginConfig is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.PluginConfig.
323-
type policyV1Beta1PolicyConfig_PluginConfig struct {
324-
Name policyV1Beta1PolicyConfig_PluginConfig_Name `json:"name,omitempty"`
325-
Options []*optionV1Option `json:"options,omitempty"`
326-
Args []string `json:"args,omitempty"`
327-
}
328-
329-
// policyV1Beta1PolicyConfig_PluginConfig_Name is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.PluginConfig.Name.
330-
type policyV1Beta1PolicyConfig_PluginConfig_Name struct {
331-
Owner string `json:"owner,omitempty"`
332-
Plugin string `json:"plugin,omitempty"`
333-
Ref string `json:"ref,omitempty"`
334-
}
335-
336-
// optionV1Option is a stable JSON representation of the buf.plugin.option.v1.Option.
337-
type optionV1Option struct {
338-
Key string `json:"key,omitempty"`
339-
Value *optionV1Value `json:"value,omitempty"`
340-
}
341-
342-
// optionV1Value is a stable JSON representation of the buf.plugin.option.v1.Value.
343-
type optionV1Value struct {
344-
BoolValue bool `json:"boolValue,omitempty"`
345-
Int64Value int64 `json:"intValue,omitempty"`
346-
DoubleValue float64 `json:"floatValue,omitempty"`
347-
StringValue string `json:"stringValue,omitempty"`
348-
BytesValue []byte `json:"bytesValue,omitempty"`
349-
ListValue *optionV1ListValue `json:"listValue,omitempty"`
350-
}
351-
352-
// optionV1ListValue is a stable JSON representation of the buf.plugin.option.v1.ListValue.
353-
type optionV1ListValue struct {
354-
Values []*optionV1Value `json:"values,omitempty"`
355-
}
356-
357-
// optionsToOptionsV1Options converts a map of options to a slice of optionV1Option.
358-
func optionsToOptionConfig(keyToValue map[string]any) ([]*optionV1Option, error) {
359-
options, err := option.NewOptions(keyToValue) // This will validate the options.
360-
if err != nil {
361-
return nil, fmt.Errorf("failed to convert options: %w", err)
362-
}
363-
optionsProto, err := options.ToProto()
364-
if err != nil {
365-
return nil, fmt.Errorf("failed to convert options to proto: %w", err)
366-
}
367-
// Sort the options by key to ensure a stable order.
368-
slices.SortFunc(optionsProto, func(a, b *pluginoptionv1.Option) int {
369-
return strings.Compare(a.Key, b.Key)
370-
})
371-
optionsV1Options := make([]*optionV1Option, len(optionsProto))
372-
for i, optionProto := range optionsProto {
373-
optionValue, err := optionV1ValueProtoToOptionValue(optionProto.Value)
374-
if err != nil {
375-
return nil, fmt.Errorf("failed to convert option value: %w", err)
376-
}
377-
optionsV1Options[i] = &optionV1Option{
378-
Key: optionProto.Key,
379-
Value: optionValue,
380-
}
381-
}
382-
return optionsV1Options, nil
383-
}
384-
385-
func optionV1ValueProtoToOptionValue(optionValue *pluginoptionv1.Value) (*optionV1Value, error) {
386-
if optionValue == nil {
387-
return nil, nil
388-
}
389-
switch optionValue.Type.(type) {
390-
case *pluginoptionv1.Value_BoolValue:
391-
return &optionV1Value{BoolValue: optionValue.GetBoolValue()}, nil
392-
case *pluginoptionv1.Value_Int64Value:
393-
return &optionV1Value{Int64Value: optionValue.GetInt64Value()}, nil
394-
case *pluginoptionv1.Value_DoubleValue:
395-
return &optionV1Value{DoubleValue: optionValue.GetDoubleValue()}, nil
396-
case *pluginoptionv1.Value_StringValue:
397-
return &optionV1Value{StringValue: optionValue.GetStringValue()}, nil
398-
case *pluginoptionv1.Value_BytesValue:
399-
return &optionV1Value{BytesValue: optionValue.GetBytesValue()}, nil
400-
case *pluginoptionv1.Value_ListValue:
401-
listValue, err := optionV1ListValueProtoToOptionListValue(optionValue.GetListValue())
402-
if err != nil {
403-
return nil, err
404-
}
405-
return &optionV1Value{ListValue: listValue}, nil
406-
default:
407-
return nil, fmt.Errorf("unknown option value type: %T", optionValue.Type)
408-
}
409-
}
410-
411-
func optionV1ListValueProtoToOptionListValue(listValue *pluginoptionv1.ListValue) (*optionV1ListValue, error) {
412-
if listValue == nil {
413-
return nil, nil
414-
}
415-
values := make([]*optionV1Value, len(listValue.Values))
416-
for i, value := range listValue.Values {
417-
optionValue, err := optionV1ValueProtoToOptionValue(value)
418-
if err != nil {
419-
return nil, fmt.Errorf("failed to convert option value: %w", err)
420-
}
421-
values[i] = optionValue
422-
}
423-
return &optionV1ListValue{Values: values}, nil
424-
}

0 commit comments

Comments
 (0)