From d50295d91a43bbdbd1e54c3c20664172a3bbba8c Mon Sep 17 00:00:00 2001 From: Dipjyoti Metia Date: Sun, 14 Apr 2024 00:58:21 +1000 Subject: [PATCH 1/4] initial improvements --- Makefile | 22 ++ go.mod | 4 +- go.sum | 8 +- google/api/annotations.proto | 31 +++ google/api/http.proto | 375 +++++++++++++++++++++++++++++++ google/api/httpbody.proto | 77 +++++++ google/type/date.proto | 52 +++++ google/type/datetime.proto | 104 +++++++++ google/type/money.proto | 42 ++++ google/type/phone_number.proto | 113 ++++++++++ google/type/postal_address.proto | 134 +++++++++++ pkg/commands/local.go | 2 + pkg/engine/files/files.go | 26 +-- pkg/engine/gemini/client.go | 1 + pkg/engine/gemini/model.go | 25 ++- pkg/engine/gemini/text.go | 17 ++ pkg/engine/utils/walker.go | 28 ++- 17 files changed, 1018 insertions(+), 43 deletions(-) create mode 100644 Makefile create mode 100644 google/api/annotations.proto create mode 100644 google/api/http.proto create mode 100644 google/api/httpbody.proto create mode 100644 google/type/date.proto create mode 100644 google/type/datetime.proto create mode 100644 google/type/money.proto create mode 100644 google/type/phone_number.proto create mode 100644 google/type/postal_address.proto diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b79a118 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +VERSION = 0.0.3 +LDFLAGS = -ldflags="-X 'github.com/dipjyotimetia/jarvis/cmd/cmd.version=$(VERSION)'" +OUTDIR = ./dist + +.PHONY: tidy +tidy: + go fmt ./... + go mod tidy -v + +.PHONY: build +build: + mkdir -p $(OUTDIR) + rm -rf $(OUTDIR)/* + go build -o $(OUTDIR) $(LDFLAGS) ./... + +.PHONY: run +run: build + ./dist/jarvis generate-scenarios --path="specs/openapi/v3.0/mini_blog.yaml" + +.PHONY: no-dirty +no-dirty: + git diff --exit-code \ No newline at end of file diff --git a/go.mod b/go.mod index 7429d58..c210fce 100644 --- a/go.mod +++ b/go.mod @@ -13,12 +13,12 @@ require ( github.com/go-git/go-git/v5 v5.12.0 github.com/google/generative-ai-go v0.10.0 github.com/google/go-github/v61 v61.0.0 + github.com/jhump/protoreflect v1.16.0 github.com/manifoldco/promptui v0.9.0 github.com/olekukonko/tablewriter v0.0.5 github.com/spf13/cobra v1.8.0 golang.org/x/oauth2 v0.19.0 google.golang.org/api v0.172.0 - google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -32,6 +32,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v1.0.0 // indirect + github.com/bufbuild/protocompile v0.10.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect @@ -80,6 +81,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240412170617-26222e5d3d56 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 // indirect google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 182a0bf..49e8035 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A= github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE= +github.com/bufbuild/protocompile v0.10.0 h1:+jW/wnLMLxaCEG8AX9lD0bQ5v9h1RUiMKOBOT5ll9dM= +github.com/bufbuild/protocompile v0.10.0/go.mod h1:G9qQIQo0xZ6Uyj6CMNz0saGmx2so+KONo8/KrELABiY= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -137,6 +139,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg= +github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -342,8 +346,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002 h1:V7Da7qt0MkY3noVANIMVBk28nOnijADeOR3i5Hcvpj4= +google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/google/api/annotations.proto b/google/api/annotations.proto new file mode 100644 index 0000000..85c361b --- /dev/null +++ b/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/google/api/http.proto b/google/api/http.proto new file mode 100644 index 0000000..69460cf --- /dev/null +++ b/google/api/http.proto @@ -0,0 +1,375 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/google/api/httpbody.proto b/google/api/httpbody.proto new file mode 100644 index 0000000..1a5bb78 --- /dev/null +++ b/google/api/httpbody.proto @@ -0,0 +1,77 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +syntax = "proto3"; + +package google.api; + +import "google/protobuf/any.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; +option java_multiple_files = true; +option java_outer_classname = "HttpBodyProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Message that represents an arbitrary HTTP body. It should only be used for +// payload formats that can't be represented as JSON, such as raw binary or +// an HTML page. +// +// +// This message can be used both in streaming and non-streaming API methods in +// the request as well as the response. +// +// It can be used as a top-level request field, which is convenient if one +// wants to extract parameters from either the URL or HTTP template into the +// request fields and also want access to the raw HTTP body. +// +// Example: +// +// message GetResourceRequest { +// // A unique request id. +// string request_id = 1; +// +// // The raw HTTP body is bound to this field. +// google.api.HttpBody http_body = 2; +// } +// +// service ResourceService { +// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); +// rpc UpdateResource(google.api.HttpBody) returns +// (google.protobuf.Empty); +// } +// +// Example with streaming methods: +// +// service CaldavService { +// rpc GetCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// rpc UpdateCalendar(stream google.api.HttpBody) +// returns (stream google.api.HttpBody); +// } +// +// Use of this type only changes how the request and response bodies are +// handled, all other features will continue to work unchanged. +message HttpBody { + // The HTTP Content-Type header value specifying the content type of the body. + string content_type = 1; + + // The HTTP request/response body as raw binary. + bytes data = 2; + + // Application specific response metadata. Must be set in the first response + // for streaming APIs. + repeated google.protobuf.Any extensions = 3; +} diff --git a/google/type/date.proto b/google/type/date.proto new file mode 100644 index 0000000..ded971b --- /dev/null +++ b/google/type/date.proto @@ -0,0 +1,52 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +syntax = "proto3"; + +package google.type; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/type/date;date"; +option java_multiple_files = true; +option java_outer_classname = "DateProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a whole or partial calendar date, such as a birthday. The time of +// day and time zone are either specified elsewhere or are insignificant. The +// date is relative to the Gregorian Calendar. This can represent one of the +// following: +// +// * A full date, with non-zero year, month, and day values +// * A month and day value, with a zero year, such as an anniversary +// * A year on its own, with zero month and day values +// * A year and month value, with a zero day, such as a credit card expiration +// date +// +// Related types are [google.type.TimeOfDay][google.type.TimeOfDay] and +// `google.protobuf.Timestamp`. +message Date { + // Year of the date. Must be from 1 to 9999, or 0 to specify a date without + // a year. + int32 year = 1; + + // Month of a year. Must be from 1 to 12, or 0 to specify a year without a + // month and day. + int32 month = 2; + + // Day of a month. Must be from 1 to 31 and valid for the year and month, or 0 + // to specify a year by itself or a year and month where the day isn't + // significant. + int32 day = 3; +} \ No newline at end of file diff --git a/google/type/datetime.proto b/google/type/datetime.proto new file mode 100644 index 0000000..86f98c9 --- /dev/null +++ b/google/type/datetime.proto @@ -0,0 +1,104 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +syntax = "proto3"; + +package google.type; + +import "google/protobuf/duration.proto"; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/type/datetime;datetime"; +option java_multiple_files = true; +option java_outer_classname = "DateTimeProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents civil time (or occasionally physical time). +// +// This type can represent a civil time in one of a few possible ways: +// +// * When utc_offset is set and time_zone is unset: a civil time on a calendar +// day with a particular offset from UTC. +// * When time_zone is set and utc_offset is unset: a civil time on a calendar +// day in a particular time zone. +// * When neither time_zone nor utc_offset is set: a civil time on a calendar +// day in local time. +// +// The date is relative to the Proleptic Gregorian Calendar. +// +// If year is 0, the DateTime is considered not to have a specific year. month +// and day must have valid, non-zero values. +// +// This type may also be used to represent a physical time if all the date and +// time fields are set and either case of the `time_offset` oneof is set. +// Consider using `Timestamp` message for physical time instead. If your use +// case also would like to store the user's timezone, that can be done in +// another field. +// +// This type is more flexible than some applications may want. Make sure to +// document and validate your application's limitations. +message DateTime { + // Optional. Year of date. Must be from 1 to 9999, or 0 if specifying a + // datetime without a year. + int32 year = 1; + + // Required. Month of year. Must be from 1 to 12. + int32 month = 2; + + // Required. Day of month. Must be from 1 to 31 and valid for the year and + // month. + int32 day = 3; + + // Required. Hours of day in 24 hour format. Should be from 0 to 23. An API + // may choose to allow the value "24:00:00" for scenarios like business + // closing time. + int32 hours = 4; + + // Required. Minutes of hour of day. Must be from 0 to 59. + int32 minutes = 5; + + // Required. Seconds of minutes of the time. Must normally be from 0 to 59. An + // API may allow the value 60 if it allows leap-seconds. + int32 seconds = 6; + + // Required. Fractions of seconds in nanoseconds. Must be from 0 to + // 999,999,999. + int32 nanos = 7; + + // Optional. Specifies either the UTC offset or the time zone of the DateTime. + // Choose carefully between them, considering that time zone data may change + // in the future (for example, a country modifies their DST start/end dates, + // and future DateTimes in the affected range had already been stored). + // If omitted, the DateTime is considered to be in local time. + oneof time_offset { + // UTC offset. Must be whole seconds, between -18 hours and +18 hours. + // For example, a UTC offset of -4:00 would be represented as + // { seconds: -14400 }. + google.protobuf.Duration utc_offset = 8; + + // Time zone. + TimeZone time_zone = 9; + } +} + +// Represents a time zone from the +// [IANA Time Zone Database](https://www.iana.org/time-zones). +message TimeZone { + // IANA Time Zone Database time zone, e.g. "America/New_York". + string id = 1; + + // Optional. IANA Time Zone Database version number, e.g. "2019a". + string version = 2; +} \ No newline at end of file diff --git a/google/type/money.proto b/google/type/money.proto new file mode 100644 index 0000000..56567f2 --- /dev/null +++ b/google/type/money.proto @@ -0,0 +1,42 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +syntax = "proto3"; + +package google.type; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/type/money;money"; +option java_multiple_files = true; +option java_outer_classname = "MoneyProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents an amount of money with its currency type. +message Money { +// The three-letter currency code defined in ISO 4217. +string currency_code = 1; + +// The whole units of the amount. +// For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar. +int64 units = 2; + +// Number of nano (10^-9) units of the amount. +// The value must be between -999,999,999 and +999,999,999 inclusive. +// If `units` is positive, `nanos` must be positive or zero. +// If `units` is zero, `nanos` can be positive, zero, or negative. +// If `units` is negative, `nanos` must be negative or zero. +// For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000. +int32 nanos = 3; +} \ No newline at end of file diff --git a/google/type/phone_number.proto b/google/type/phone_number.proto new file mode 100644 index 0000000..0aaecfe --- /dev/null +++ b/google/type/phone_number.proto @@ -0,0 +1,113 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +syntax = "proto3"; + +package google.type; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/type/phone_number;phone_number"; +option java_multiple_files = true; +option java_outer_classname = "PhoneNumberProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// An object representing a phone number, suitable as an API wire format. +// +// This representation: +// +// - should not be used for locale-specific formatting of a phone number, such +// as "+1 (650) 253-0000 ext. 123" +// +// - is not designed for efficient storage +// - may not be suitable for dialing - specialized libraries (see references) +// should be used to parse the number for that purpose +// +// To do something meaningful with this number, such as format it for various +// use-cases, convert it to an `i18n.phonenumbers.PhoneNumber` object first. +// +// For instance, in Java this would be: +// +// com.google.type.PhoneNumber wireProto = +// com.google.type.PhoneNumber.newBuilder().build(); +// com.google.i18n.phonenumbers.Phonenumber.PhoneNumber phoneNumber = +// PhoneNumberUtil.getInstance().parse(wireProto.getE164Number(), "ZZ"); +// if (!wireProto.getExtension().isEmpty()) { +// phoneNumber.setExtension(wireProto.getExtension()); +// } +// +// Reference(s): +// - https://github.com/google/libphonenumber +message PhoneNumber { + // An object representing a short code, which is a phone number that is + // typically much shorter than regular phone numbers and can be used to + // address messages in MMS and SMS systems, as well as for abbreviated dialing + // (e.g. "Text 611 to see how many minutes you have remaining on your plan."). + // + // Short codes are restricted to a region and are not internationally + // dialable, which means the same short code can exist in different regions, + // with different usage and pricing, even if those regions share the same + // country calling code (e.g. US and CA). + message ShortCode { + // Required. The BCP-47 region code of the location where calls to this + // short code can be made, such as "US" and "BB". + // + // Reference(s): + // - http://www.unicode.org/reports/tr35/#unicode_region_subtag + string region_code = 1; + + // Required. The short code digits, without a leading plus ('+') or country + // calling code, e.g. "611". + string number = 2; + } + + // Required. Either a regular number, or a short code. New fields may be + // added to the oneof below in the future, so clients should ignore phone + // numbers for which none of the fields they coded against are set. + oneof kind { + // The phone number, represented as a leading plus sign ('+'), followed by a + // phone number that uses a relaxed ITU E.164 format consisting of the + // country calling code (1 to 3 digits) and the subscriber number, with no + // additional spaces or formatting, e.g.: + // - correct: "+15552220123" + // - incorrect: "+1 (555) 222-01234 x123". + // + // The ITU E.164 format limits the latter to 12 digits, but in practice not + // all countries respect that, so we relax that restriction here. + // National-only numbers are not allowed. + // + // References: + // - https://www.itu.int/rec/T-REC-E.164-201011-I + // - https://en.wikipedia.org/wiki/E.164. + // - https://en.wikipedia.org/wiki/List_of_country_calling_codes + string e164_number = 1; + + // A short code. + // + // Reference(s): + // - https://en.wikipedia.org/wiki/Short_code + ShortCode short_code = 2; + } + + // The phone number's extension. The extension is not standardized in ITU + // recommendations, except for being defined as a series of numbers with a + // maximum length of 40 digits. Other than digits, some other dialing + // characters such as ',' (indicating a wait) or '#' may be stored here. + // + // Note that no regions currently use extensions with short codes, so this + // field is normally only set in conjunction with an E.164 number. It is held + // separately from the E.164 number to allow for short code extensions in the + // future. + string extension = 3; +} \ No newline at end of file diff --git a/google/type/postal_address.proto b/google/type/postal_address.proto new file mode 100644 index 0000000..48f049d --- /dev/null +++ b/google/type/postal_address.proto @@ -0,0 +1,134 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// 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. + +syntax = "proto3"; + +package google.type; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/type/postaladdress;postaladdress"; +option java_multiple_files = true; +option java_outer_classname = "PostalAddressProto"; +option java_package = "com.google.type"; +option objc_class_prefix = "GTP"; + +// Represents a postal address, e.g. for postal delivery or payments addresses. +// Given a postal address, a postal service can deliver items to a premise, P.O. +// Box or similar. +// It is not intended to model geographical locations (roads, towns, +// mountains). +// +// In typical usage an address would be created via user input or from importing +// existing data, depending on the type of process. +// +// Advice on address input / editing: +// - Use an i18n-ready address widget such as +// https://github.com/google/libaddressinput) +// - Users should not be presented with UI elements for input or editing of +// fields outside countries where that field is used. +// +// For more guidance on how to use this schema, please see: +// https://support.google.com/business/answer/6397478 +message PostalAddress { + // The schema revision of the `PostalAddress`. This must be set to 0, which is + // the latest revision. + // + // All new revisions **must** be backward compatible with old revisions. + int32 revision = 1; + + // Required. CLDR region code of the country/region of the address. This + // is never inferred and it is up to the user to ensure the value is + // correct. See http://cldr.unicode.org/ and + // http://www.unicode.org/cldr/charts/30/supplemental/territory_information.html + // for details. Example: "CH" for Switzerland. + string region_code = 2; + + // Optional. BCP-47 language code of the contents of this address (if + // known). This is often the UI language of the input form or is expected + // to match one of the languages used in the address' country/region, or their + // transliterated equivalents. + // This can affect formatting in certain countries, but is not critical + // to the correctness of the data and will never affect any validation or + // other non-formatting related operations. + // + // If this value is not known, it should be omitted (rather than specifying a + // possibly incorrect default). + // + // Examples: "zh-Hant", "ja", "ja-Latn", "en". + string language_code = 3; + + // Optional. Postal code of the address. Not all countries use or require + // postal codes to be present, but where they are used, they may trigger + // additional validation with other parts of the address (e.g. state/zip + // validation in the U.S.A.). + string postal_code = 4; + + // Optional. Additional, country-specific, sorting code. This is not used + // in most regions. Where it is used, the value is either a string like + // "CEDEX", optionally followed by a number (e.g. "CEDEX 7"), or just a number + // alone, representing the "sector code" (Jamaica), "delivery area indicator" + // (Malawi) or "post office indicator" (e.g. Côte d'Ivoire). + string sorting_code = 5; + + // Optional. Highest administrative subdivision which is used for postal + // addresses of a country or region. + // For example, this can be a state, a province, an oblast, or a prefecture. + // Specifically, for Spain this is the province and not the autonomous + // community (e.g. "Barcelona" and not "Catalonia"). + // Many countries don't use an administrative area in postal addresses. E.g. + // in Switzerland this should be left unpopulated. + string administrative_area = 6; + + // Optional. Generally refers to the city/town portion of the address. + // Examples: US city, IT comune, UK post town. + // In regions of the world where localities are not well defined or do not fit + // into this structure well, leave locality empty and use address_lines. + string locality = 7; + + // Optional. Sublocality of the address. + // For example, this can be neighborhoods, boroughs, districts. + string sublocality = 8; + + // Unstructured address lines describing the lower levels of an address. + // + // Because values in address_lines do not have type information and may + // sometimes contain multiple values in a single field (e.g. + // "Austin, TX"), it is important that the line order is clear. The order of + // address lines should be "envelope order" for the country/region of the + // address. In places where this can vary (e.g. Japan), address_language is + // used to make it explicit (e.g. "ja" for large-to-small ordering and + // "ja-Latn" or "en" for small-to-large). This way, the most specific line of + // an address can be selected based on the language. + // + // The minimum permitted structural representation of an address consists + // of a region_code with all remaining information placed in the + // address_lines. It would be possible to format such an address very + // approximately without geocoding, but no semantic reasoning could be + // made about any of the address components until it was at least + // partially resolved. + // + // Creating an address only containing a region_code and address_lines, and + // then geocoding is the recommended way to handle completely unstructured + // addresses (as opposed to guessing which parts of the address should be + // localities or administrative areas). + repeated string address_lines = 9; + + // Optional. The recipient at the address. + // This field may, under certain circumstances, contain multiline information. + // For example, it might contain "care of" information. + repeated string recipients = 10; + + // Optional. The name of the organization at the address. + string organization = 11; +} \ No newline at end of file diff --git a/pkg/commands/local.go b/pkg/commands/local.go index 475c879..e7a64dd 100644 --- a/pkg/commands/local.go +++ b/pkg/commands/local.go @@ -2,6 +2,7 @@ package commands import ( "errors" + "fmt" "github.com/dipjyotimetia/jarvis/pkg/engine/files" "github.com/dipjyotimetia/jarvis/pkg/engine/prompt" @@ -36,6 +37,7 @@ func SpecAnalyzer() *cobra.Command { if len(specs) == 0 { return errors.New("no files found") } + fmt.Println("Analyzing spec files..." + spec) switch spec { case "protobuf": utils.ProtoAnalyzer(specs) diff --git a/pkg/engine/files/files.go b/pkg/engine/files/files.go index a5475da..168ae10 100644 --- a/pkg/engine/files/files.go +++ b/pkg/engine/files/files.go @@ -1,10 +1,8 @@ package files import ( - "bufio" "encoding/json" "fmt" - "io" "io/fs" "log" "os" @@ -70,28 +68,18 @@ func identifyFileType(data []byte) string { } func ReadFile(path string) ([]genai.Text, error) { - file, err := os.Open(path) + data, err := os.ReadFile(path) if err != nil { return nil, err } - defer file.Close() - var lines []genai.Text - reader := bufio.NewReader(file) - const bufferSize = 4096 - buffer := make([]byte, bufferSize) - - for { - bytesRead, err := reader.Read(buffer) - if err != nil { - if err != io.EOF { - return nil, fmt.Errorf("error reading file: %v", err) - } - break - } - lines = append(lines, genai.Text(buffer[:bytesRead])) + lines := strings.Split(string(data), "\n") + var texts []genai.Text + for _, line := range lines { + texts = append(texts, genai.Text(line)) } - return lines, nil + + return texts, nil } func CheckDirectryExists(output string) { diff --git a/pkg/engine/gemini/client.go b/pkg/engine/gemini/client.go index 4aed5f7..546f697 100644 --- a/pkg/engine/gemini/client.go +++ b/pkg/engine/gemini/client.go @@ -20,6 +20,7 @@ type Client interface { GenerateVisionStream(ctx context.Context, prompt string) (*genai.GenerateContentResponseIterator, error) VisionModel() *genai.GenerativeModel ProModel() *genai.GenerativeModel + ProcessDataHouse(ctx context.Context, specs []genai.Text) error Close() } diff --git a/pkg/engine/gemini/model.go b/pkg/engine/gemini/model.go index 799e98c..8fde09e 100644 --- a/pkg/engine/gemini/model.go +++ b/pkg/engine/gemini/model.go @@ -2,11 +2,18 @@ package gemini import "github.com/google/generative-ai-go/genai" +const ( + GEMINI_PRO = "gemini-1.0-pro" + GEMINI_PRO_VISION = "gemini-pro-vision" + EMBEDDING = "embedding-001" +) + +// Model returns the model func (c *client) ProModel() *genai.GenerativeModel { - proModel := c.client.GenerativeModel("gemini-1.0-pro") - proModel.SetTemperature(0.8) + proModel := c.client.GenerativeModel(GEMINI_PRO) + proModel.SetTemperature(0.1) proModel.SetTopK(40) - proModel.SetTopP(0.8) + proModel.SetTopP(0.9) proModel.SafetySettings = []*genai.SafetySetting{ { @@ -21,11 +28,12 @@ func (c *client) ProModel() *genai.GenerativeModel { return proModel } +// VisionModel returns the vision model func (c *client) VisionModel() *genai.GenerativeModel { - visionModel := c.client.GenerativeModel("gemini-pro-vision") - visionModel.SetTemperature(0.8) + visionModel := c.client.GenerativeModel(GEMINI_PRO_VISION) + visionModel.SetTemperature(0.1) visionModel.SetTopK(40) - visionModel.SetTopP(0.8) + visionModel.SetTopP(0.9) visionModel.SafetySettings = []*genai.SafetySetting{ { @@ -39,3 +47,8 @@ func (c *client) VisionModel() *genai.GenerativeModel { } return visionModel } + +// EmbeddingModel returns the embedding model +func (c *client) EmbeddingModel() *genai.EmbeddingModel { + return c.client.EmbeddingModel(EMBEDDING) +} diff --git a/pkg/engine/gemini/text.go b/pkg/engine/gemini/text.go index b138be0..893aad1 100644 --- a/pkg/engine/gemini/text.go +++ b/pkg/engine/gemini/text.go @@ -53,6 +53,23 @@ func (c *client) GenerateTextStream(ctx context.Context, specs []genai.Text, spe return nil } +// ProcessDataHouse ProcessDataHouse +func (c *client) ProcessDataHouse(ctx context.Context, specs []genai.Text) error { + var prompts []genai.Part + for _, spec := range specs { + prompts = append(prompts, spec) + } + + prompts = append(prompts, genai.Text("Generate all possible positive and negative test scenarios in simple english for the provided.")) + + resp, err := c.EmbeddingModel().EmbedContent(ctx, prompts...) + if err != nil { + return err + } + fmt.Println(resp.Embedding.Values) + return nil +} + // GenerateTextStreamWriter GenerateContentStream func (c *client) GenerateTextStreamWriter(ctx context.Context, specs []genai.Text, language, specType string, outputFolder string) error { var prompts []genai.Part diff --git a/pkg/engine/utils/walker.go b/pkg/engine/utils/walker.go index dd2f485..5044d7c 100644 --- a/pkg/engine/utils/walker.go +++ b/pkg/engine/utils/walker.go @@ -5,9 +5,8 @@ import ( "fmt" "os" + "github.com/jhump/protoreflect/desc/protoparse" "github.com/olekukonko/tablewriter" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/descriptorpb" "gopkg.in/yaml.v3" ) @@ -68,23 +67,22 @@ func OpenApiAnalyzer(specFiles []string) { func ProtoAnalyzer(protoFiles []string) error { table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"File", "Service", "Method", "Input Type", "Output Type", "Streaming"}) - for _, protoFile := range protoFiles { - data, err := os.ReadFile(protoFile) - if err != nil { - return fmt.Errorf("error reading file %s: %v", protoFile, err) + parser := protoparse.Parser{ + ImportPaths: []string{"."}, + IncludeSourceCodeInfo: true, + InferImportPaths: true, } - - fds := &descriptorpb.FileDescriptorSet{} - if err := proto.Unmarshal(data, fds); err != nil { + fds, err := parser.ParseFiles(protoFile) + if err != nil { return fmt.Errorf("error parsing Proto file %s: %v", protoFile, err) } - for _, file := range fds.File { - for _, service := range file.Service { - for _, method := range service.Method { + for _, file := range fds { + for _, service := range file.GetServices() { + for _, method := range service.GetMethods() { streaming := "No" - if method.GetClientStreaming() || method.GetServerStreaming() { + if method.AsMethodDescriptorProto().GetClientStreaming() || method.AsMethodDescriptorProto().GetServerStreaming() { streaming = "Yes" } @@ -92,8 +90,8 @@ func ProtoAnalyzer(protoFiles []string) error { file.GetName(), service.GetName(), method.GetName(), - method.GetInputType(), - method.GetOutputType(), + method.AsMethodDescriptorProto().GetInputType(), + method.AsMethodDescriptorProto().GetOutputType(), streaming, }) } From 972b2e6a53f741d602d0b3f7114c41ce5bb2546a Mon Sep 17 00:00:00 2001 From: Dipjyoti Metia Date: Sun, 14 Apr 2024 01:45:32 +1000 Subject: [PATCH 2/4] added new capability for grpcurl --- cmd/cmd.go | 1 + pkg/commands/local.go | 24 ++++++++++++++++++++++++ pkg/engine/utils/walker.go | 37 ++++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index f8db54a..1271c49 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -28,6 +28,7 @@ func init() { rootCmd.AddCommand(commands.GenerateTestModule()) rootCmd.AddCommand(commands.GenerateTestScenarios()) rootCmd.AddCommand(commands.SpecAnalyzer()) + rootCmd.AddCommand(commands.GrpcCurlGenerator()) } func Execute() { diff --git a/pkg/commands/local.go b/pkg/commands/local.go index e7a64dd..3f0d44d 100644 --- a/pkg/commands/local.go +++ b/pkg/commands/local.go @@ -14,6 +14,12 @@ func setSpecPathFlag(cmd *cobra.Command) { cmd.Flags().StringP("path", "p", "", "spec path") } +func setGrpCurlPathFlag(cmd *cobra.Command) { + cmd.Flags().String("proto", "", "protofile path") + cmd.Flags().String("service", "", "service name") + cmd.Flags().String("method", "", "method name") +} + func SpecAnalyzer() *cobra.Command { cmd := &cobra.Command{ Use: "spec-analyzer", @@ -52,3 +58,21 @@ func SpecAnalyzer() *cobra.Command { setSpecPathFlag(cmd) return cmd } + +func GrpcCurlGenerator() *cobra.Command { + cmd := &cobra.Command{ + Use: "grpc-curl", + Aliases: []string{"grpc-curl"}, + Short: "grpc-curl is for generating curl commands for grpc services", + Long: `grpc-curl is for generating curl commands for grpc services`, + RunE: func(cmd *cobra.Command, args []string) error { + protoFile, _ := cmd.Flags().GetString("proto") + serviceName, _ := cmd.Flags().GetString("service") + methodName, _ := cmd.Flags().GetString("method") + utils.GrpCurlCommand(protoFile, serviceName, methodName) + return nil + }, + } + setGrpCurlPathFlag(cmd) + return cmd +} diff --git a/pkg/engine/utils/walker.go b/pkg/engine/utils/walker.go index 5044d7c..de9a6eb 100644 --- a/pkg/engine/utils/walker.go +++ b/pkg/engine/utils/walker.go @@ -69,6 +69,7 @@ func ProtoAnalyzer(protoFiles []string) error { table.SetHeader([]string{"File", "Service", "Method", "Input Type", "Output Type", "Streaming"}) for _, protoFile := range protoFiles { parser := protoparse.Parser{ + //required for google proto files ImportPaths: []string{"."}, IncludeSourceCodeInfo: true, InferImportPaths: true, @@ -81,8 +82,9 @@ func ProtoAnalyzer(protoFiles []string) error { for _, file := range fds { for _, service := range file.GetServices() { for _, method := range service.GetMethods() { + descriptor := method.AsMethodDescriptorProto() streaming := "No" - if method.AsMethodDescriptorProto().GetClientStreaming() || method.AsMethodDescriptorProto().GetServerStreaming() { + if descriptor.GetClientStreaming() || descriptor.GetServerStreaming() { streaming = "Yes" } @@ -90,8 +92,8 @@ func ProtoAnalyzer(protoFiles []string) error { file.GetName(), service.GetName(), method.GetName(), - method.AsMethodDescriptorProto().GetInputType(), - method.AsMethodDescriptorProto().GetOutputType(), + descriptor.GetInputType(), + descriptor.GetOutputType(), streaming, }) } @@ -102,3 +104,32 @@ func ProtoAnalyzer(protoFiles []string) error { table.Render() return nil } + +// generateGrpcurlCommand generates a grpcurl command for a given service and method +func GrpCurlCommand(protoFile, serviceName, methodName string) { + var grpCurl string + parser := protoparse.Parser{ + //required for google proto files + ImportPaths: []string{"."}, + IncludeSourceCodeInfo: true, + InferImportPaths: true, + } + + fds, err := parser.ParseFiles(protoFile) + if err != nil { + fmt.Errorf("error parsing Proto file %s: %v", protoFile, err) + } + for _, file := range fds { + for _, service := range file.GetServices() { + if service.GetName() == serviceName { + for _, method := range service.GetMethods() { + if method.GetName() == methodName { + grpCurl = fmt.Sprintf("grpcurl -plaintext -proto %s -d '{\"%s\"}' localhost:50051 %s/%s", + "", method.AsMethodDescriptorProto().GetInputType(), service.GetFullyQualifiedName(), methodName) + } + } + } + } + } + fmt.Println(grpCurl) +} From 7a8d3c0419159e2af230255977bc4d83031e8ef7 Mon Sep 17 00:00:00 2001 From: Dipjyoti Metia Date: Sun, 14 Apr 2024 02:53:16 +1000 Subject: [PATCH 3/4] fix request body --- pkg/engine/utils/walker.go | 46 +++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/pkg/engine/utils/walker.go b/pkg/engine/utils/walker.go index de9a6eb..7477e2d 100644 --- a/pkg/engine/utils/walker.go +++ b/pkg/engine/utils/walker.go @@ -7,6 +7,7 @@ import ( "github.com/jhump/protoreflect/desc/protoparse" "github.com/olekukonko/tablewriter" + "google.golang.org/protobuf/types/descriptorpb" "gopkg.in/yaml.v3" ) @@ -109,7 +110,6 @@ func ProtoAnalyzer(protoFiles []string) error { func GrpCurlCommand(protoFile, serviceName, methodName string) { var grpCurl string parser := protoparse.Parser{ - //required for google proto files ImportPaths: []string{"."}, IncludeSourceCodeInfo: true, InferImportPaths: true, @@ -119,17 +119,57 @@ func GrpCurlCommand(protoFile, serviceName, methodName string) { if err != nil { fmt.Errorf("error parsing Proto file %s: %v", protoFile, err) } + + serviceFound := false + methodFound := false for _, file := range fds { for _, service := range file.GetServices() { if service.GetName() == serviceName { + serviceFound = true for _, method := range service.GetMethods() { if method.GetName() == methodName { - grpCurl = fmt.Sprintf("grpcurl -plaintext -proto %s -d '{\"%s\"}' localhost:50051 %s/%s", - "", method.AsMethodDescriptorProto().GetInputType(), service.GetFullyQualifiedName(), methodName) + message, err := createJSONRequestBody(method.GetInputType().AsDescriptorProto().GetField()) + if err != nil { + fmt.Errorf("error creating JSON request body: %v", err) + } + grpCurl = fmt.Sprintf("grpcurl -plaintext -proto %s -d '%s' localhost:50051 %s/%s", + "", message, service.GetFullyQualifiedName(), method.GetName()) + methodFound = true + break } } } + if serviceFound && methodFound { + break + } + } + if serviceFound && methodFound { + break } } + + if !serviceFound { + fmt.Errorf("service %s not found", serviceName) + } + if !methodFound { + fmt.Errorf("method %s not found in service %s", methodName, serviceName) + } fmt.Println(grpCurl) } + +func createJSONRequestBody(fields []*descriptorpb.FieldDescriptorProto) (string, error) { + var fieldsMap = make(map[string]interface{}) + fmt.Println(fields) + for _, field := range fields { + if field.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED { + fieldsMap[field.GetJsonName()] = []interface{}{} + } else { + fieldsMap[field.GetJsonName()] = "" + } + } + jsonData, err := json.Marshal(fieldsMap) + if err != nil { + return "", err + } + return string(jsonData), nil +} From ace080f200462fe5a6c051b67538b76445b47729 Mon Sep 17 00:00:00 2001 From: Dipjyoti Metia Date: Sun, 14 Apr 2024 02:55:53 +1000 Subject: [PATCH 4/4] update labels --- .github/labeler.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 7677cef..5b4fc4a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -18,11 +18,6 @@ Documentation: - docs/* - guides/* -# Add 'Documentation' label to any change to .md files within the entire repository -Documentation: -- changed-files: - - any-glob-to-any-file: '**/*.md' - # Add 'source' label to any change to src files within the source dir EXCEPT for the docs sub-folder source: - all: