Merge pull request #50 from giolekva/rest_user_service

Add rest router
diff --git a/core/kg/api/rest/handler.go b/core/kg/api/rest/handler.go
new file mode 100644
index 0000000..88d0670
--- /dev/null
+++ b/core/kg/api/rest/handler.go
@@ -0,0 +1,37 @@
+package rest
+
+import (
+	"encoding/json"
+	"net/http"
+)
+
+type HandlerFunc func(w http.ResponseWriter, r *http.Request) error
+
+// ServeHTTP calls f(w, r) and handles error
+func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	if err := f(w, r); err != nil {
+		jsoner(w, http.StatusBadRequest, err.Error()) // TODO detect the correct statusCode from error
+	}
+}
+
+func jsoner(w http.ResponseWriter, statusCode int, payload interface{}) error {
+	w.Header().Set("Content-Type", "application/json")
+
+	// If there is nothing to marshal then set status code and return.
+	if payload == nil {
+		_, err := w.Write([]byte("{}"))
+		return err
+	}
+
+	w.WriteHeader(statusCode)
+
+	encoder := json.NewEncoder(w)
+	encoder.SetEscapeHTML(true)
+	encoder.SetIndent("", "")
+
+	if err := encoder.Encode(payload); err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/core/kg/api/rest/router.go b/core/kg/api/rest/router.go
new file mode 100644
index 0000000..d9f24d1
--- /dev/null
+++ b/core/kg/api/rest/router.go
@@ -0,0 +1,53 @@
+package rest
+
+import (
+	"net/http"
+
+	"github.com/giolekva/pcloud/core/kg/common"
+	"github.com/giolekva/pcloud/core/kg/log"
+	"github.com/gorilla/mux"
+)
+
+const APIURLSuffix = "/api/v1"
+
+type Router struct {
+	App    common.AppIface
+	Logger common.LoggerIface
+
+	Root    *mux.Router // ''
+	APIRoot *mux.Router // 'api/v1'
+	Users   *mux.Router // 'api/v1/users'
+	User    *mux.Router // 'api/v1/users/{user_id:[A-Za-z0-9]+}'
+}
+
+func NewRouter(root *mux.Router, app common.AppIface, logger common.LoggerIface) *Router {
+	apiRoot := root.PathPrefix(APIURLSuffix).Subrouter()
+	users := apiRoot.PathPrefix("/users").Subrouter()
+	user := apiRoot.PathPrefix("/users/{user_id:[A-Za-z0-9]+}").Subrouter()
+
+	routers := &Router{
+		App:    app,
+		Logger: logger,
+
+		Root:    root,
+		APIRoot: apiRoot,
+		Users:   users,
+		User:    user,
+	}
+
+	root.Handle("/api/v1/{anything:.*}", http.HandlerFunc(http.NotFound))
+	routers.initUsers()
+	root.Use(routers.loggerMiddleware)
+	return routers
+}
+
+func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	router.Root.ServeHTTP(w, req)
+}
+
+func (router *Router) loggerMiddleware(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		router.Logger.Debug(r.Method, log.String("url", r.URL.String()))
+		next.ServeHTTP(w, r)
+	})
+}
diff --git a/core/kg/api/rest/user_service.go b/core/kg/api/rest/user_service.go
new file mode 100644
index 0000000..c976de7
--- /dev/null
+++ b/core/kg/api/rest/user_service.go
@@ -0,0 +1,86 @@
+package rest
+
+import (
+	"encoding/json"
+	"net/http"
+	"strconv"
+
+	"github.com/giolekva/pcloud/core/kg/app"
+	"github.com/giolekva/pcloud/core/kg/model"
+	"github.com/gorilla/mux"
+	"github.com/pkg/errors"
+)
+
+func (router *Router) initUsers() {
+	router.Users.Handle("", router.buildCreateUserHandler()).Methods("POST")
+	router.Users.Handle("", router.buildGetUsersHandler()).Methods("GET")
+	router.User.Handle("", router.buildGetUserHandler()).Methods("GET")
+}
+
+func (router *Router) buildCreateUserHandler() http.Handler {
+	fn := func(w http.ResponseWriter, r *http.Request) error {
+		var user *model.User
+		if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
+			return errors.Wrap(err, "can't decode request body")
+		}
+		if err := user.IsValidInput(); err != nil {
+			return errors.Wrap(err, "invalid user input")
+		}
+		user.Password = app.HashPassword(user.Password)
+		user.SanitizeInput()
+		updatedUser, err := router.App.CreateUser(user)
+		if err != nil {
+			return errors.Wrap(err, "can't create user")
+		}
+		updatedUser.SanitizeOutput()
+
+		jsoner(w, http.StatusOK, updatedUser)
+		return nil
+	}
+	return HandlerFunc(fn)
+}
+
+func (router *Router) buildGetUsersHandler() http.Handler {
+	fn := func(w http.ResponseWriter, r *http.Request) error {
+		page := r.URL.Query().Get("page")
+		perPage := r.URL.Query().Get("per_page")
+
+		pageInt, err := strconv.Atoi(page)
+		if err != nil {
+			return errors.New("parameter page should be an int")
+		}
+		perPageInt, err := strconv.Atoi(perPage)
+		if err != nil {
+			return errors.New("parameter per_page should be an int")
+		}
+		users, err := router.App.GetUsers(pageInt, perPageInt)
+		if err != nil {
+			return errors.Wrap(err, "can't get users from app")
+		}
+
+		jsoner(w, http.StatusOK, users)
+		return nil
+	}
+	return HandlerFunc(fn)
+}
+
+func (router *Router) buildGetUserHandler() http.Handler {
+	fn := func(w http.ResponseWriter, r *http.Request) error {
+		params := mux.Vars(r)
+
+		var userID string
+		var ok bool
+		if userID, ok = params["user_id"]; !ok {
+			return errors.New("missing parameter: user_id")
+		}
+		user, err := router.App.GetUser(userID)
+
+		if err != nil {
+			return errors.Wrapf(err, "can't get user from app")
+		}
+
+		jsoner(w, http.StatusOK, user)
+		return nil
+	}
+	return HandlerFunc(fn)
+}
diff --git a/core/kg/rpc/user_service.go b/core/kg/api/rpc/user_service.go
similarity index 90%
rename from core/kg/rpc/user_service.go
rename to core/kg/api/rpc/user_service.go
index ee385cb..c424cee 100644
--- a/core/kg/rpc/user_service.go
+++ b/core/kg/api/rpc/user_service.go
@@ -3,6 +3,7 @@
 import (
 	"context"
 
+	"github.com/giolekva/pcloud/core/kg/common"
 	"github.com/giolekva/pcloud/core/kg/model/proto"
 	"github.com/pkg/errors"
 	"google.golang.org/protobuf/types/known/timestamppb"
@@ -10,11 +11,11 @@
 
 type userService struct {
 	proto.UnimplementedUserServiceServer
-	app appIface
+	app common.AppIface
 }
 
 // NewService returns new user service
-func NewService(app appIface) proto.UserServiceServer {
+func NewService(app common.AppIface) proto.UserServiceServer {
 	s := &userService{
 		app: app,
 	}
diff --git a/core/kg/rpc/user_service_test.go b/core/kg/api/rpc/user_service_test.go
similarity index 100%
rename from core/kg/rpc/user_service_test.go
rename to core/kg/api/rpc/user_service_test.go
diff --git a/core/kg/app/app.go b/core/kg/app/app.go
index 0c65fc8..8cdc0d6 100644
--- a/core/kg/app/app.go
+++ b/core/kg/app/app.go
@@ -1,19 +1,23 @@
 package app
 
 import (
+	"github.com/giolekva/pcloud/core/kg/common"
+	"github.com/giolekva/pcloud/core/kg/model"
 	"github.com/giolekva/pcloud/core/kg/store"
 )
 
 // App represents an application layer of the kg
 type App struct {
 	store  store.Store
-	logger logger
+	config *model.Config
+	logger common.LoggerIface
 }
 
 // NewApp creates new app
-func NewApp(store store.Store, logger logger) *App {
+func NewApp(store store.Store, config *model.Config, logger common.LoggerIface) *App {
 	return &App{
 		store:  store,
+		config: config,
 		logger: logger,
 	}
 }
diff --git a/core/kg/app/app_mock.go b/core/kg/app/app_mock.go
index f85e754..41575ff 100644
--- a/core/kg/app/app_mock.go
+++ b/core/kg/app/app_mock.go
@@ -2,6 +2,7 @@
 
 import (
 	"github.com/giolekva/pcloud/core/kg/log"
+	"github.com/giolekva/pcloud/core/kg/model"
 	"github.com/giolekva/pcloud/core/kg/store/memory"
 )
 
@@ -13,6 +14,7 @@
 func NewTestApp() *MockApp {
 	memStore := memory.New()
 	logger := &log.NoOpLogger{}
-	a := NewApp(memStore, logger)
+	config := model.NewConfig()
+	a := NewApp(memStore, config, logger)
 	return &MockApp{a}
 }
diff --git a/core/kg/app/interfaces.go b/core/kg/app/interfaces.go
deleted file mode 100644
index 53cb1aa..0000000
--- a/core/kg/app/interfaces.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package app
-
-import "github.com/giolekva/pcloud/core/kg/log"
-
-type logger interface {
-	Debug(message string, fields ...log.Field)
-	Info(message string, fields ...log.Field)
-	Warn(message string, fields ...log.Field)
-	Error(message string, fields ...log.Field)
-}
diff --git a/core/kg/app/user.go b/core/kg/app/user.go
index 07ff301..c21b86e 100644
--- a/core/kg/app/user.go
+++ b/core/kg/app/user.go
@@ -1,8 +1,10 @@
 package app
 
 import (
+	"github.com/giolekva/pcloud/core/kg/log"
 	"github.com/giolekva/pcloud/core/kg/model"
 	"github.com/pkg/errors"
+	"golang.org/x/crypto/bcrypt"
 )
 
 // GetUser returns user
@@ -13,3 +15,47 @@
 	}
 	return user, nil
 }
+
+// CreateUser creates a user. For now it is used only for creation of the very first user
+func (a *App) CreateUser(user *model.User) (*model.User, error) {
+	if !a.isFirstUserAccount() {
+		return nil, errors.New("not a first user")
+	}
+
+	updatedUser, err := a.store.User().Save(user)
+	if err != nil {
+		return nil, errors.Wrap(err, "can't save user to the DB")
+	}
+	return updatedUser, nil
+}
+
+//GetUsers returns list of users
+func (a *App) GetUsers(page, perPage int) ([]*model.User, error) {
+	users, err := a.store.User().GetAllWithOptions(page, perPage)
+	if err != nil {
+		return nil, errors.Wrap(err, "can't get users with options from store")
+	}
+	return users, nil
+}
+
+func (a *App) isFirstUserAccount() bool {
+	count, err := a.store.User().Count()
+	if err != nil {
+		a.logger.Error("error fetching first user account", log.Err(err))
+	}
+	return count > 0
+}
+
+// HashPassword hashes user's password
+func HashPassword(password string) string {
+	if password == "" {
+		return ""
+	}
+
+	hash, err := bcrypt.GenerateFromPassword([]byte(password), 10)
+	if err != nil {
+		panic(err)
+	}
+
+	return string(hash)
+}
diff --git a/core/kg/cmd/commands/root.go b/core/kg/cmd/commands/root.go
index 70a4d23..bb4136a 100644
--- a/core/kg/cmd/commands/root.go
+++ b/core/kg/cmd/commands/root.go
@@ -1,9 +1,11 @@
 package commands
 
 import (
+	"github.com/giolekva/pcloud/core/kg/app"
 	"github.com/giolekva/pcloud/core/kg/log"
 	"github.com/giolekva/pcloud/core/kg/model"
 	"github.com/giolekva/pcloud/core/kg/server"
+	"github.com/giolekva/pcloud/core/kg/store/memory"
 	"github.com/spf13/cobra"
 )
 
@@ -35,8 +37,11 @@
 	})
 	config := model.NewConfig()
 
