Skip to content
This repository was archived by the owner on May 11, 2022. It is now read-only.

Commit ed50e1e

Browse files
committed
fix #177: implement read input command
1 parent 77f75df commit ed50e1e

File tree

5 files changed

+147
-22
lines changed

5 files changed

+147
-22
lines changed

cmd/grpc_helper.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import (
88
"os"
99
"reflect"
1010
"strings"
11+
"time"
1112

1213
pb "github.com/kamilsk/form-api/pkg/server/grpc"
1314
kit "github.com/kamilsk/go-kit/pkg/strings"
1415

1516
"github.com/kamilsk/form-api/pkg/config"
17+
"github.com/kamilsk/form-api/pkg/domain"
1618
"github.com/kamilsk/form-api/pkg/server/grpc/middleware"
1719
"github.com/mitchellh/mapstructure"
1820
"github.com/pkg/errors"
@@ -25,6 +27,7 @@ import (
2527
const (
2628
schemaKind kind = "Schema"
2729
templateKind kind = "Template"
30+
inputKind kind = "Input"
2831
)
2932

3033
var entities factory
@@ -38,6 +41,7 @@ func init() {
3841
readCmd: {
3942
schemaKind: func() interface{} { return &pb.ReadSchemaRequest{} },
4043
templateKind: func() interface{} { return &pb.ReadTemplateRequest{} },
44+
inputKind: func() interface{} { return &inputReadProxy{} },
4145
},
4246
updateCmd: {
4347
schemaKind: func() interface{} { return &pb.UpdateSchemaRequest{} },
@@ -50,6 +54,19 @@ func init() {
5054
}
5155
}
5256

57+
type inputReadProxy struct {
58+
Filter struct {
59+
ID domain.ID `json:"id" mapstructure:"id" yaml:"id"`
60+
Condition struct {
61+
SchemaID domain.ID `json:"schema_id" mapstructure:"schema_id" yaml:"schema_id"`
62+
CreatedAt struct {
63+
Start string `json:"start" mapstructure:"start" yaml:"start"`
64+
End string `json:"end" mapstructure:"end" yaml:"end"`
65+
} `json:"created_at" mapstructure:"created_at" yaml:"created_at"`
66+
} `json:"condition" mapstructure:"condition" yaml:"condition"`
67+
} `json:"filter" mapstructure:"filter" yaml:"filter"`
68+
}
69+
5370
func communicate(cmd *cobra.Command, _ []string) error {
5471
entity, err := entities.new(cmd)
5572
if err != nil {
@@ -167,6 +184,38 @@ func call(cnf config.GRPCConfig, entity interface{}) (interface{}, error) {
167184
case *pb.ReadTemplateRequest:
168185
return pb.NewTemplateClient(conn).Read(ctx, request)
169186

187+
// TODO issue#180
188+
// - remove hacks with proxies
189+
// - remove deps to github.com/mitchellh/mapstructure
190+
// - use github.com/ghodss/yaml and github.com/grpc-ecosystem/grpc-gateway/runtime instead
191+
case *inputReadProxy:
192+
grpcRequest := &pb.ReadInputRequest{}
193+
if request.Filter.ID != "" {
194+
grpcRequest.Filter = &pb.ReadInputRequest_Id{Id: request.Filter.ID.String()}
195+
} else {
196+
var start, end *time.Time
197+
if request.Filter.Condition.CreatedAt.Start != "" {
198+
t, parseErr := time.Parse(time.RFC3339, request.Filter.Condition.CreatedAt.Start)
199+
if parseErr == nil {
200+
start = &t
201+
}
202+
}
203+
if request.Filter.Condition.CreatedAt.End != "" {
204+
t, parseErr := time.Parse(time.RFC3339, request.Filter.Condition.CreatedAt.End)
205+
if parseErr == nil {
206+
end = &t
207+
}
208+
}
209+
grpcRequest.Filter = &pb.ReadInputRequest_Condition{Condition: &pb.InputFilter{
210+
SchemaId: request.Filter.Condition.SchemaID.String(),
211+
CreatedAt: &pb.TimestampRange{
212+
Start: pb.Timestamp(start),
213+
End: pb.Timestamp(end),
214+
},
215+
}}
216+
}
217+
return pb.NewInputClient(conn).Read(ctx, grpcRequest)
218+
170219
case *pb.UpdateSchemaRequest:
171220
return pb.NewSchemaClient(conn).Update(ctx, request)
172221
case *pb.UpdateTemplateRequest:

pkg/server/grpc/server_input.go

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@ package grpc
22

33
import (
44
"context"
5-
"log"
5+
"encoding/json"
6+
"time"
7+
8+
repository "github.com/kamilsk/form-api/pkg/storage/types"
9+
10+
"github.com/kamilsk/form-api/pkg/domain"
11+
"github.com/kamilsk/form-api/pkg/server/grpc/middleware"
12+
"github.com/kamilsk/form-api/pkg/storage/query"
13+
"google.golang.org/grpc/codes"
14+
"google.golang.org/grpc/status"
615
)
716

817
// NewInputServer returns new instance of server API for Input service.
@@ -15,7 +24,72 @@ type inputServer struct {
1524
}
1625

1726
// Read TODO issue#173
18-
func (*inputServer) Read(context.Context, *ReadInputRequest) (*ReadInputResponse, error) {
19-
log.Println("InputServer.Read was called")
20-
return &ReadInputResponse{}, nil
27+
func (server *inputServer) Read(ctx context.Context, req *ReadInputRequest) (*ReadInputResponse, error) {
28+
tokenID, authErr := middleware.TokenExtractor(ctx)
29+
if authErr != nil {
30+
return nil, authErr
31+
}
32+
33+
var entries []*InputEntry
34+
push := func(input repository.Input) error {
35+
data, encodeErr := json.Marshal(input.Data)
36+
if encodeErr != nil {
37+
return status.Errorf(codes.Internal,
38+
"trying to marshal data `%#v` of the input %q into JSON: %+v",
39+
input.Data, input.ID, encodeErr)
40+
}
41+
entries = append(entries, &InputEntry{
42+
Id: input.ID.String(),
43+
SchemaId: input.SchemaID.String(),
44+
Data: string(data),
45+
})
46+
return nil
47+
}
48+
49+
switch filter := req.Filter.(type) {
50+
case *ReadInputRequest_Condition:
51+
inputs, readErr := server.storage.ReadInputByFilter(ctx, tokenID, query.InputFilter{
52+
SchemaID: func() domain.ID {
53+
if filter.Condition == nil {
54+
return ""
55+
}
56+
return domain.ID(filter.Condition.SchemaId)
57+
}(),
58+
From: func() *time.Time {
59+
if filter.Condition == nil {
60+
return nil
61+
}
62+
if filter.Condition.CreatedAt == nil {
63+
return nil
64+
}
65+
return Time(filter.Condition.CreatedAt.Start)
66+
}(),
67+
To: func() *time.Time {
68+
if filter.Condition == nil {
69+
return nil
70+
}
71+
if filter.Condition.CreatedAt == nil {
72+
return nil
73+
}
74+
return Time(filter.Condition.CreatedAt.End)
75+
}(),
76+
})
77+
if readErr != nil {
78+
return nil, status.Errorf(codes.Internal, "error happened: %+v", readErr)
79+
}
80+
for _, input := range inputs {
81+
if pushErr := push(input); pushErr != nil {
82+
return nil, pushErr
83+
}
84+
}
85+
case *ReadInputRequest_Id:
86+
input, readErr := server.storage.ReadInputByID(ctx, tokenID, domain.ID(filter.Id))
87+
if readErr != nil {
88+
return nil, status.Errorf(codes.Internal, "error happened: %+v", readErr)
89+
}
90+
if pushErr := push(input); pushErr != nil {
91+
return nil, pushErr
92+
}
93+
}
94+
return &ReadInputResponse{Entries: entries}, nil
2195
}

pkg/storage/executor/internal/postgres/input.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,20 @@ func (scope inputScope) ReadByFilter(token *types.Token, filter query.InputFilte
6767
q := `SELECT "i"."id", "i"."data", "i"."created_at"
6868
FROM "input" "i"
6969
INNER JOIN "schema" "s" ON "s"."id" = "i"."schema_id"
70-
WHERE "i"."schema_id" = $1 AND "s"."account_id"`
70+
WHERE "i"."schema_id" = $1 AND "s"."account_id" = $2`
7171
args := append(make([]interface{}, 0, 4), filter.SchemaID, token.User.AccountID)
7272
// TODO go 1.10 builder := strings.Builder{}
7373
builder := bytes.NewBuffer(make([]byte, 0, len(q)+39))
7474
_, _ = builder.WriteString(q)
7575
switch {
76-
case !filter.From.IsZero() && !filter.To.IsZero():
77-
_, _ = builder.WriteString(` AND "i"."created_at" BETWEEN $2 AND $3`)
76+
case filter.From != nil && filter.To != nil:
77+
_, _ = builder.WriteString(` AND "i"."created_at" BETWEEN $3 AND $4`)
7878
args = append(args, filter.From, filter.To)
79-
case !filter.From.IsZero():
80-
_, _ = builder.WriteString(` AND "i"."created_at" >= $2`)
79+
case filter.From != nil:
80+
_, _ = builder.WriteString(` AND "i"."created_at" >= $3`)
8181
args = append(args, filter.From)
82-
case !filter.To.IsZero():
83-
_, _ = builder.WriteString(` AND "i"."created_at" <= $2`)
82+
case filter.To != nil:
83+
_, _ = builder.WriteString(` AND "i"."created_at" <= $3`)
8484
args = append(args, filter.To)
8585
}
8686
entities := make([]types.Input, 0, 8)

pkg/storage/executor/internal/postgres/input_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ func TestInputReader(t *testing.T) {
7878
assert.NoError(t, err)
7979
defer conn.Close()
8080

81-
testCases := []struct {
81+
now := time.Now()
82+
83+
tests := []struct {
8284
name string
8385
mocker func(sqlmock.Sqlmock)
8486
filter query.InputFilter
@@ -92,7 +94,7 @@ func TestInputReader(t *testing.T) {
9294
WillReturnRows(
9395
sqlmock.
9496
NewRows([]string{"id", "data", "created_at"}).
95-
AddRow(id, `{"input":["test"]}`, time.Now()),
97+
AddRow(id, `{"input":["test"]}`, now),
9698
)
9799
},
98100
filter: query.InputFilter{SchemaID: id},
@@ -106,10 +108,10 @@ func TestInputReader(t *testing.T) {
106108
WillReturnRows(
107109
sqlmock.
108110
NewRows([]string{"id", "data", "created_at"}).
109-
AddRow(id, `{"input":["test"]}`, time.Now()),
111+
AddRow(id, `{"input":["test"]}`, now),
110112
)
111113
},
112-
filter: query.InputFilter{SchemaID: id, From: time.Now()},
114+
filter: query.InputFilter{SchemaID: id, From: &now},
113115
},
114116
{
115117
name: `by schema ID and "to" date`,
@@ -120,10 +122,10 @@ func TestInputReader(t *testing.T) {
120122
WillReturnRows(
121123
sqlmock.
122124
NewRows([]string{"id", "data", "created_at"}).
123-
AddRow(id, `{"input":["test"]}`, time.Now()),
125+
AddRow(id, `{"input":["test"]}`, now),
124126
)
125127
},
126-
filter: query.InputFilter{SchemaID: id, To: time.Now()},
128+
filter: query.InputFilter{SchemaID: id, To: &now},
127129
},
128130
{
129131
name: `by schema ID and "from" and "to" dates`,
@@ -134,15 +136,15 @@ func TestInputReader(t *testing.T) {
134136
WillReturnRows(
135137
sqlmock.
136138
NewRows([]string{"id", "data", "created_at"}).
137-
AddRow(id, `{"input":["test"]}`, time.Now()),
139+
AddRow(id, `{"input":["test"]}`, now),
138140
)
139141
},
140-
filter: query.InputFilter{SchemaID: id, From: time.Now(), To: time.Now()},
142+
filter: query.InputFilter{SchemaID: id, From: &now, To: &now},
141143
},
142144
}
143145

144146
var exec executor.InputReader = NewInputContext(ctx, conn)
145-
for _, test := range testCases {
147+
for _, test := range tests {
146148
test.mocker(mock)
147149
inputs, readErr := exec.ReadByFilter(token, test.filter)
148150
assert.NoError(t, readErr, test.name)

pkg/storage/query/input.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import (
99
// InputFilter TODO issue#173
1010
type InputFilter struct {
1111
SchemaID domain.ID
12-
From time.Time
13-
To time.Time
12+
From *time.Time
13+
To *time.Time
1414
}
1515

1616
// WriteInput TODO issue#173

0 commit comments

Comments
 (0)