Skip to content

Commit

Permalink
adding parser
Browse files Browse the repository at this point in the history
the raw protobuf types aren't enough to help with
things like getting the parent resource structs. Creating
yet another intermediary struct to help facilitate it.

leveraging that functionality to construct paths for
resources with parents.
  • Loading branch information
toumorokoshi committed Sep 27, 2023
1 parent d9cd1c4 commit 1e51ae7
Show file tree
Hide file tree
Showing 9 changed files with 1,786 additions and 45 deletions.
12 changes: 8 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import (

"github.com/ghodss/yaml"

"github.com/aep-dev/aepc/reader"
"github.com/aep-dev/aepc/loader"
"github.com/aep-dev/aepc/parser"
"github.com/aep-dev/aepc/schema"
"github.com/aep-dev/aepc/validator"
"github.com/aep-dev/aepc/writer/proto"
Expand Down Expand Up @@ -55,8 +56,11 @@ func NewCommand() *cobra.Command {
if len(errors) > 0 {
log.Fatalf("error validating service: %v", errors)
}

proto, _ := proto.WriteServiceToProto(s)
ps, err := parser.NewParsedService(s)
if err != nil {
log.Fatal(err)
}
proto, _ := proto.WriteServiceToProto(ps)

err = writeFile(outputFile, proto)
if err != nil {
Expand All @@ -74,7 +78,7 @@ func NewCommand() *cobra.Command {
func unmarshal(ext string, b []byte, s *schema.Service) error {
switch ext {
case ".proto":
if err := reader.ReadServiceFromProto(b, s); err != nil {
if err := loader.ReadServiceFromProto(b, s); err != nil {
return fmt.Errorf("unable to decode proto %q: %w", string(b), err)
}
case ".yaml":
Expand Down
4 changes: 2 additions & 2 deletions examples/bookstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ name: bookstore.example.com
resources:
- kind: Book
parents:
- "bookstore.example.com/Publisher"
- kind: Publisher
- "bookstore.example.com/Publisher"
- kind: Publisher
18 changes: 17 additions & 1 deletion examples/bookstore.yaml.output.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ message ReadBookRequest {
string path = 1;
}

message DeleteBookRequest {
string path = 1;
}

message Publisher {
string path = 1;
}
Expand All @@ -32,13 +36,21 @@ message ReadPublisherRequest {
string path = 1;
}

message DeletePublisherRequest {
string path = 1;
}

service Bookstore {
rpc CreateBook ( CreateBookRequest ) returns ( Book ) {
option (google.api.http) = { post: "/book" };
}

rpc ReadBook ( ReadBookRequest ) returns ( Book ) {
option (google.api.http) = { get: "/{path=book/*}" };
option (google.api.http) = { get: "/{path=publisher/*/book/*}" };
}

rpc DeleteBook ( DeleteBookRequest ) returns ( DeleteBookRequest ) {
option (google.api.http) = { delete: "/{path=publisher/*/book/*}" };
}

rpc CreatePublisher ( CreatePublisherRequest ) returns ( Publisher ) {
Expand All @@ -48,4 +60,8 @@ service Bookstore {
rpc ReadPublisher ( ReadPublisherRequest ) returns ( Publisher ) {
option (google.api.http) = { get: "/{path=publisher/*}" };
}

rpc DeletePublisher ( DeletePublisherRequest ) returns ( DeletePublisherRequest ) {
option (google.api.http) = { delete: "/{path=publisher/*}" };
}
}
21 changes: 1 addition & 20 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,7 @@ require (
require gopkg.in/yaml.v2 v2.4.0 // indirect

require (
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/bufbuild/protocompile v0.6.0 // indirect
github.com/ghodss/yaml v1.0.0
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.12.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
golang.org/x/text v0.12.0
)
1,667 changes: 1,667 additions & 0 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion reader/proto.go → loader/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package reader
package loader

import (
"log"
Expand Down
57 changes: 57 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// package parser converts the schema
// into a full-fledged struct that provides
// more functionality for discovering resource references, etc.
package parser

import (
"fmt"

"github.com/aep-dev/aepc/schema"
)

// ParsedService wraps schema.Service, but includes
// helper functions for things like retrieving the resource
// definitions within a service.
type ParsedService struct {
*schema.Service
ResourceByType map[string]*ParsedResource
}

type ParsedResource struct {
*schema.Resource
Parents []*ParsedResource
}

func NewParsedService(s *schema.Service) (*ParsedService, error) {
resourceByType, err := loadResourceByType(s)
if err != nil {
return nil, fmt.Errorf("unable to build service %q: %w", s, err)
}
ps := ParsedService{
Service: s,
ResourceByType: resourceByType,
}
return &ps, nil
}

func loadResourceByType(s *schema.Service) (map[string]*ParsedResource, error) {
resourceByType := map[string]*ParsedResource{}
for _, r := range s.Resources {
name := fmt.Sprintf("%s/%s", s.Name, r.Kind)
resourceByType[name] = &ParsedResource{
Resource: r,
Parents: []*ParsedResource{},
}
}
// populate resource parents
for _, r := range resourceByType {
for _, p := range r.Resource.Parents {
parentResource, exists := resourceByType[p]
if !exists {
return nil, fmt.Errorf("parent %q for resource %q not found", p, r.Kind)
}
r.Parents = append(r.Parents, parentResource)
}
}
return resourceByType, nil
}
8 changes: 4 additions & 4 deletions writer/proto/proto.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"fmt"
"strings"

"github.com/aep-dev/aepc/schema"
"github.com/aep-dev/aepc/parser"
"github.com/jhump/protoreflect/desc/builder"
"github.com/jhump/protoreflect/desc/protoprint"
"golang.org/x/text/cases"
Expand All @@ -32,21 +32,21 @@ func init() {
capitalizer = cases.Title(language.AmericanEnglish)
}

func WriteServiceToProto(s *schema.Service) ([]byte, error) {
func WriteServiceToProto(ps *parser.ParsedService) ([]byte, error) {
fb := builder.NewFile("test.proto")
fb.IsProto3 = true
fb.SetComments(builder.Comments{
LeadingComment: "this file is autogenerated by aepc",
})
pServiceName := toProtoServiceName(s.Name)
pServiceName := toProtoServiceName(ps.Name)
serviceNameAsLower := fmt.Sprintf("/%s", strings.ToLower(pServiceName))
fo := &descriptorpb.FileOptions{
GoPackage: &serviceNameAsLower,
}
fb.SetOptions(fo)
sb := builder.NewService(pServiceName)
fb.AddService(sb)
for _, r := range s.Resources {
for _, r := range ps.ResourceByType {
err := AddResource(r, fb, sb)
if err != nil {
return []byte{}, fmt.Errorf("adding resource %v failed: %w", r.Kind, err)
Expand Down
42 changes: 29 additions & 13 deletions writer/proto/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ import (
"fmt"
"strings"

"github.com/aep-dev/aepc/schema"
"github.com/aep-dev/aepc/parser"
"github.com/jhump/protoreflect/desc/builder"
"google.golang.org/genproto/googleapis/api/annotations"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
)

// AddResource adds a resource's protos and RPCs to a file and service.
func AddResource(r *schema.Resource, fb *builder.FileBuilder, sb *builder.ServiceBuilder) error {
func AddResource(r *parser.ParsedResource, fb *builder.FileBuilder, sb *builder.ServiceBuilder) error {
resourceMb, err := GeneratedResourceMessage(r)
if err != nil {
return fmt.Errorf("unable to generated resource %v: %w", r.Kind, err)
Expand All @@ -39,15 +39,15 @@ func AddResource(r *schema.Resource, fb *builder.FileBuilder, sb *builder.Servic
if err != nil {
return err
}
// err = AddDelete(r, resourceMb, fb, sb)
// if err != nil {
// return err
// }
err = AddDelete(r, resourceMb, fb, sb)
if err != nil {
return err
}
return nil
}

// GenerateResourceMesssage adds the resource message.
func GeneratedResourceMessage(r *schema.Resource) (*builder.MessageBuilder, error) {
func GeneratedResourceMessage(r *parser.ParsedResource) (*builder.MessageBuilder, error) {
mb := builder.NewMessage(r.Kind)
mb.AddField(
builder.NewField(FIELD_NAME_PATH, builder.FieldTypeString()).SetNumber(1),
Expand All @@ -65,7 +65,7 @@ func GeneratedResourceMessage(r *schema.Resource) (*builder.MessageBuilder, erro
return mb, nil
}

func AddCreate(r *schema.Resource, resourceMb *builder.MessageBuilder, fb *builder.FileBuilder, sb *builder.ServiceBuilder) error {
func AddCreate(r *parser.ParsedResource, resourceMb *builder.MessageBuilder, fb *builder.FileBuilder, sb *builder.ServiceBuilder) error {
// add the resource message
// create request messages
mb := builder.NewMessage("Create" + r.Kind + "Request")
Expand All @@ -87,7 +87,7 @@ func AddCreate(r *schema.Resource, resourceMb *builder.MessageBuilder, fb *build
return nil
}

func AddRead(r *schema.Resource, resourceMb *builder.MessageBuilder, fb *builder.FileBuilder, sb *builder.ServiceBuilder) error {
func AddRead(r *parser.ParsedResource, resourceMb *builder.MessageBuilder, fb *builder.FileBuilder, sb *builder.ServiceBuilder) error {
// add the resource message
// create request messages
mb := builder.NewMessage("Read" + r.Kind + "Request")
Expand All @@ -102,15 +102,16 @@ func AddRead(r *schema.Resource, resourceMb *builder.MessageBuilder, fb *builder
options := &descriptorpb.MethodOptions{}
proto.SetExtension(options, annotations.E_Http, &annotations.HttpRule{
Pattern: &annotations.HttpRule_Get{
Get: fmt.Sprintf("/{path=%s/*}", strings.ToLower(r.Kind)),
// Get: fmt.Sprintf("/{path=%s/*}", strings.ToLower(r.Kind)),
Get: generateHTTPPath(r),
},
})
method.SetOptions(options)
sb.AddMethod(method)
return nil
}

func AddDelete(r *schema.Resource, resourceMb *builder.MessageBuilder, fb *builder.FileBuilder, sb *builder.ServiceBuilder) error {
func AddDelete(r *parser.ParsedResource, resourceMb *builder.MessageBuilder, fb *builder.FileBuilder, sb *builder.ServiceBuilder) error {
// add the resource message
// create request messages
mb := builder.NewMessage("Delete" + r.Kind + "Request")
Expand All @@ -120,15 +121,30 @@ func AddDelete(r *schema.Resource, resourceMb *builder.MessageBuilder, fb *build
fb.AddMessage(mb)
method := builder.NewMethod("Delete"+r.Kind,
builder.RpcTypeMessage(mb, false),
builder.RpcTypeMessage(nil, false),
// builder.RpcTypeImportedMessage(emptypb.File_google_protobuf_empty_proto, false),
)
options := &descriptorpb.MethodOptions{}
proto.SetExtension(options, annotations.E_Http, &annotations.HttpRule{
Pattern: &annotations.HttpRule_Delete{
Delete: fmt.Sprintf("/{path=%s/*}", strings.ToLower(r.Kind)),
Delete: generateHTTPPath(r),
},
})
method.SetOptions(options)
sb.AddMethod(method)
return nil
}

func generateHTTPPath(r *parser.ParsedResource) string {
elements := []string{strings.ToLower(r.Kind)}
if len(r.Parents) > 0 {
// TODO: handle multiple parents
p := r.Parents[0]
for p != nil {
elements = append([]string{strings.ToLower(p.Kind)}, elements...)
if len(p.Parents) == 0 {
break
}
}
}
return fmt.Sprintf("/{path=%v/*}", strings.Join(elements, "/*/"))
}

0 comments on commit 1e51ae7

Please sign in to comment.