-	grpcServer := server.NewGRPCServer(logger, config, nil)
-	httpServer := server.NewHTTPServer(logger, config, nil)
+	st := memory.New()
+	a := app.NewApp(st, config, logger)
+
+	grpcServer := server.NewGRPCServer(logger, config, a)
+	httpServer := server.NewHTTPServer(logger, config, a)
 
 	servers := server.New(logger)
 	servers.AddServers(grpcServer)
diff --git a/core/kg/common/interfaces.go b/core/kg/common/interfaces.go
index daee20e..4966c13 100644
--- a/core/kg/common/interfaces.go
+++ b/core/kg/common/interfaces.go
@@ -14,4 +14,6 @@
 
 type AppIface interface {
 	GetUser(userID string) (*model.User, error)
+	CreateUser(user *model.User) (*model.User, error)
+	GetUsers(page, perPage int) ([]*model.User, error)
 }
diff --git a/core/kg/go.mod b/core/kg/go.mod
index f6bf84a..05af918 100644
--- a/core/kg/go.mod
+++ b/core/kg/go.mod
@@ -11,7 +11,7 @@
 	github.com/spf13/cobra v1.1.3
 	github.com/stretchr/testify v1.7.0
 	go.uber.org/zap v1.16.0
-	golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 // indirect
+	golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
 	golang.org/x/tools v0.0.0-20200313205530-4303120df7d8 // indirect
 	google.golang.org/grpc v1.35.0
 	google.golang.org/protobuf v1.25.0
