-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
133 lines (116 loc) · 3.23 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package main
import (
"context"
"errors"
"log"
"net"
"sync"
"github.com/shayanh/grpc-go-contracts/contracts"
pb "github.com/shayanh/grpc-go-contracts/examples/mynote/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
const (
port = ":8000"
)
type noteServiceServer struct {
pb.UnimplementedNoteServiceServer
authServerAddress string
contract *contracts.ServerContract
mutex sync.Mutex
notes []*pb.Note
}
var noteService noteServiceServer
func init() {
noteService.mutex.Lock()
defer noteService.mutex.Unlock()
noteService.authServerAddress = "localhost:8001"
noteService.notes = []*pb.Note{
{NoteId: 0, Text: "blah blah blah"},
{NoteId: 1, Text: "very important note"},
}
}
func createContract() *contracts.ServerContract {
getNoteContract := &contracts.UnaryRPCContract{
MethodName: "GetNote",
PreConditions: []contracts.Condition{
func(in *pb.GetNoteRequest) error {
if in.NoteId < 0 {
return errors.New("NoteId must be positive")
}
return nil
},
},
PostConditions: []contracts.Condition{
func(out *pb.Note, outErr error, in *pb.GetNoteRequest, calls contracts.RPCCallHistory) error {
if outErr != nil {
return nil
}
if calls.Filter("mynote.AuthService", "Authenticate").Successful().Empty() {
return errors.New("no successful call to auth service")
}
return nil
},
func(out *pb.Note, outErr error, in *pb.GetNoteRequest, calls contracts.RPCCallHistory) error {
if outErr != nil {
return nil
}
if in.NoteId != out.NoteId {
return errors.New("wrong note id in response")
}
return nil
},
},
}
noteServiceContract := &contracts.ServiceContract{
ServiceName: "mynote.NoteService",
RPCContracts: []*contracts.UnaryRPCContract{
getNoteContract,
},
}
serverContract := contracts.NewServerContract(log.Println)
serverContract.RegisterServiceContract(noteServiceContract)
return serverContract
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatal(err)
}
contract := createContract()
noteService.contract = contract
s := grpc.NewServer(grpc.UnaryInterceptor(contract.UnaryServerInterceptor()))
pb.RegisterNoteServiceServer(s, ¬eService)
if err := s.Serve(lis); err != nil {
log.Fatal(err)
}
}
func (ns *noteServiceServer) authenticate(ctx context.Context, token string) (int, error) {
conn, err := grpc.Dial(ns.authServerAddress, grpc.WithInsecure(),
grpc.WithUnaryInterceptor(ns.contract.UnaryClientInterceptor()))
if err != nil {
return -1, err
}
defer conn.Close()
c := pb.NewAuthServiceClient(conn)
resp, err := c.Authenticate(ctx, &pb.AuthenticateRequest{Token: token})
if err != nil {
return -1, err
}
return int(resp.UserId), nil
}
func (ns *noteServiceServer) GetNote(ctx context.Context, in *pb.GetNoteRequest) (*pb.Note, error) {
_, err := ns.authenticate(ctx, in.Token)
if err != nil {
return nil, status.Error(codes.Unauthenticated, "authentication failed")
}
for _, note := range ns.notes {
if note.NoteId == in.NoteId {
return note, nil
// Wrong implementation:
// return &pb.Note{NoteId: note.NoteId + 1, Text: note.Text}, nil
}
}
return nil, status.Errorf(codes.NotFound, "no note with ID %d", in.NoteId)
}