mirror of https://github.com/hashicorp/boundary
This interceptor is responsible for maintaining compatability with the JSON API with the change to the protobuf messages. The messages are changing from a single attribue field to a oneof to explicitly associate subtype attribute messages with the resource message. The interceptor transforms the request prior to the auditReqeust interceptor, and transforms the response after the auditResponse interceptor. This allows the audit interceptors to use all the necessary classification information when creating the audit events.pull/2031/head
parent
af93f7fccb
commit
198a39ef1e
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,513 @@
|
||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
||||
// source: testing/attribute/v1/attribute.proto
|
||||
|
||||
/*
|
||||
Package attribute is a reverse proxy.
|
||||
|
||||
It translates gRPC into RESTful JSON APIs.
|
||||
*/
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Suppress "imported and not used" errors
|
||||
var _ codes.Code
|
||||
var _ io.Reader
|
||||
var _ status.Status
|
||||
var _ = runtime.String
|
||||
var _ = utilities.NewDoubleArray
|
||||
var _ = metadata.Join
|
||||
|
||||
func request_TestResourceService_TestListResource_0(ctx context.Context, marshaler runtime.Marshaler, client TestResourceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TestListResourceRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := client.TestListResource(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_TestResourceService_TestListResource_0(ctx context.Context, marshaler runtime.Marshaler, server TestResourceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TestListResourceRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := server.TestListResource(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_TestResourceService_TestCreateResource_0(ctx context.Context, marshaler runtime.Marshaler, client TestResourceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TestCreateResourceRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.TestCreateResource(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_TestResourceService_TestCreateResource_0(ctx context.Context, marshaler runtime.Marshaler, server TestResourceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TestCreateResourceRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.TestCreateResource(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
filter_TestResourceService_TestUpdateResource_0 = &utilities.DoubleArray{Encoding: map[string]int{"item": 0, "id": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}}
|
||||
)
|
||||
|
||||
func request_TestResourceService_TestUpdateResource_0(ctx context.Context, marshaler runtime.Marshaler, client TestResourceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TestUpdateResourceRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 {
|
||||
if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Item); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
} else {
|
||||
protoReq.UpdateMask = fieldMask
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["id"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
|
||||
}
|
||||
|
||||
protoReq.Id, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
|
||||
}
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TestResourceService_TestUpdateResource_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.TestUpdateResource(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_TestResourceService_TestUpdateResource_0(ctx context.Context, marshaler runtime.Marshaler, server TestResourceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TestUpdateResourceRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 {
|
||||
if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Item); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
} else {
|
||||
protoReq.UpdateMask = fieldMask
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["id"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
|
||||
}
|
||||
|
||||
protoReq.Id, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
|
||||
}
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_TestResourceService_TestUpdateResource_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.TestUpdateResource(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_TestResourceService_TestGetResource_0(ctx context.Context, marshaler runtime.Marshaler, client TestResourceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TestGetResourceRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["id"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
|
||||
}
|
||||
|
||||
protoReq.Id, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
|
||||
}
|
||||
|
||||
msg, err := client.TestGetResource(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_TestResourceService_TestGetResource_0(ctx context.Context, marshaler runtime.Marshaler, server TestResourceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TestGetResourceRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["id"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
|
||||
}
|
||||
|
||||
protoReq.Id, err = runtime.String(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
|
||||
}
|
||||
|
||||
msg, err := server.TestGetResource(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterTestResourceServiceHandlerServer registers the http handlers for service TestResourceService to "mux".
|
||||
// UnaryRPC :call TestResourceServiceServer directly.
|
||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterTestResourceServiceHandlerFromEndpoint instead.
|
||||
func RegisterTestResourceServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server TestResourceServiceServer) error {
|
||||
|
||||
mux.Handle("GET", pattern_TestResourceService_TestListResource_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/testing.attribute.v1.TestResourceService/TestListResource", runtime.WithHTTPPathPattern("/v1/test-resources"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_TestResourceService_TestListResource_0(ctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_TestResourceService_TestListResource_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_TestResourceService_TestCreateResource_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/testing.attribute.v1.TestResourceService/TestCreateResource", runtime.WithHTTPPathPattern("/v1/test-resources"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_TestResourceService_TestCreateResource_0(ctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_TestResourceService_TestCreateResource_0(ctx, mux, outboundMarshaler, w, req, response_TestResourceService_TestCreateResource_0{resp}, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("PATCH", pattern_TestResourceService_TestUpdateResource_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/testing.attribute.v1.TestResourceService/TestUpdateResource", runtime.WithHTTPPathPattern("/v1/test-resources/{id}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_TestResourceService_TestUpdateResource_0(ctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_TestResourceService_TestUpdateResource_0(ctx, mux, outboundMarshaler, w, req, response_TestResourceService_TestUpdateResource_0{resp}, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_TestResourceService_TestGetResource_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/testing.attribute.v1.TestResourceService/TestGetResource", runtime.WithHTTPPathPattern("/v1/test-resources/{id}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_TestResourceService_TestGetResource_0(ctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_TestResourceService_TestGetResource_0(ctx, mux, outboundMarshaler, w, req, response_TestResourceService_TestGetResource_0{resp}, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterTestResourceServiceHandlerFromEndpoint is same as RegisterTestResourceServiceHandler but
|
||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||
func RegisterTestResourceServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||
conn, err := grpc.Dial(endpoint, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
||||
}
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
|
||||
}
|
||||
}()
|
||||
}()
|
||||
|
||||
return RegisterTestResourceServiceHandler(ctx, mux, conn)
|
||||
}
|
||||
|
||||
// RegisterTestResourceServiceHandler registers the http handlers for service TestResourceService to "mux".
|
||||
// The handlers forward requests to the grpc endpoint over "conn".
|
||||
func RegisterTestResourceServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
||||
return RegisterTestResourceServiceHandlerClient(ctx, mux, NewTestResourceServiceClient(conn))
|
||||
}
|
||||
|
||||
// RegisterTestResourceServiceHandlerClient registers the http handlers for service TestResourceService
|
||||
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "TestResourceServiceClient".
|
||||
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "TestResourceServiceClient"
|
||||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
||||
// "TestResourceServiceClient" to call the correct interceptors.
|
||||
func RegisterTestResourceServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client TestResourceServiceClient) error {
|
||||
|
||||
mux.Handle("GET", pattern_TestResourceService_TestListResource_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/testing.attribute.v1.TestResourceService/TestListResource", runtime.WithHTTPPathPattern("/v1/test-resources"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_TestResourceService_TestListResource_0(ctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_TestResourceService_TestListResource_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_TestResourceService_TestCreateResource_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/testing.attribute.v1.TestResourceService/TestCreateResource", runtime.WithHTTPPathPattern("/v1/test-resources"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_TestResourceService_TestCreateResource_0(ctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_TestResourceService_TestCreateResource_0(ctx, mux, outboundMarshaler, w, req, response_TestResourceService_TestCreateResource_0{resp}, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("PATCH", pattern_TestResourceService_TestUpdateResource_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/testing.attribute.v1.TestResourceService/TestUpdateResource", runtime.WithHTTPPathPattern("/v1/test-resources/{id}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_TestResourceService_TestUpdateResource_0(ctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_TestResourceService_TestUpdateResource_0(ctx, mux, outboundMarshaler, w, req, response_TestResourceService_TestUpdateResource_0{resp}, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_TestResourceService_TestGetResource_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
ctx, err = runtime.AnnotateContext(ctx, mux, req, "/testing.attribute.v1.TestResourceService/TestGetResource", runtime.WithHTTPPathPattern("/v1/test-resources/{id}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_TestResourceService_TestGetResource_0(ctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_TestResourceService_TestGetResource_0(ctx, mux, outboundMarshaler, w, req, response_TestResourceService_TestGetResource_0{resp}, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type response_TestResourceService_TestCreateResource_0 struct {
|
||||
proto.Message
|
||||
}
|
||||
|
||||
func (m response_TestResourceService_TestCreateResource_0) XXX_ResponseBody() interface{} {
|
||||
response := m.Message.(*TestCreateResourceResponse)
|
||||
return response.Item
|
||||
}
|
||||
|
||||
type response_TestResourceService_TestUpdateResource_0 struct {
|
||||
proto.Message
|
||||
}
|
||||
|
||||
func (m response_TestResourceService_TestUpdateResource_0) XXX_ResponseBody() interface{} {
|
||||
response := m.Message.(*TestUpdateResourceResponse)
|
||||
return response.Item
|
||||
}
|
||||
|
||||
type response_TestResourceService_TestGetResource_0 struct {
|
||||
proto.Message
|
||||
}
|
||||
|
||||
func (m response_TestResourceService_TestGetResource_0) XXX_ResponseBody() interface{} {
|
||||
response := m.Message.(*TestGetResourceResponse)
|
||||
return response.Item
|
||||
}
|
||||
|
||||
var (
|
||||
pattern_TestResourceService_TestListResource_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "test-resources"}, ""))
|
||||
|
||||
pattern_TestResourceService_TestCreateResource_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "test-resources"}, ""))
|
||||
|
||||
pattern_TestResourceService_TestUpdateResource_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "test-resources", "id"}, ""))
|
||||
|
||||
pattern_TestResourceService_TestGetResource_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "test-resources", "id"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
forward_TestResourceService_TestListResource_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_TestResourceService_TestCreateResource_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_TestResourceService_TestUpdateResource_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_TestResourceService_TestGetResource_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
@ -0,0 +1,209 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package attribute
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// TestResourceServiceClient is the client API for TestResourceService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type TestResourceServiceClient interface {
|
||||
TestListResource(ctx context.Context, in *TestListResourceRequest, opts ...grpc.CallOption) (*TestListResourceResponse, error)
|
||||
TestCreateResource(ctx context.Context, in *TestCreateResourceRequest, opts ...grpc.CallOption) (*TestCreateResourceResponse, error)
|
||||
TestUpdateResource(ctx context.Context, in *TestUpdateResourceRequest, opts ...grpc.CallOption) (*TestUpdateResourceResponse, error)
|
||||
TestGetResource(ctx context.Context, in *TestGetResourceRequest, opts ...grpc.CallOption) (*TestGetResourceResponse, error)
|
||||
}
|
||||
|
||||
type testResourceServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewTestResourceServiceClient(cc grpc.ClientConnInterface) TestResourceServiceClient {
|
||||
return &testResourceServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *testResourceServiceClient) TestListResource(ctx context.Context, in *TestListResourceRequest, opts ...grpc.CallOption) (*TestListResourceResponse, error) {
|
||||
out := new(TestListResourceResponse)
|
||||
err := c.cc.Invoke(ctx, "/testing.attribute.v1.TestResourceService/TestListResource", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *testResourceServiceClient) TestCreateResource(ctx context.Context, in *TestCreateResourceRequest, opts ...grpc.CallOption) (*TestCreateResourceResponse, error) {
|
||||
out := new(TestCreateResourceResponse)
|
||||
err := c.cc.Invoke(ctx, "/testing.attribute.v1.TestResourceService/TestCreateResource", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *testResourceServiceClient) TestUpdateResource(ctx context.Context, in *TestUpdateResourceRequest, opts ...grpc.CallOption) (*TestUpdateResourceResponse, error) {
|
||||
out := new(TestUpdateResourceResponse)
|
||||
err := c.cc.Invoke(ctx, "/testing.attribute.v1.TestResourceService/TestUpdateResource", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *testResourceServiceClient) TestGetResource(ctx context.Context, in *TestGetResourceRequest, opts ...grpc.CallOption) (*TestGetResourceResponse, error) {
|
||||
out := new(TestGetResourceResponse)
|
||||
err := c.cc.Invoke(ctx, "/testing.attribute.v1.TestResourceService/TestGetResource", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// TestResourceServiceServer is the server API for TestResourceService service.
|
||||
// All implementations must embed UnimplementedTestResourceServiceServer
|
||||
// for forward compatibility
|
||||
type TestResourceServiceServer interface {
|
||||
TestListResource(context.Context, *TestListResourceRequest) (*TestListResourceResponse, error)
|
||||
TestCreateResource(context.Context, *TestCreateResourceRequest) (*TestCreateResourceResponse, error)
|
||||
TestUpdateResource(context.Context, *TestUpdateResourceRequest) (*TestUpdateResourceResponse, error)
|
||||
TestGetResource(context.Context, *TestGetResourceRequest) (*TestGetResourceResponse, error)
|
||||
mustEmbedUnimplementedTestResourceServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedTestResourceServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedTestResourceServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedTestResourceServiceServer) TestListResource(context.Context, *TestListResourceRequest) (*TestListResourceResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method TestListResource not implemented")
|
||||
}
|
||||
func (UnimplementedTestResourceServiceServer) TestCreateResource(context.Context, *TestCreateResourceRequest) (*TestCreateResourceResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method TestCreateResource not implemented")
|
||||
}
|
||||
func (UnimplementedTestResourceServiceServer) TestUpdateResource(context.Context, *TestUpdateResourceRequest) (*TestUpdateResourceResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method TestUpdateResource not implemented")
|
||||
}
|
||||
func (UnimplementedTestResourceServiceServer) TestGetResource(context.Context, *TestGetResourceRequest) (*TestGetResourceResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method TestGetResource not implemented")
|
||||
}
|
||||
func (UnimplementedTestResourceServiceServer) mustEmbedUnimplementedTestResourceServiceServer() {}
|
||||
|
||||
// UnsafeTestResourceServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to TestResourceServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeTestResourceServiceServer interface {
|
||||
mustEmbedUnimplementedTestResourceServiceServer()
|
||||
}
|
||||
|
||||
func RegisterTestResourceServiceServer(s grpc.ServiceRegistrar, srv TestResourceServiceServer) {
|
||||
s.RegisterService(&TestResourceService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _TestResourceService_TestListResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TestListResourceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TestResourceServiceServer).TestListResource(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/testing.attribute.v1.TestResourceService/TestListResource",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TestResourceServiceServer).TestListResource(ctx, req.(*TestListResourceRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TestResourceService_TestCreateResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TestCreateResourceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TestResourceServiceServer).TestCreateResource(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/testing.attribute.v1.TestResourceService/TestCreateResource",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TestResourceServiceServer).TestCreateResource(ctx, req.(*TestCreateResourceRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TestResourceService_TestUpdateResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TestUpdateResourceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TestResourceServiceServer).TestUpdateResource(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/testing.attribute.v1.TestResourceService/TestUpdateResource",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TestResourceServiceServer).TestUpdateResource(ctx, req.(*TestUpdateResourceRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _TestResourceService_TestGetResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TestGetResourceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TestResourceServiceServer).TestGetResource(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/testing.attribute.v1.TestResourceService/TestGetResource",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TestResourceServiceServer).TestGetResource(ctx, req.(*TestGetResourceRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// TestResourceService_ServiceDesc is the grpc.ServiceDesc for TestResourceService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var TestResourceService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "testing.attribute.v1.TestResourceService",
|
||||
HandlerType: (*TestResourceServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "TestListResource",
|
||||
Handler: _TestResourceService_TestListResource_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "TestCreateResource",
|
||||
Handler: _TestResourceService_TestCreateResource_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "TestUpdateResource",
|
||||
Handler: _TestResourceService_TestUpdateResource_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "TestGetResource",
|
||||
Handler: _TestResourceService_TestGetResource_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "testing/attribute/v1/attribute.proto",
|
||||
}
|
||||
@ -0,0 +1,132 @@
|
||||
package subtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
func convertAttributesToSubtype(msg proto.Message, st Subtype) error {
|
||||
r := msg.ProtoReflect()
|
||||
d := r.Descriptor()
|
||||
|
||||
defaultAttrField, err := attributeField(d, defaultSubtype)
|
||||
if err != nil {
|
||||
// If unable to get a default attribute field, the message is either
|
||||
// not registered or not in the format needed for conversion, so no
|
||||
// conversion should be performed. The most likely case here is that
|
||||
// the message has not been changed to use a oneof for attributes yet.
|
||||
return nil
|
||||
}
|
||||
|
||||
stAttrField, err := attributeField(d, st)
|
||||
if err != nil {
|
||||
// This error should not be possible, since any registration issue
|
||||
// would trigger the previous call, and if this particular subtype
|
||||
// is unknown, the default should be returned.
|
||||
return nil
|
||||
}
|
||||
|
||||
if defaultAttrField == stAttrField {
|
||||
// no need to convert
|
||||
return nil
|
||||
}
|
||||
|
||||
defaultAttrs, ok := r.Get(defaultAttrField).Message().Interface().(*structpb.Struct)
|
||||
if !ok {
|
||||
// This should not be possible since this is checked in
|
||||
// (attributeRegistry).register at initialization time and would panic
|
||||
// if this was the case.
|
||||
return fmt.Errorf("found default attribute field that is not structpb.Struct: %s %s", d.FullName(), defaultAttrField.FullName())
|
||||
}
|
||||
stAttrs := r.Get(stAttrField).Message().New().Interface()
|
||||
if err := structToProto(defaultAttrs, stAttrs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// implicitly clears any previously set oneof value
|
||||
r.Set(stAttrField, protoreflect.ValueOfMessage(stAttrs.ProtoReflect()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertAttributesToDefault(msg proto.Message, st Subtype) error {
|
||||
r := msg.ProtoReflect()
|
||||
d := r.Descriptor()
|
||||
|
||||
defaultAttrField, err := attributeField(d, defaultSubtype)
|
||||
if err != nil {
|
||||
// If unable to get a default attribute field, the message is either
|
||||
// not registered or not in the format needed for conversion, so no
|
||||
// conversion should be performed. The most likely case here is that
|
||||
// the message has not been changed to use a oneof for attributes yet.
|
||||
return nil
|
||||
}
|
||||
|
||||
stAttrField, err := attributeField(d, st)
|
||||
if err != nil {
|
||||
// This error should not be possible, since any registration issue
|
||||
// would trigger the previous call, and if this particular subtype
|
||||
// is unknown, the default should be returned.
|
||||
return nil
|
||||
}
|
||||
|
||||
if defaultAttrField == stAttrField {
|
||||
// no need to convert
|
||||
return nil
|
||||
}
|
||||
|
||||
stAttrs, ok := r.Get(stAttrField).Message().Interface().(proto.Message)
|
||||
if !ok {
|
||||
return fmt.Errorf("found subtype attribute field that is not proto.Message: %s %s", d.FullName(), stAttrField.FullName())
|
||||
}
|
||||
defaultAttrs, err := protoToStruct(stAttrs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// implicitly clears any previously set oneof value
|
||||
r.Set(defaultAttrField, protoreflect.ValueOfMessage(defaultAttrs.ProtoReflect()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func structToProto(fields *structpb.Struct, p proto.Message) error {
|
||||
if fields == nil {
|
||||
// If there is no struct, don't update the default proto message.
|
||||
return nil
|
||||
}
|
||||
js, err := fields.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: replicate this logic but with a proto extension set on the Field
|
||||
// descriptor There are some attributes where we want to discard unknown
|
||||
// fields, while others that should error if there are unknown fields
|
||||
//
|
||||
// opts := GetOpts(opt...)
|
||||
// if opts.withDiscardUnknownFields {
|
||||
// err = (protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(js, p))
|
||||
// } else {
|
||||
// err = protojson.Unmarshal(js, p)
|
||||
// }
|
||||
|
||||
err = protojson.Unmarshal(js, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func protoToStruct(p proto.Message) (*structpb.Struct, error) {
|
||||
js, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st := &structpb.Struct{}
|
||||
if err := protojson.Unmarshal(js, st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
@ -0,0 +1,308 @@
|
||||
package subtypes
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/protooptions"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
)
|
||||
|
||||
func messageDomain(m proto.Message) string {
|
||||
r := m.ProtoReflect()
|
||||
fd := r.Descriptor().ParentFile()
|
||||
if fd == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
opts, ok := fd.Options().(*descriptorpb.FileOptions)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
domain := proto.GetExtension(opts, protooptions.E_Domain).(string)
|
||||
return domain
|
||||
}
|
||||
|
||||
// transformRequestAttributes will modify the request proto.Message, setting
|
||||
// any subtype attribute fields into the corresponding strongly-typed struct
|
||||
// for the subtype. It looks for some specific structure in the proto.Message
|
||||
// to identify the correct subtype and apply the transformation.
|
||||
//
|
||||
// The first structure is a message that contains a single "item" that is a
|
||||
// message that has a "type" and an "attrs" oneof for attributes:
|
||||
//
|
||||
// message CreateFooRequest {
|
||||
// item Foo = 1;
|
||||
// }
|
||||
// message Foo {
|
||||
// string type = 1;
|
||||
// // other fields
|
||||
// oneof attrs {
|
||||
// google.protobuf.Struct attributes = 10 [(custom_options.v1.subtype) = "default"];
|
||||
// // other subtype attributes types
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The second structure is similar to the first, but rather then the type field
|
||||
// being provided, an id field is set. Note this is a different id from the
|
||||
// third structure. In this case it is an id for a related resource and the id
|
||||
// is marked with the "subtype_source_id" custom option:
|
||||
//
|
||||
// message CreateFooRequest {
|
||||
// item Foo = 1;
|
||||
// }
|
||||
// message Foo {
|
||||
// string bar_id = 1 [(custom_options.v1.subtype_source_id) = true];
|
||||
// // other fields
|
||||
// oneof attrs {
|
||||
// google.protobuf.Struct attributes = 10 [(custom_options.v1.subtype) = "default"];
|
||||
// // other subtype attributes types
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The third structure is a message that contains an id string and an "item"
|
||||
// that is a message that has an "attrs" oneof for attributes:
|
||||
//
|
||||
// message UpdateFooRequest {
|
||||
// string id = 1;
|
||||
// item Foo = 2;
|
||||
// }
|
||||
// message Foo {
|
||||
// // other fields
|
||||
// oneof attrs {
|
||||
// google.protobuf.Struct attributes = 10 [(custom_options.v1.subtype) = "default"];
|
||||
// // other subtype attributes types
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The forth structure is a message that contains an id string and an "attrs" oneof for
|
||||
// attributes:
|
||||
//
|
||||
// message FooActionRequest {
|
||||
// string id = 1;
|
||||
// oneof attrs {
|
||||
// google.protobuf.Struct attributes = 10 [(custom_options.v1.subtype) = "default"];
|
||||
// // other subtype attributes types
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Also note that for any of the id based lookups to function, the file that contains
|
||||
// the proto.Message definition must set the "domain" custom option.
|
||||
func transformRequestAttributes(req proto.Message) (proto.Message, error) {
|
||||
domain := messageDomain(req)
|
||||
|
||||
r := req.ProtoReflect()
|
||||
fields := r.Descriptor().Fields()
|
||||
|
||||
itemField := fields.ByName("item")
|
||||
idField := fields.ByName("id")
|
||||
attributesField := fields.ByName("attributes")
|
||||
|
||||
fieldValue := func(m protoreflect.Message, fd protoreflect.FieldDescriptor) string {
|
||||
if fd == nil {
|
||||
return ""
|
||||
}
|
||||
return m.Get(fd).String()
|
||||
}
|
||||
|
||||
var st Subtype
|
||||
switch {
|
||||
case itemField != nil:
|
||||
itemR := itemField.Message()
|
||||
if itemR == nil {
|
||||
return req, nil
|
||||
}
|
||||
id := fieldValue(r, idField)
|
||||
|
||||
item := r.Get(itemField).Message().Interface()
|
||||
itemFields := itemR.Fields()
|
||||
|
||||
typeField := itemFields.ByName("type")
|
||||
t := fieldValue(item.ProtoReflect(), typeField)
|
||||
|
||||
sourceIdField := sourceIdFieldDescriptor(item.ProtoReflect().Descriptor())
|
||||
sourceId := fieldValue(item.ProtoReflect(), sourceIdField)
|
||||
|
||||
switch {
|
||||
case idField != nil && id != "":
|
||||
st = SubtypeFromId(domain, id)
|
||||
case sourceIdField != nil && sourceId != "":
|
||||
st = SubtypeFromId(domain, sourceId)
|
||||
case typeField != nil && t != "":
|
||||
st = Subtype(t)
|
||||
default: // need either type or id
|
||||
return req, nil
|
||||
}
|
||||
if err := convertAttributesToSubtype(item, st); err != nil {
|
||||
return req, err
|
||||
}
|
||||
case idField != nil && attributesField != nil:
|
||||
id := r.Get(idField).String()
|
||||
st = SubtypeFromId(domain, id)
|
||||
if err := convertAttributesToSubtype(req, st); err != nil {
|
||||
return req, err
|
||||
}
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func transformResponseItemAttributes(item proto.Message) error {
|
||||
r := item.ProtoReflect()
|
||||
desc := r.Descriptor()
|
||||
|
||||
typeField := desc.Fields().ByName("type")
|
||||
if typeField == nil {
|
||||
// not an item with subtypes
|
||||
return nil
|
||||
}
|
||||
|
||||
attrsField := desc.Oneofs().ByName("attrs")
|
||||
if attrsField == nil {
|
||||
// not an item with attrs oneof
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.WhichOneof(attrsField) == nil {
|
||||
// attrs field is not set, nothing to do
|
||||
return nil
|
||||
}
|
||||
|
||||
st := Subtype(item.ProtoReflect().Get(typeField).String())
|
||||
return convertAttributesToDefault(item, st)
|
||||
}
|
||||
|
||||
// transformResponseAttributes will modify the response proto.Message, setting
|
||||
// any subtype attribute fields into the default structpb.Struct field. It looks
|
||||
// for some specific structure in the proto.Message to identify that it is a
|
||||
// message that needs transformation.
|
||||
//
|
||||
// The first structure is a message that contains a single "item" that is a
|
||||
// message that has an "attrs" oneof for attributes:
|
||||
//
|
||||
// message CreateFooResponse {
|
||||
// item Foo = 1;
|
||||
// }
|
||||
// message Foo {
|
||||
// oneof attrs {
|
||||
// google.protobuf.Struct attributes = 10 [(custom_options.v1.subtype) = "default"];
|
||||
// // other subtype attributes types
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The second structure is a message that contains a single "items" that is a
|
||||
// slice of item messages that have an "attrs" oneof for attributes:
|
||||
//
|
||||
// message ListFooResponse {
|
||||
// items []Foo = 1;
|
||||
// }
|
||||
// message Foo {
|
||||
// oneof attrs {
|
||||
// google.protobuf.Struct attributes = 10 [(custom_options.v1.subtype) = "default"];
|
||||
// // other subtype attributes types
|
||||
// }
|
||||
// }
|
||||
func transformResponseAttributes(res proto.Message) (proto.Message, error) {
|
||||
r := res.ProtoReflect()
|
||||
fields := r.Descriptor().Fields()
|
||||
|
||||
itemField := fields.ByName("item")
|
||||
itemsField := fields.ByName("items")
|
||||
switch {
|
||||
case itemField != nil:
|
||||
if itemR := itemField.Message(); itemR == nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
item := r.Get(itemField).Message().Interface()
|
||||
if err := transformResponseItemAttributes(item); err != nil {
|
||||
return res, err
|
||||
}
|
||||
case itemsField != nil:
|
||||
if !itemsField.IsList() {
|
||||
return res, nil
|
||||
}
|
||||
items := r.Get(itemsField).List()
|
||||
|
||||
for i := 0; i < items.Len(); i++ {
|
||||
item := items.Get(i).Message().Interface()
|
||||
if err := transformResponseItemAttributes(item); err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// AttributeTransformerInterceptor is a grpc server interceptor that will
|
||||
// transform subtype attributes for requests and responses. This will only
|
||||
// modify requests and responses that adhere to a specific structure and is done
|
||||
// to support the use of a oneof for attributes to strongly type the attributes
|
||||
// while allowing the JSON API to provide attributes via a single key.
|
||||
//
|
||||
// For example with a protobuf message definition like:
|
||||
//
|
||||
// message Account {
|
||||
// string id = 1;
|
||||
// string type = 2;
|
||||
// oneof attrs {
|
||||
// google.protobuf.Struct attributes = 10 [(controller.custom_options.v1.subtype) = "default"];
|
||||
// PasswordAttributes password_attributes = 20 [(controller.custom_options.v1.subtype) = "password"];
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// message PasswordAttributes {
|
||||
// string login_name = 1;
|
||||
// }
|
||||
//
|
||||
// message AccountCreateRequest {
|
||||
// Account item = 1;
|
||||
// }
|
||||
//
|
||||
// message AccountCreateResponse {
|
||||
// Account item = 1;
|
||||
// }
|
||||
//
|
||||
// And a create request with JSON request body like:
|
||||
// {
|
||||
// "type": "password",
|
||||
// "attributes": {
|
||||
// "login_name": "tim"
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Will result in a proto request like:
|
||||
//
|
||||
// type:"password" attributes:{fields:{key:"login_name" value:{string_value:"tim"}}}
|
||||
//
|
||||
// This request will be transformed into:
|
||||
//
|
||||
// type:"password" password_attributes:{login_name:"tim"}
|
||||
func AttributeTransformerInterceptor(_ context.Context) grpc.UnaryServerInterceptor {
|
||||
const op = "subtypes.AttributeTransformInterceptor"
|
||||
return func(interceptorCtx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
var err error
|
||||
if reqMsg, ok := req.(proto.Message); ok {
|
||||
req, err = transformRequestAttributes(reqMsg)
|
||||
if err != nil {
|
||||
return nil, handlers.InvalidArgumentErrorf("Error in provided request.",
|
||||
map[string]string{"attributes": "Attribute fields do not match the expected format."})
|
||||
}
|
||||
}
|
||||
|
||||
res, handlerErr := handler(interceptorCtx, req)
|
||||
newRes := res
|
||||
|
||||
if res, ok := res.(proto.Message); ok {
|
||||
newRes, err = transformResponseAttributes(res)
|
||||
if err != nil {
|
||||
return nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "failed building attribute struct: %v", err)
|
||||
}
|
||||
}
|
||||
return newRes, handlerErr
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,355 @@
|
||||
package subtypes_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/internal/gen/testing/attribute"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
|
||||
"github.com/hashicorp/boundary/internal/types/subtypes"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
func TestAttributeTransformerInterceptor(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
req interface{}
|
||||
handlerResp interface{}
|
||||
expectHandlerReq interface{}
|
||||
excpetResp interface{}
|
||||
}{
|
||||
{
|
||||
"TestCreateResource/SubResourceRequest",
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestCreateResource/SubResourceRequest/OtherId",
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
OtherId: "trsr_parent",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
OtherId: "trsr_parent",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
OtherId: "trsr_parent",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
OtherId: "trsr_parent",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestUpdateResource/SubResourceRequest",
|
||||
&attribute.TestUpdateResourceRequest{
|
||||
Id: "trsr_one",
|
||||
Item: &attribute.TestResource{
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateResourceRequest{
|
||||
Id: "trsr_one",
|
||||
Item: &attribute.TestResource{
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestNoItemAttributes/SubResourceRequest",
|
||||
&attribute.TestNoItemAttributes{
|
||||
Id: "trsr_one",
|
||||
Attrs: &attribute.TestNoItemAttributes_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
&attribute.TestNoItemAttributes{
|
||||
Id: "trsr_one",
|
||||
Attrs: &attribute.TestNoItemAttributes_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
&attribute.TestNoItemAttributes{
|
||||
Id: "trsr_one",
|
||||
Attrs: &attribute.TestNoItemAttributes_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestNoItemAttributes{
|
||||
Id: "trsr_one",
|
||||
Attrs: &attribute.TestNoItemAttributes_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestListResourceResponse",
|
||||
nil,
|
||||
&attribute.TestListResourceResponse{
|
||||
Items: []*attribute.TestResource{
|
||||
{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "default",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "unknown",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
&attribute.TestListResourceResponse{
|
||||
Items: []*attribute.TestResource{
|
||||
{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "default",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "unknown",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
require.Empty(t, cmp.Diff(req, tc.expectHandlerReq, protocmp.Transform()))
|
||||
return tc.handlerResp, nil
|
||||
}
|
||||
|
||||
got, err := subtypes.AttributeTransformerInterceptor(ctx)(ctx, tc.req, nil, handler)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, cmp.Diff(got, tc.excpetResp, protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttributeTransformerInterceptorRequestErrors(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
req interface{}
|
||||
want error
|
||||
}{
|
||||
{
|
||||
"InvalidAttributes",
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"foo": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
handlers.InvalidArgumentErrorf("Error in provided request.",
|
||||
map[string]string{"attributes": "Attribute fields do not match the expected format."}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
t.Fatalf("handler should not be called")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
_, err := subtypes.AttributeTransformerInterceptor(ctx)(ctx, tc.req, nil, handler)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,590 @@
|
||||
package subtypes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/boundary/internal/gen/testing/attribute"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register("test", Subtype("sub_resource"), "trsr")
|
||||
Register("test", Subtype("resource_plugin"), "trrp")
|
||||
}
|
||||
|
||||
func TestTransformRequestAttributes(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
req proto.Message
|
||||
expected proto.Message
|
||||
}{
|
||||
{
|
||||
"TestCreateResource/SubResourceRequest",
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestCreateResource/DefaultResourceRequest",
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "default",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "default",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestCreateResource/UnknownResourceRequest",
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "unknown",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceRequest{
|
||||
Item: &attribute.TestResource{
|
||||
Type: "unknown",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestUpdateResource/SubResourceRequest",
|
||||
&attribute.TestUpdateResourceRequest{
|
||||
Id: "trsr_one",
|
||||
Item: &attribute.TestResource{
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateResourceRequest{
|
||||
Id: "trsr_one",
|
||||
Item: &attribute.TestResource{
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestUpdateResource/PluginResourceRequest",
|
||||
&attribute.TestUpdateResourceRequest{
|
||||
Id: "trdp_one",
|
||||
Item: &attribute.TestResource{
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateResourceRequest{
|
||||
Id: "trdp_one",
|
||||
Item: &attribute.TestResource{
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestUpdateResource/UnknownResourceRequest",
|
||||
&attribute.TestUpdateResourceRequest{
|
||||
Id: "unknown",
|
||||
Item: &attribute.TestResource{
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateResourceRequest{
|
||||
Id: "unknown",
|
||||
Item: &attribute.TestResource{
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestRequestNoItem",
|
||||
&attribute.TestRequestNoItem{
|
||||
Name: "test",
|
||||
},
|
||||
&attribute.TestRequestNoItem{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestRequestItemNotMessage",
|
||||
&attribute.TestRequestItemNotMessage{
|
||||
Item: "test",
|
||||
},
|
||||
&attribute.TestRequestItemNotMessage{
|
||||
Item: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestRequestItemNoType",
|
||||
&attribute.TestRequestItemNoType{
|
||||
Item: &attribute.TestItemNoType{
|
||||
Id: "test",
|
||||
},
|
||||
},
|
||||
&attribute.TestRequestItemNoType{
|
||||
Item: &attribute.TestItemNoType{
|
||||
Id: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestNoItemAttributes/SubResourceRequest",
|
||||
&attribute.TestNoItemAttributes{
|
||||
Id: "trsr_one",
|
||||
Attrs: &attribute.TestNoItemAttributes_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
&attribute.TestNoItemAttributes{
|
||||
Id: "trsr_one",
|
||||
Attrs: &attribute.TestNoItemAttributes_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestCreateNoOneOfRequest",
|
||||
&attribute.TestCreateNoOneOfRequest{
|
||||
Item: &attribute.TestNoOneOf{
|
||||
Type: "sub_resource",
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateNoOneOfRequest{
|
||||
Item: &attribute.TestNoOneOf{
|
||||
Type: "sub_resource",
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestUpdateNoOneOfRequest",
|
||||
&attribute.TestUpdateNoOneOfRequest{
|
||||
Id: "trsr_one",
|
||||
Item: &attribute.TestNoOneOf{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateNoOneOfRequest{
|
||||
Id: "trsr_one",
|
||||
Item: &attribute.TestNoOneOf{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := transformRequestAttributes(tc.req)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, cmp.Diff(got, tc.expected, protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransformResponseAttributes(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
resp proto.Message
|
||||
expected proto.Message
|
||||
}{
|
||||
{
|
||||
"TestListResourceResponse",
|
||||
&attribute.TestListResourceResponse{
|
||||
Items: []*attribute.TestResource{
|
||||
{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "default",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestListResourceResponse{
|
||||
Items: []*attribute.TestResource{
|
||||
{
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "default",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestGetResourceResponse/SubResourceAttributes",
|
||||
&attribute.TestGetResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestGetResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestGetResourceResponse/PluginAttributes",
|
||||
&attribute.TestGetResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trrp_one",
|
||||
Type: "plugin",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestGetResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trrp_one",
|
||||
Type: "plugin",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestCreateResourceResponse",
|
||||
&attribute.TestCreateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestUpdateResourceResponse",
|
||||
&attribute.TestUpdateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_SubResourceAttributes{
|
||||
SubResourceAttributes: &attribute.TestSubResourceAttributes{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attrs: &attribute.TestResource_Attributes{
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestResponseNoItem",
|
||||
&attribute.TestResponseNoItem{
|
||||
Name: "test",
|
||||
},
|
||||
&attribute.TestResponseNoItem{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestResponseItemNotMessage",
|
||||
&attribute.TestResponseItemNotMessage{
|
||||
Item: "test",
|
||||
},
|
||||
&attribute.TestResponseItemNotMessage{
|
||||
Item: "test",
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestResponseItemNoType",
|
||||
&attribute.TestResponseItemNoType{
|
||||
Item: &attribute.TestItemNoType{
|
||||
Id: "test",
|
||||
},
|
||||
},
|
||||
&attribute.TestResponseItemNoType{
|
||||
Item: &attribute.TestItemNoType{
|
||||
Id: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestCreateNoOneOfResponse",
|
||||
&attribute.TestCreateNoOneOfResponse{
|
||||
Item: &attribute.TestNoOneOf{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateNoOneOfResponse{
|
||||
Item: &attribute.TestNoOneOf{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestUpdateNoOneOfResponse",
|
||||
&attribute.TestUpdateNoOneOfResponse{
|
||||
Item: &attribute.TestNoOneOf{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
&attribute.TestUpdateNoOneOfResponse{
|
||||
Item: &attribute.TestNoOneOf{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
Attributes: func() *structpb.Struct {
|
||||
attrs, _ := structpb.NewStruct(map[string]interface{}{
|
||||
"name": "test",
|
||||
})
|
||||
return attrs
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"TestCreateResourceOneofUnset",
|
||||
&attribute.TestCreateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
},
|
||||
},
|
||||
&attribute.TestCreateResourceResponse{
|
||||
Item: &attribute.TestResource{
|
||||
Id: "trsr_one",
|
||||
Type: "sub_resource",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := transformResponseAttributes(tc.resp)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, cmp.Diff(got, tc.expected, protocmp.Transform()))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package subtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/protooptions"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
)
|
||||
|
||||
func init() {
|
||||
globalSourceRegistry = sourceRegistry{
|
||||
m: make(map[protoreflect.FullName]protoreflect.FieldDescriptor),
|
||||
}
|
||||
|
||||
protoregistry.GlobalTypes.RangeMessages(func(m protoreflect.MessageType) bool {
|
||||
d := m.Descriptor()
|
||||
if err := globalSourceRegistry.register(d); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
var globalSourceRegistry sourceRegistry
|
||||
|
||||
// sourceRegistry is collection of proto messages with a corresponding
|
||||
// field descriptor that can be used to determine the Subtype for the message.
|
||||
type sourceRegistry struct {
|
||||
sync.RWMutex
|
||||
|
||||
m map[protoreflect.FullName]protoreflect.FieldDescriptor
|
||||
}
|
||||
|
||||
func (s sourceRegistry) register(d protoreflect.MessageDescriptor) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
fn := d.FullName()
|
||||
|
||||
if _, present := s.m[fn]; present {
|
||||
return fmt.Errorf("proto message %s already registered", fn)
|
||||
}
|
||||
|
||||
fields := d.Fields()
|
||||
for i := 0; i < fields.Len(); i++ {
|
||||
f := fields.Get(i)
|
||||
|
||||
opts := f.Options().(*descriptorpb.FieldOptions)
|
||||
isSourceId := proto.GetExtension(opts, protooptions.E_SubtypeSourceId).(bool)
|
||||
if isSourceId {
|
||||
s.m[fn] = f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s sourceRegistry) get(d protoreflect.MessageDescriptor) protoreflect.FieldDescriptor {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
return s.m[d.FullName()]
|
||||
}
|
||||
|
||||
// sourceIdFieldDescriptor is used by the AttributeTransformInterceptor to retrieve
|
||||
// a proto FieldDescriptor of an id field that can be used to determine
|
||||
// the given Message's subtype.
|
||||
func sourceIdFieldDescriptor(d protoreflect.MessageDescriptor) protoreflect.FieldDescriptor {
|
||||
return globalSourceRegistry.get(d)
|
||||
}
|
||||
Loading…
Reference in new issue