diff --git a/core/kg/go.sum b/core/kg/go.sum
index 37a24ce..1d580f2 100644
--- a/core/kg/go.sum
+++ b/core/kg/go.sum
@@ -229,6 +229,8 @@
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
+golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -264,8 +266,9 @@
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -288,12 +291,14 @@
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/core/kg/model/config.go b/core/kg/model/config.go
index fd8c034..cc556ff 100644
--- a/core/kg/model/config.go
+++ b/core/kg/model/config.go
@@ -17,6 +17,7 @@
 	SQL  SQLConfig
 	HTTP HTTPConfig
 	GRPC GRPCConfig
+	App  AppConfig
 }
 
 func NewConfig() *Config {
@@ -80,3 +81,15 @@
 		s.Port = defaultGRPCPort
 	}
 }
+
+type AppConfig struct {
+	EnableSignUp *bool
+}
+
+func (s *AppConfig) SetDefaults() {
+	if s.EnableSignUp == nil {
+		s.EnableSignUp = NewBool(true)
+	}
+}
+
+func NewBool(b bool) *bool { return &b }
diff --git a/core/kg/model/user.go b/core/kg/model/user.go
index dd34110..04357d1 100644
--- a/core/kg/model/user.go
+++ b/core/kg/model/user.go
@@ -45,11 +45,35 @@
 	return nil
 }
 
