Skip to content

Commit

Permalink
refactor(logic)!: add error field to anwser proto type
Browse files Browse the repository at this point in the history
  • Loading branch information
ccamel committed Jan 13, 2024
1 parent 250a818 commit b70ecc9
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 68 deletions.
7 changes: 6 additions & 1 deletion proto/logic/v1beta2/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ message Result {
message Answer {
option (gogoproto.goproto_stringer) = true;

// result is the result of the query.
// success specifies if the query was successful.
bool success = 1 [(gogoproto.moretags) = "yaml:\"success\",omitempty"];
// error specifies the error message if the query caused an error.
string error = 5 [
(gogoproto.nullable) = true,
(gogoproto.moretags) = "yaml:\"error\",omitempty"
];
// has_more specifies if there are more solutions than the ones returned.
bool has_more = 2 [(gogoproto.moretags) = "yaml:\"has_more\",omitempty"];
// variables represent all the variables in the query.
Expand Down
98 changes: 82 additions & 16 deletions x/logic/keeper/grpc_query_ask_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ func TestGRPCAsk(t *testing.T) {
cases := []struct {
program string
query string
expectedAsnwer *types.Answer
expectedError bool
expectedAnswer *types.Answer
expectedError string
}{
{
program: "father(bob, alice).",
query: "father(bob, X).",
expectedAsnwer: &types.Answer{
expectedAnswer: &types.Answer{
Success: true,
HasMore: false,
Variables: []string{"X"},
Expand All @@ -46,12 +46,27 @@ func TestGRPCAsk(t *testing.T) {
},
}}}},
},
expectedError: false,
},
{
program: `father("bob", "alice").`,
query: `father("bob", X).`,
expectedAnswer: &types.Answer{
Success: true,
HasMore: false,
Variables: []string{"X"},
Results: []types.Result{{Substitutions: []types.Substitution{{
Variable: "X",
Term: types.Term{
Name: "[a,l,i,c,e]",
Arguments: nil,
},
}}}},
},
},
{
program: "father(bob, alice). father(bob, john).",
query: "father(bob, X).",
expectedAsnwer: &types.Answer{
expectedAnswer: &types.Answer{
Success: true,
HasMore: true,
Variables: []string{"X"},
Expand All @@ -63,24 +78,74 @@ func TestGRPCAsk(t *testing.T) {
},
}}}},
},
expectedError: false,
},
{
program: "father(bob, alice).",
query: "father(bob, john).",
expectedAsnwer: &types.Answer{
expectedAnswer: &types.Answer{
Success: false,
HasMore: false,
Variables: nil,
Results: nil,
},
expectedError: false,
},
{
program: "father(bob, alice).",
query: "father(bob, X, O).",
expectedAsnwer: nil,
expectedError: true,
program: "",
query: "block_height(X).",
expectedAnswer: &types.Answer{
Success: true,
HasMore: false,
Variables: []string{"X"},
Results: []types.Result{{Substitutions: []types.Substitution{{
Variable: "X",
Term: types.Term{
Name: "0",
Arguments: nil,
},
}}}},
},
},
{
program: "father(bob, 'élodie').",
query: "father(bob, X).",
expectedAnswer: &types.Answer{
Success: true,
HasMore: false,
Variables: []string{"X"},
Results: []types.Result{{Substitutions: []types.Substitution{{
Variable: "X",
Term: types.Term{
Name: "élodie",
Arguments: nil,
},
}}}},
},
},
{
program: "father(bob, alice).",
query: "father(bob, X, O).",
expectedAnswer: &types.Answer{
Success: false,
Error: "error(existence_error(procedure,father/3),root)",
HasMore: false,
Variables: nil,
Results: nil,
},
},
{
program: "father°(bob, alice).",
query: "father(bob, X).",
expectedError: "error compiling query: unexpected token: invalid(°): invalid argument",
},
{
program: "father(bob, alice).",
query: "father°(bob, X).",
expectedError: "error executing query: unexpected token: invalid(°): invalid argument",
},
{
program: `father("bob", "alice").`,
query: `father("bob"", X).`,
expectedError: "error executing query: EOF: invalid argument",
},
}

