Skip to content

Commit

Permalink
Add in-app notifications. (heroiclabs#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
mofirouz authored and zyro committed Apr 22, 2018
1 parent 159b025 commit 4c819fd
Show file tree
Hide file tree
Showing 29 changed files with 745 additions and 226 deletions.
38 changes: 17 additions & 21 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,38 @@
package main

import (
"database/sql"
"fmt"
"math/rand"
"net/http"
"net/url"
"os"
"os/signal"
"runtime"
"syscall"
"time"

"go.uber.org/zap"

"database/sql"
"net/url"

"github.com/golang/protobuf/jsonpb"
"github.com/heroiclabs/nakama/migrations"
"github.com/heroiclabs/nakama/server"

"math/rand"

"github.com/golang/protobuf/jsonpb"
_ "github.com/lib/pq"
"go.uber.org/zap"
)

var (
version string = "2.0.0"
commitID string = "dev"

// Shared utility components.
jsonpbMarshaler = &jsonpb.Marshaler{
EnumsAsInts: true,
EmitDefaults: false,
Indent: "",
OrigName: false,
}
jsonpbUnmarshaler = &jsonpb.Unmarshaler{
AllowUnknownFields: false,
}
)

func main() {
Expand Down Expand Up @@ -76,17 +83,6 @@ func main() {
// Check migration status and log if the schema has diverged.
migrations.StartupCheck(multiLogger, db)

// Shared utility components.
jsonpbMarshaler := &jsonpb.Marshaler{
EnumsAsInts: true,
EmitDefaults: false,
Indent: "",
OrigName: false,
}
jsonpbUnmarshaler := &jsonpb.Unmarshaler{
AllowUnknownFields: false,
}

// Start up server components.
registry := server.NewSessionRegistry()
tracker := server.StartLocalTracker(jsonLogger, registry, jsonpbMarshaler, config.GetName())
Expand All @@ -96,7 +92,7 @@ func main() {
multiLogger.Fatal("Failed initializing runtime modules", zap.Error(err))
}
pipeline := server.NewPipeline(config, db, registry, tracker, router, runtimePool)
apiServer := server.StartApiServer(jsonLogger, db, config, registry, tracker, pipeline, runtimePool, jsonpbMarshaler, jsonpbUnmarshaler)
apiServer := server.StartApiServer(jsonLogger, db, jsonpbMarshaler, jsonpbUnmarshaler, config, registry, tracker, router, pipeline, runtimePool)

// Respect OS stop signals.
c := make(chan os.Signal, 2)
Expand Down
2 changes: 1 addition & 1 deletion migrations/migrations-packr.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ import "github.com/gobuffalo/packr"
// Go binary. You can use the "packr clean" command to clean up this,
// and any other packr generated files.
func init() {
packr.PackJSONBytes("./sql", "20180103142001_initial_schema.sql", "\"H4sIAAAAAAAA/6RXXZPaNhR951fc2YcGp+ZjSdJmsm1mHPAmnhCTYpNknxghX4y6tuRKMizt9L93ZBuwgWXTlslDLB2d+6Gje+/2nrfgOQxFtpUsXmkY9K9fQ7hC8Mk9SQk4uV4JqVpQ4MaMIlcYQc4jlKBXCE5G6Ap3OzZ8QamY4DDo9qFtAFfV1pV1Yyi2IoeUbIELDblC0CumYMkSBHygmGlgHKhIs4QRThE2TK8KOxVL13DcVRxioQnjQICKbAtiWQcC0ZXTK62zN73eZrPpksLZrpBxLylhqjf2hq4fuJ1Bt18dmPEElQKJf+RMYgSLLZAsSxgliwQhIRsQEkgsESPQwji8kUwzHtugxFJviERDEzGlJVvkupGvnXtMNQCCA+Fw5QTgBVfwzgm8wDYkX73ww2QWwldnOnX80HMDmExhOPFHXuhN/AAmt+D4d/DR80c2INMrlIAPmTQRCAnMZBKjIm0BYsOFpShdUhlStmQUEsLjnMQIsVij5IzHkKFMmTI3qoDwyNAkLGWa6GLpJC5jqNdqdTrwY8piSTTCLGsNp64TuhA678YueLfgT0Jwv3lBGBgNSAXtFgDA56n3yZnewUf3DtossuxWscwiqP1mM290+DJM/mw8tgukIeMkxXLvizMdfnCm7evBawtMzoJw6nh+WNqc78Dze9zCzPd+m7lHdBFTWUK285JyRzd49coq98maaCLnuUzq5g77nU4hPvWm19NCJKrLUC8L9a10mvQWNHv5cwE0iZ9rEh/5bdyGkXvrzMYhPEP+rKRNBC3S30QXZo1J7MZduAoIh1tJOGWKChuGzlVxVrMU/xQcL579TEo9hCxFaM8C+AGGhJOIWCVJippERJOS5N1d6Dr7C9m7+9ffz2D4wR1+hHaCPNar9u6YBb/Ai0G/37eO8o0pYcmeqe4cVBdU4jKi1EbI6Jz9pskd8mCyZFgSigsh7ueFtppCqVuKhYgTnFcKvIAjKVLkGqXBPo5TGkm6o7uAo7nSIn3aLkYxzqnIuTZfRttwchP9XU5q4Le/wkn2qUSicW4UYrLqva/RVQx1yNtThjyLnmKoQ84wrFGy5fYiQx1ShnEI9OT5mopdsZ0la0AeY2tZN62nStg8wjWj+GghM6u3k6nrvffL1eIQiyyYurfu1PWHblULC7x5i+FkNHkDEx9G7tgNXRg6wdAZuUdFsaGN02poJNSomP8qKKOYcyEpkUtqnoUNShONNmRCMVOTzoW6R/+XYE/YIlSa8aIA/p/87Z066irNFO7CqpK319AeZQzNzERkmqkSUoOQpiUKDlJs1JlncSD5vifRjPcxR4trOLz94JMzHhsrZ96GcXkpGfKo3bdsYHzNNLav9/+N2gPLhkUi6D1G7ReWDREmaNZfWjYQSVdsjVH7lVUmsuqcdU0cXdFTUuNCm3ZT9rRLD6g5CByPAueGgXntyGW0yhe/I9X73Wb3OUZTwTXyA7rZgy52weqo6UjXP51pglREWHdzf5XHbpefnQ74GBPN1ghrkuSogEiEYKs0piBRoVwX85+JEc24dsiJyYgNj/4M9Ww8LqRd8aWoFIlRnWkYxxX2e5qGEUanAwlTuqECVdgkxRWCXhFdxGT+ZKikaEZbfMjMeF6UIGnGb1hKkQKBmK2RgzeCNs2lEtLq7tTn+SP32wX1zSvNlP8eTPFoirPat4FFle/7MXckNrw1mk4+HzR+xsLNeci+2l7aL1vMBYS6af0TAAD//0vLM27QDQAA\"")
packr.PackJSONBytes("./sql", "20180103142001_initial_schema.sql", "\"H4sIAAAAAAAA/6RWXXPaOBe+51ecycUb3NcBkra7nWa3M65xWk+pyWLTbq4YIR9sbWzJlWQou7P/fUe2AZuQZD+4wtKj53zo0Tln+KIHL8AVxVayJNVwNbp8A1GKEJB7khNwSp0KqXpQ4SaMIlcYQ8ljlKBTBKcgNMXdjg1fUComOFwNRtA3gLNm68y6NhRbUUJOtsCFhlIh6JQpWLEMAb9TLDQwDlTkRcYIpwgbptPKTsMyMBx3DYdYasI4EKCi2IJYtYFAdON0qnXxdjjcbDYDUjk7EDIZZjVMDSe+6wWhd3E1GDUH5jxDpUDit5JJjGG5BVIUGaNkmSFkZANCAkkkYgxaGIc3kmnGExuUWOkNkWhoYqa0ZMtSd/K1c4+pDkBwIBzOnBD88AzeO6Ef2obkqx99nM4j+OrMZk4Q+V4I0xm402DsR/40CGF6A05wB5/8YGwDMp2iBPxeSBOBkMBMJjGu0hYidlxYidolVSBlK0YhIzwpSYKQiDVKzngCBcqcKXOjCgiPDU3GcqaJrpYexGUMDXu9iwv4f84SSTTCvOi5M8+JPIic9xMP/BsIphF4v/phFBoNSAX9HgDA7cz/7Mzu4JN3B30WW3avWmYxtH7zuT8+fBmmYD6Z2BXSkHGSY733xZm5H51Z//LqjQUmZ2E0c/wgqm0uduDFPW5hHvi/zL0jupipIiPbRU25o7t6/dqq98maaCIXpcza5g77FxeV+NTb4VALkakBQ72q1JfqPBsuafHqxwpoEr/QJDny27gNY+/GmU8iOEd+XtNmglbp76Irs8YkDpIBnIWEw40knDJFhQ2uc1ad1SzH3wXHJ8/ekloPEcsR+vMQ/gcu4SQmVk2SoyYx0aQmeX8Xec7+Qvbu/vHnObgfPfcT9DPkiU77u2MW/AQvr0ajkXWUb8wJy/ZMbeeguaAaVxClNkLGp+x3Te6QB5M1w4pQXApxv6i01RVK21IiRJLholHgEziSI0WuURrs4zilkeQ7uidwtFRa5M/bxTjBBRUl1+bLaBse3MRol5MW+N3P8CD7VCLRuDAKMVn1P7ToGoY25N1DhrKIn2NoQ04wrFGy1fZJhjakDuMQ6IPnayp2w3aSrAN5jK1nXfeeK2GLGNeM4qOFzKzeTGee/yGoV6tDLLZg5t14My9wvaYWVnjzFqPpePoWpgGMvYkXeeA6oeuMvaOi2NHGw2poJNSpmP8oKKOYUyEpUUpqnoUNShONNhRCMVOTToW6R/+bYB+wxag041UB/C/52zt11FW6KdyF1SRvr6E9yhiam4nINFMlpAYhTUsUHKTYqBPP4kDy955EN97HHK2u4fD2w8/OZGKsnHgbxuWVZMjj/siygfE109i/3P+N+1eWDctM0HuM+y8tG2LM0Ky/smwgkqZsjXH/tVUnsumcbU0cXdFzUuNCm3ZT97QTamt0bHcqlBO6Nuyu+lbiCqWZ1cwVmLmlvgWVijKLISVrhCUih7EXurAsNbiC3ktBaHqu4FuJcguEk2yrUJrJzAynNMO1maW4KJN0cGIQOR5FOiNGO6YFix+fMXaP9DHOo2sul78h1fvdbo88RlPBNfIDutspn+zVzVHTNy9/ONGqqYix7eZecMdu158XFxBgQjRbI6xJVqICIhHUVmnMQaJCuca4DhHNTHlIiUmIDY/+DPN8MqnfX02Xo1IkQXWiqx23gec7WyXe9jg7FhveG8+mtwctn9Dx9WnIvqo+tV+3kicQ6rr3VwAAAP//T8C4JbgNAAA=\"")
}
13 changes: 5 additions & 8 deletions migrations/sql/20180103142001_initial_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,16 @@ CREATE TABLE IF NOT EXISTS user_edge (
);

CREATE TABLE IF NOT EXISTS notification (
PRIMARY KEY (id),
id UUID NOT NULL,
PRIMARY KEY (user_id, create_time ASC, id), -- Preferred sorting order should have been DESC but Cockroach's query analyser is not clever enough.
id UUID CONSTRAINT notification_id_key UNIQUE NOT NULL,
user_id UUID NOT NULL,
subject VARCHAR(255) NOT NULL,
content BYTEA DEFAULT '{}' CHECK (length(content) < 16000) NOT NULL,
code SMALLINT NOT NULL, -- Negative values are System reserved.
sender_id UUID, -- NULL for System messages
create_time BIGINT CHECK (create_time > 0) NOT NULL,
code SMALLINT NOT NULL, -- Negative values are system reserved
sender_id UUID, -- NULL for system messages
create_time BIGINT CHECK (create_time > 0) NOT NULL
);

-- list notifications for a user that are not deleted or expired, starting from a given ID (cursor).
CREATE INDEX IF NOT EXISTS notification_user_id_id_idx ON notification (user_id, id);

-- +migrate Down
DROP TABLE IF EXISTS notification;
DROP TABLE IF EXISTS user_edge;
Expand Down
6 changes: 5 additions & 1 deletion server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,13 @@ type ApiServer struct {
db *sql.DB
config Config
runtimePool *RuntimePool
tracker Tracker
router MessageRouter
grpcServer *grpc.Server
grpcGatewayServer *http.Server
}

func StartApiServer(logger *zap.Logger, db *sql.DB, config Config, registry *SessionRegistry, tracker Tracker, pipeline *pipeline, runtimePool *RuntimePool, jsonpbMarshaler *jsonpb.Marshaler, jsonpbUnmarshaler *jsonpb.Unmarshaler) *ApiServer {
func StartApiServer(logger *zap.Logger, db *sql.DB, jsonpbMarshaler *jsonpb.Marshaler, jsonpbUnmarshaler *jsonpb.Unmarshaler, config Config, registry *SessionRegistry, tracker Tracker, router MessageRouter, pipeline *pipeline, runtimePool *RuntimePool) *ApiServer {
grpcServer := grpc.NewServer(
grpc.StatsHandler(ocgrpc.NewServerStatsHandler()),
grpc.UnaryInterceptor(SecurityInterceptorFunc(logger, config)),
Expand All @@ -66,6 +68,8 @@ func StartApiServer(logger *zap.Logger, db *sql.DB, config Config, registry *Ses
db: db,
config: config,
runtimePool: runtimePool,
tracker: tracker,
router: router,
grpcServer: grpcServer,
}

Expand Down
13 changes: 7 additions & 6 deletions server/api_authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
package server

import (
"golang.org/x/net/context"
"github.com/heroiclabs/nakama/api"
"regexp"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"math/rand"
"regexp"
"strings"
"time"

"github.com/dgrijalva/jwt-go"
"strings"
"github.com/heroiclabs/nakama/api"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

var (
Expand Down
31 changes: 16 additions & 15 deletions server/api_friend.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
package server

import (
"golang.org/x/net/context"
"github.com/heroiclabs/nakama/api"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/heroiclabs/nakama/api"
"github.com/satori/go.uuid"
"go.uber.org/zap"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func (s *ApiServer) ListFriends(ctx context.Context, in *empty.Empty) (*api.Friends, error) {
Expand All @@ -41,16 +41,17 @@ func (s *ApiServer) AddFriends(ctx context.Context, in *api.AddFriendsRequest) (
}

userID := ctx.Value(ctxUserIDKey{}).(uuid.UUID)
username := ctx.Value(ctxUsernameKey{}).(string)

for _, id := range in.GetIds() {
if userID.String() == id {
return nil, status.Error(codes.InvalidArgument, "Cannot add self as friend.")
}
if _, err := uuid.FromString(id); err != nil {
return nil, status.Error(codes.InvalidArgument, "Invalid user ID '" + id +"'.")
return nil, status.Error(codes.InvalidArgument, "Invalid user ID '"+id+"'.")
}
}

username := ctx.Value(ctxUsernameKey{}).(string)
for _, u := range in.GetUsernames() {
if username == u {
return nil, status.Error(codes.InvalidArgument, "Cannot add self as friend.")
Expand All @@ -63,15 +64,15 @@ func (s *ApiServer) AddFriends(ctx context.Context, in *api.AddFriendsRequest) (
return nil, status.Error(codes.Internal, "Error while trying to add friends.")
}

if len(userIDs) + len(in.GetIds()) == 0 {
if len(userIDs)+len(in.GetIds()) == 0 {
return nil, status.Error(codes.InvalidArgument, "No valid ID or username was provided.")
}

allIDs := make([]string, 0, len(in.GetIds()) + len(userIDs))
allIDs := make([]string, 0, len(in.GetIds())+len(userIDs))
allIDs = append(allIDs, in.GetIds()...)
allIDs = append(allIDs, userIDs...)

if err := AddFriends(s.logger, s.db, userID, allIDs); err != nil {
if err := AddFriends(s.logger, s.db, s.tracker, s.router, userID, username, allIDs); err != nil {
return nil, status.Error(codes.Internal, "Error while trying to add friends.")
}

Expand All @@ -89,7 +90,7 @@ func (s *ApiServer) DeleteFriends(ctx context.Context, in *api.DeleteFriendsRequ
return nil, status.Error(codes.InvalidArgument, "Cannot delete self.")
}
if _, err := uuid.FromString(id); err != nil {
return nil, status.Error(codes.InvalidArgument, "Invalid user ID '" + id +"'.")
return nil, status.Error(codes.InvalidArgument, "Invalid user ID '"+id+"'.")
}
}

Expand All @@ -106,11 +107,11 @@ func (s *ApiServer) DeleteFriends(ctx context.Context, in *api.DeleteFriendsRequ
return nil, status.Error(codes.Internal, "Error while trying to delete friends.")
}

if len(userIDs) + len(in.GetIds()) == 0 {
if len(userIDs)+len(in.GetIds()) == 0 {
return nil, status.Error(codes.InvalidArgument, "No valid ID or username was provided.")
}

allIDs := make([]string, 0, len(in.GetIds()) + len(userIDs))
allIDs := make([]string, 0, len(in.GetIds())+len(userIDs))
allIDs = append(allIDs, in.GetIds()...)
allIDs = append(allIDs, userIDs...)

Expand All @@ -132,7 +133,7 @@ func (s *ApiServer) BlockFriends(ctx context.Context, in *api.BlockFriendsReques
return nil, status.Error(codes.InvalidArgument, "Cannot block self.")
}
if _, err := uuid.FromString(id); err != nil {
return nil, status.Error(codes.InvalidArgument, "Invalid user ID '" + id +"'.")
return nil, status.Error(codes.InvalidArgument, "Invalid user ID '"+id+"'.")
}
}

Expand All @@ -149,11 +150,11 @@ func (s *ApiServer) BlockFriends(ctx context.Context, in *api.BlockFriendsReques
return nil, status.Error(codes.Internal, "Error while trying to block friends.")
}

if len(userIDs) + len(in.GetIds()) == 0 {
if len(userIDs)+len(in.GetIds()) == 0 {
return nil, status.Error(codes.InvalidArgument, "No valid ID or username was provided.")
}

allIDs := make([]string, 0, len(in.GetIds()) + len(userIDs))
allIDs := make([]string, 0, len(in.GetIds())+len(userIDs))
allIDs = append(allIDs, in.GetIds()...)
allIDs = append(allIDs, userIDs...)

Expand Down
2 changes: 1 addition & 1 deletion server/api_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
package server

import (
"golang.org/x/net/context"
"github.com/heroiclabs/nakama/api"
"golang.org/x/net/context"
)

func (s *ApiServer) CreateGroup(ctx context.Context, in *api.CreateGroupsRequest) (*api.Groups, error) {
Expand Down
27 changes: 14 additions & 13 deletions server/api_link.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,28 @@
package server

import (
"golang.org/x/net/context"
"github.com/heroiclabs/nakama/api"
"github.com/golang/protobuf/ptypes/empty"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go.uber.org/zap"
"time"
"database/sql"
"strings"
"time"

"github.com/golang/protobuf/ptypes/empty"
"github.com/heroiclabs/nakama/api"
"github.com/lib/pq"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
"strings"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func (s *ApiServer) LinkCustom(ctx context.Context, in *api.AccountCustom) (*empty.Empty, error) {
customID := in.Id
if customID == "" {
return nil, status.Error(codes.InvalidArgument, "Custom ID is required.")
} else if invalidCharsRegex.MatchString(customID) {
return nil, status.Error(codes.InvalidArgument, "Invalid custom ID, no spaces or control characters allowed.")
return nil, status.Error(codes.InvalidArgument, "Invalid custom ID, no spaces or control characters allowed.")
} else if len(customID) < 10 || len(customID) > 128 {
return nil, status.Error(codes.InvalidArgument, "Invalid custom ID, must be 10-128 bytes.")
return nil, status.Error(codes.InvalidArgument, "Invalid custom ID, must be 10-128 bytes.")
}

userID := ctx.Value(ctxUserIDKey{})
Expand Down Expand Up @@ -67,12 +68,12 @@ func (s *ApiServer) LinkDevice(ctx context.Context, in *api.AccountDevice) (*emp
if deviceID == "" {
return nil, status.Error(codes.InvalidArgument, "Device ID is required.")
} else if invalidCharsRegex.MatchString(deviceID) {
return nil, status.Error(codes.InvalidArgument, "Device ID invalid, no spaces or control characters allowed.")
return nil, status.Error(codes.InvalidArgument, "Device ID invalid, no spaces or control characters allowed.")
} else if len(deviceID) < 10 || len(deviceID) > 128 {
return nil, status.Error(codes.InvalidArgument, "Device ID invalid, must be 10-128 bytes.")
return nil, status.Error(codes.InvalidArgument, "Device ID invalid, must be 10-128 bytes.")
}

fnErr := Transact(s.logger, s.db, func (tx *sql.Tx) error {
fnErr := Transact(s.logger, s.db, func(tx *sql.Tx) error {
userID := ctx.Value(ctxUserIDKey{})
ts := time.Now().UTC().Unix()

Expand Down
33 changes: 28 additions & 5 deletions server/api_notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,37 @@ package server

import (
"context"
"github.com/heroiclabs/nakama/api"

"github.com/golang/protobuf/ptypes/empty"
"github.com/heroiclabs/nakama/api"
"github.com/satori/go.uuid"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func (s *ApiServer) DeleteNotifications(ctx context.Context, in *api.DeleteNotificationsRequest) (*empty.Empty, error) {
return &empty.Empty{}, nil
func (s *ApiServer) ListNotifications(ctx context.Context, in *api.ListNotificationsRequest) (*api.NotificationList, error) {
if in.GetLimit() < 1 || in.GetLimit() > 100 {
return nil, status.Error(codes.InvalidArgument, "Invalid limit - limit must be between 1 and 100.")
}

userID := ctx.Value(ctxUserIDKey{}).(uuid.UUID)
notificationList, err := NotificationList(s.logger, s.db, userID, in.GetLimit(), in.GetCacheableCursor())
if err != nil {
return nil, status.Error(codes.Internal, "Error retrieving notifications.")
}

return notificationList, nil
}

func (s *ApiServer) ListNotifications(ctx context.Context, in *api.ListNotificationsRequest) (*api.NotificationList, error) {
return nil, nil
func (s *ApiServer) DeleteNotifications(ctx context.Context, in *api.DeleteNotificationsRequest) (*empty.Empty, error) {
if len(in.GetIds()) == 0 {
return nil, status.Error(codes.InvalidArgument, "There must be at least one notification ID to delete.")
}

userID := ctx.Value(ctxUserIDKey{}).(uuid.UUID)
if err := NotificationDelete(s.logger, s.db, userID, in.GetIds()); err != nil {
return nil, status.Error(codes.Internal, "Error while deleting notifications.")
}

return &empty.Empty{}, nil
}
11 changes: 6 additions & 5 deletions server/api_rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
package server

import (
"golang.org/x/net/context"
"strings"

"github.com/golang/protobuf/ptypes/wrappers"
"github.com/heroiclabs/nakama/api"
"github.com/satori/go.uuid"
"github.com/yuin/gopher-lua"
"go.uber.org/zap"
"strings"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/yuin/gopher-lua"
"github.com/satori/go.uuid"
"github.com/golang/protobuf/ptypes/wrappers"
)

func (s *ApiServer) RpcFunc(ctx context.Context, in *api.Rpc) (*api.Rpc, error) {
Expand Down
Loading

0 comments on commit 4c819fd

Please sign in to comment.