+// IsValidInput validates the user input and returns an error
+func (u *User) IsValidInput() error {
+	if !isValidUsername(u.Username) {
+		return invalidUserError("username", u.ID)
+	}
+
+	return nil
+}
+
+// Clone clones the object
 func (u *User) Clone() *User {
 	user := *u
 	return &user
 }
 
+// SanitizeInput removes input data from the user object that is not user controlled
+func (u *User) SanitizeInput() {
+	u.ID = ""
+	u.CreateAt = 0
+	u.UpdateAt = 0
+	u.DeleteAt = 0
+	u.LastPasswordUpdate = 0
+}
+
+// SanitizeOutput removes output data from the user object that is not user controlled
+func (u *User) SanitizeOutput() {
+	u.Password = ""
+}
+
 func isValidID(value string) bool {
 	if len(value) != 26 {
 		return false
diff --git a/core/kg/rpc/interfaces.go b/core/kg/rpc/interfaces.go
deleted file mode 100644
index 0cd072a..0000000
--- a/core/kg/rpc/interfaces.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package rpc
-
-import "github.com/giolekva/pcloud/core/kg/model"
-
-type appIface interface {
-	GetUser(userID string) (*model.User, error)
-}
diff --git a/core/kg/server/grpc_server.go b/core/kg/server/grpc_server.go
index 20aed55..af0b945 100644
--- a/core/kg/server/grpc_server.go
+++ b/core/kg/server/grpc_server.go
@@ -5,11 +5,11 @@
 	"net"
 	"os"
 
+	"github.com/giolekva/pcloud/core/kg/api/rpc"
 	"github.com/giolekva/pcloud/core/kg/common"
 	"github.com/giolekva/pcloud/core/kg/log"
 	"github.com/giolekva/pcloud/core/kg/model"
 	"github.com/giolekva/pcloud/core/kg/model/proto"
-	"github.com/giolekva/pcloud/core/kg/rpc"
 	"google.golang.org/grpc"
 )
 
diff --git a/core/kg/server/http_server.go b/core/kg/server/http_server.go
index 6b07914..932d53b 100644
--- a/core/kg/server/http_server.go
+++ b/core/kg/server/http_server.go
@@ -8,53 +8,53 @@
 	"os"
 	"time"
 
+	"github.com/giolekva/pcloud/core/kg/api/rest"
 	"github.com/giolekva/pcloud/core/kg/common"
 	"github.com/giolekva/pcloud/core/kg/log"
 	"github.com/giolekva/pcloud/core/kg/model"
-	"github.com/giolekva/pcloud/core/kg/store"
 	"github.com/gorilla/mux"
 )
 
 // HTTPServerImpl http server implementation
 type HTTPServerImpl struct {
-	Log    common.LoggerIface
-	srv    *http.Server
-	root   *mux.Router
-	config *model.Config
-	store  store.Store
+	srv     *http.Server
+	routers *rest.Router
+	config  *model.Config
+	app     common.AppIface
+	logger  common.LoggerIface
 }
 
 var _ Server = &HTTPServerImpl{}
 
 // NewHTTPServer creates new HTTP Server