Expand Down Expand Up @@ -128,13 +193,14 @@ func TestGRPCAsk(t *testing.T) {
result, err := queryClient.Ask(gocontext.Background(), &query)

Convey("Then it should return the expected answer", func() {
if tc.expectedError {
So(err, ShouldNotBeNil)
if tc.expectedError != "" {
So(err, ShouldNotEqual, nil)
So(err.Error(), ShouldEqual, tc.expectedError)
So(result, ShouldBeNil)
} else {
So(err, ShouldBeNil)
So(err, ShouldEqual, nil)
So(result, ShouldNotBeNil)
So(result.Answer, ShouldResemble, tc.expectedAsnwer)
So(result.Answer, ShouldResemble, tc.expectedAnswer)
}
})
})
Expand Down
49 changes: 30 additions & 19 deletions x/logic/keeper/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,32 @@ func (k Keeper) execute(ctx goctx.Context, program, query string) (*types.QueryS
_ = sols.Close()
}()

success := false
limits := k.limits(ctx)
var userOutput string
if userOutputBuffer != nil {
userOutput = userOutputBuffer.String()
}
answer, err := k.solsToAnswer(sdkCtx, sols) //nolint:contextcheck
if err != nil {
return nil, err
}

return &types.QueryServiceAskResponse{
Height: uint64(sdkCtx.BlockHeight()),
GasUsed: sdkCtx.GasMeter().GasConsumed(),
Answer: answer,
UserOutput: userOutput,
}, nil
}

// solsToAnswer consumes the given prolog solutions and convert it to an answer.
func (k Keeper) solsToAnswer(sdkCtx sdk.Context, sols *prolog.Solutions) (*types.Answer, error) {
solSuccess := false
solError := ""
limits := k.limits(sdkCtx)
var variables []string
results := make([]types.Result, 0)
for nb := sdkmath.ZeroUint(); nb.LT(*limits.MaxResultCount) && sols.Next(); nb = nb.Incr() {
success = true
solSuccess = true

m := types.TermResults{}
if err := sols.Scan(m); err != nil {
Expand All @@ -80,24 +100,15 @@ func (k Keeper) execute(ctx goctx.Context, program, query string) (*types.QueryS
if sdkCtx.GasMeter().IsOutOfGas() {
panic(sdk.ErrorOutOfGas{Descriptor: "Prolog interpreter execution"})
}
return nil, errorsmod.Wrapf(types.InvalidArgument, "error interpreting solutions: %v", err.Error())
}

var userOutput string
if userOutputBuffer != nil {
userOutput = userOutputBuffer.String()
solError = sols.Err().Error()
}

return &types.QueryServiceAskResponse{
Height: uint64(sdkCtx.BlockHeight()),
GasUsed: sdkCtx.GasMeter().GasConsumed(),
Answer: &types.Answer{
Success: success,
HasMore: sols.Next(),
Variables: variables,
Results: results,
},
UserOutput: userOutput,
return &types.Answer{
Success: solSuccess,
Error: solError,
HasMore: sols.Next(),
Variables: variables,
Results: results,
}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion x/logic/meter/safe.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

// safeMeterDecorater is a wrapper around sdk.GasMeter that provides go-routine-safe access to the underlying gas meter.
// This is needed because the interpreter is uses multiple go-routines, and the gas meter is shared between multiple
// This is needed because the interpreter uses multiple go-routines, and the gas meter is shared between multiple
// goroutines.
type safeMeterDecorater struct {
gasMeter sdk.GasMeter
Expand Down
116 changes: 85 additions & 31 deletions x/logic/types/types.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b70ecc9

Please sign in to comment.