2023-08-01 18:39:15 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
2023-08-11 13:12:13 +00:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-08-01 18:39:15 +00:00
|
|
|
|
|
|
|
package resource
|
|
|
|
|
|
|
|
import (
|
2023-08-09 16:02:17 +00:00
|
|
|
"context"
|
|
|
|
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
2023-08-01 18:39:15 +00:00
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
|
|
|
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DecodedResource is a generic holder to contain an original Resource and its
|
|
|
|
// decoded contents.
|
2023-08-22 00:20:19 +00:00
|
|
|
type DecodedResource[T proto.Message] struct {
|
2023-10-26 20:39:06 +00:00
|
|
|
// Embedding here allows us to shadow the Resource.Data Any field to fake out
|
|
|
|
// using a single struct with inlined data.
|
|
|
|
*pbresource.Resource
|
|
|
|
Data T
|
2023-08-01 18:39:15 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 17:31:06 +00:00
|
|
|
func (d *DecodedResource[T]) GetResource() *pbresource.Resource {
|
|
|
|
if d == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return d.Resource
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *DecodedResource[T]) GetData() T {
|
|
|
|
if d == nil {
|
|
|
|
var zero T
|
|
|
|
return zero
|
|
|
|
}
|
|
|
|
|
|
|
|
return d.Data
|
|
|
|
}
|
|
|
|
|
2023-08-01 18:39:15 +00:00
|
|
|
// Decode will generically decode the provided resource into a 2-field
|
|
|
|
// structure that holds onto the original Resource and the decoded contents.
|
|
|
|
//
|
|
|
|
// Returns an ErrDataParse on unmarshalling errors.
|
2023-08-22 00:20:19 +00:00
|
|
|
func Decode[T proto.Message](res *pbresource.Resource) (*DecodedResource[T], error) {
|
|
|
|
var zero T
|
|
|
|
data := zero.ProtoReflect().New().Interface().(T)
|
2023-09-25 16:50:07 +00:00
|
|
|
// check that there is data to unmarshall
|
|
|
|
if res.Data != nil {
|
|
|
|
if err := res.Data.UnmarshalTo(data); err != nil {
|
|
|
|
return nil, NewErrDataParse(data, err)
|
|
|
|
}
|
2023-08-01 18:39:15 +00:00
|
|
|
}
|
2023-08-22 00:20:19 +00:00
|
|
|
return &DecodedResource[T]{
|
2023-08-01 18:39:15 +00:00
|
|
|
Resource: res,
|
|
|
|
Data: data,
|
|
|
|
}, nil
|
|
|
|
}
|
2023-08-09 16:02:17 +00:00
|
|
|
|
|
|
|
// GetDecodedResource will generically read the requested resource using the
|
|
|
|
// client and either return nil on a NotFound or decode the response value.
|
2023-08-22 00:20:19 +00:00
|
|
|
func GetDecodedResource[T proto.Message](ctx context.Context, client pbresource.ResourceServiceClient, id *pbresource.ID) (*DecodedResource[T], error) {
|
2023-08-09 16:02:17 +00:00
|
|
|
rsp, err := client.Read(ctx, &pbresource.ReadRequest{Id: id})
|
|
|
|
switch {
|
|
|
|
case status.Code(err) == codes.NotFound:
|
|
|
|
return nil, nil
|
|
|
|
case err != nil:
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-08-22 00:20:19 +00:00
|
|
|
return Decode[T](rsp.Resource)
|
2023-08-09 16:02:17 +00:00
|
|
|
}
|
2023-11-10 01:57:53 +00:00
|
|
|
|
|
|
|
func ListDecodedResource[T proto.Message](ctx context.Context, client pbresource.ResourceServiceClient, req *pbresource.ListRequest) ([]*DecodedResource[T], error) {
|
|
|
|
rsp, err := client.List(ctx, req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
results := make([]*DecodedResource[T], len(rsp.Resources))
|
|
|
|
for idx, rsc := range rsp.Resources {
|
|
|
|
d, err := Decode[T](rsc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
results[idx] = d
|
|
|
|
}
|
|
|
|
return results, nil
|
|
|
|
}
|