-func NewHTTPServer(logger common.LoggerIface, config *model.Config, store store.Store) Server {
+func NewHTTPServer(logger common.LoggerIface, config *model.Config, app common.AppIface) Server {
 	a := &HTTPServerImpl{
-		Log:    logger,
-		root:   mux.NewRouter(),
-		config: config,
-		store:  store,
+		logger:  logger,
+		routers: rest.NewRouter(mux.NewRouter(), app, logger),
+		config:  config,
+		app:     app,
 	}
 
 	pwd, _ := os.Getwd()
-	a.Log.Info("HTTP server current working", log.String("directory", pwd))
+	a.logger.Info("HTTP server current working", log.String("directory", pwd))
 	return a
 }
 
 // Start method starts a http server
 func (a *HTTPServerImpl) Start() error {
-	a.Log.Info("Starting HTTP Server...")
+	a.logger.Info("Starting HTTP Server...")
 
 	a.srv = &http.Server{
 		Addr:         fmt.Sprintf("%s:%d", a.config.HTTP.Host, a.config.HTTP.Port),
-		Handler:      a.root,
+		Handler:      a.routers.Root,
 		ReadTimeout:  time.Duration(a.config.HTTP.ReadTimeout) * time.Second,
 		WriteTimeout: time.Duration(a.config.HTTP.WriteTimeout) * time.Second,
 		IdleTimeout:  time.Duration(a.config.HTTP.IdleTimeout) * time.Second,
 	}
 
-	a.Log.Info("HTTP Server is listening on", log.Int("port", a.config.HTTP.Port))
+	a.logger.Info("HTTP Server is listening on", log.Int("port", a.config.HTTP.Port))
 	if err := a.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
-		a.Log.Error("Failed to listen and serve: %v", log.Err(err))
+		a.logger.Error("Failed to listen and serve: %v", log.Err(err))
 		return err
 	}
 	return nil
@@ -62,7 +62,7 @@
 
 // Shutdown method shuts http server down
 func (a *HTTPServerImpl) Shutdown() error {
-	a.Log.Info("Stopping HTTP Server...")
+	a.logger.Info("Stopping HTTP Server...")
 	if a.srv == nil {
 		return errors.New("No http server present")
 	}
@@ -70,11 +70,11 @@
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
 	defer cancel()
 	if err := a.srv.Shutdown(ctx); err != nil {
-		a.Log.Error("Unable to shutdown server", log.Err(err))
+		a.logger.Error("Unable to shutdown server", log.Err(err))
 	}
 
 	// a.srv.Close()
 	// a.srv = nil
-	a.Log.Info("HTTP Server stopped")
+	a.logger.Info("HTTP Server stopped")
 	return nil
 }
diff --git a/core/kg/server/servers_mock.go b/core/kg/server/servers_mock.go
index 2694329..ab20e55 100644
--- a/core/kg/server/servers_mock.go
+++ b/core/kg/server/servers_mock.go
@@ -24,7 +24,7 @@
 	config := model.NewConfig()
 	logger := &log.NoOpLogger{}
 	grpcServer := NewGRPCServer(logger, config, app)
-	httpServer := NewHTTPServer(logger, config, nil)
+	httpServer := NewHTTPServer(logger, config, app)
 	ts := &MockServer{
 		App:     app,
 		Servers: []Server{grpcServer, httpServer},
diff --git a/core/kg/store/memory/user_store.go b/core/kg/store/memory/user_store.go
index 1805845..c551131 100644
--- a/core/kg/store/memory/user_store.go
+++ b/core/kg/store/memory/user_store.go
@@ -63,3 +63,24 @@
 	}
 	return users, nil
 }
+
+func (us *memoryUserStore) GetAllWithOptions(page, perPage int) ([]*model.User, error) {
+	us.mutex.RLock()
+	defer us.mutex.RUnlock()
+
+	// Not an ideal way to implement a pagination over a map but memory store is for testing anyway.
+	v := make([]*model.User, 0, len(us.users))
+	for _, value := range us.users {
+		v = append(v, value)
+	}
+	users := make([]*model.User, 0, perPage)
+	startIndex := page * perPage
+	for i := startIndex; i < startIndex+perPage && i < len(us.users); i++ {
+		users = append(users, v[i].Clone())
+	}
+	return users, nil
+}
+
+func (us *memoryUserStore) Count() (int64, error) {
+	return int64(us.maxID) - 1, nil
+}
diff --git a/core/kg/store/sqlstore/user_store.go b/core/kg/store/sqlstore/user_store.go
index f8a2d58..34ca081 100644
--- a/core/kg/store/sqlstore/user_store.go
+++ b/core/kg/store/sqlstore/user_store.go
@@ -49,3 +49,11 @@
 func (us SqlUserStore) GetAll() ([]*model.User, error) {
 	return nil, nil
 }
+
+func (us SqlUserStore) Count() (int64, error) {
+	return 0, nil
+}
+
+func (us SqlUserStore) GetAllWithOptions(page, perPage int) ([]*model.User, error) {
+	return nil, nil
+}
diff --git a/core/kg/store/store.go b/core/kg/store/store.go
index d291e4a..d8ca29d 100644
--- a/core/kg/store/store.go
+++ b/core/kg/store/store.go
@@ -12,4 +12,6 @@
 	Save(user *model.User) (*model.User, error)
 	Get(id string) (*model.User, error)
 	GetAll() ([]*model.User, error)
+	Count() (int64, error)
+	GetAllWithOptions(page, perPage int) ([]*model.User, error)
 }