-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from slackhq/go-pre-interview-mac
Golang pre interview code v1
- Loading branch information
Showing
21 changed files
with
990 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,4 +28,4 @@ jobs: | |
|
||
- name: Run 🚀 | ||
working-directory: ./go | ||
run: ./run_test.sh | ||
run: ./setup.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
main | ||
datastore.db | ||
server.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
|
||
"go-pre-interview/model" | ||
"go-pre-interview/server" | ||
) | ||
|
||
// API implements the server dispatch interface | ||
type API struct { | ||
datastore *model.Datastore | ||
} | ||
|
||
// Init bootstraps the API | ||
func Init(datastore *model.Datastore) *API { | ||
return &API{datastore} | ||
} | ||
|
||
type handlerFunc = func() error | ||
|
||
// Dispatch executes a command in the given session | ||
func (api *API) Dispatch(ctx context.Context, requestType string, requestID int, payload []byte) { | ||
|
||
session := ctx.Value(server.SessionKey).(Session) | ||
if session == nil { | ||
panic("no session in context") | ||
} | ||
err := api.dispatch(session, requestType, requestID, payload) | ||
if err != nil { | ||
session.Send(NewErrorResponse(requestID, err.Error())) | ||
} | ||
} | ||
|
||
func (api *API) dispatch(session Session, requestType string, requestID int, payload []byte) error { | ||
|
||
log.Printf("got api request %s %s", requestType, payload) | ||
|
||
dispatchTable := map[string]handlerFunc{ | ||
"test.create": func() error { return api.CreateHandler(session, requestID, payload) }, | ||
"test.info": func() error { return api.infoHandler(session, requestID, payload) }, | ||
"test.broadcast": func() error { return api.broadcastHandler(session, requestID, payload) }, | ||
} | ||
|
||
handler, found := dispatchTable[requestType] | ||
if !found { | ||
return fmt.Errorf("unimplemented API method %s", requestType) | ||
} | ||
|
||
switch requestType { | ||
case "test.create": | ||
break | ||
default: | ||
if session.ID() == 0 { | ||
return errCreatingTest | ||
} | ||
} | ||
|
||
return handler() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package api | ||
|
||
import "encoding/json" | ||
|
||
// Response is sent for all API success responses without additional | ||
// data and for all errors | ||
type Response struct { | ||
OK bool `json:"ok"` | ||
ReplyTo int `json:"reply_to"` | ||
Error string `json:"error,omitempty"` | ||
} | ||
|
||
// NewOKResponse creates a JSON encoded ok response | ||
func NewOKResponse(requestID int) []byte { | ||
okResponse := Response{OK: true, ReplyTo: requestID} | ||
data, err := json.Marshal(okResponse) | ||
if err != nil { | ||
panic("response unable to marshal to json") | ||
} | ||
return data | ||
} | ||
|
||
// NewErrorResponse creates a JSON encoded error response wrapping the given message | ||
func NewErrorResponse(requestID int, error string) []byte { | ||
errResponse := Response{OK: false, ReplyTo: requestID, Error: error} | ||
data, err := json.Marshal(errResponse) | ||
if err != nil { | ||
panic("response unable to marshal to json") | ||
} | ||
return data | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package api | ||
|
||
import "errors" | ||
|
||
var ( | ||
errInvalidRequest = errors.New("invalid_request") | ||
errCreatingTest = errors.New("error_creating_test") | ||
errMissingID = errors.New("missing_id") | ||
errTestNotFound = errors.New("test_not_found") | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package api | ||
|
||
// Session wraps a session | ||
type Session interface { | ||
// ID returns the id for the current session | ||
ID() int | ||
|
||
// SetID sets the id in the current session | ||
SetID(id int) | ||
|
||
// Send a reply on the current connection | ||
Send(message []byte) | ||
|
||
// Subscribe the given ID | ||
Subscribe(sessionid, id int) | ||
|
||
// Broadcast a message. | ||
Broadcast(id int, message []byte) []int | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package api | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
"go-pre-interview/model" | ||
) | ||
|
||
type apiResponse struct { | ||
OK bool `json:"ok"` | ||
ReplyTO int `json:"reply_to"` | ||
TestTable *model.TestTable `json:"test"` | ||
} | ||
|
||
type CreateRequest struct { | ||
Name string `json:"name"` | ||
RandomString string `json:"random_string"` | ||
} | ||
|
||
type broadcastRequest struct { | ||
ID int `json:"test_id"` | ||
} | ||
|
||
type Message struct { | ||
Type string `json:"type"` | ||
RequestId int `json:"requestId"` | ||
Test int `json:"test"` | ||
} | ||
|
||
func (api *API) CreateHandler(session Session, requestID int, payload []byte) error { | ||
var request CreateRequest | ||
err := json.Unmarshal(payload, &request) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
test := api.datastore.TestTableStore.Create(request.Name, request.RandomString) | ||
|
||
session.SetID(test.ID) | ||
|
||
response := apiResponse{true, requestID, test} | ||
data, err := json.Marshal(response) | ||
if err != nil { | ||
return err | ||
} | ||
session.Send(data) | ||
|
||
return nil | ||
} | ||
|
||
type infoRequest struct { | ||
TestId int `json:"test_id"` | ||
} | ||
|
||
func (api *API) infoHandler(session Session, requestID int, payload []byte) error { | ||
var request infoRequest | ||
err := json.Unmarshal(payload, &request) | ||
if err != nil { | ||
return errInvalidRequest | ||
} | ||
|
||
if request.TestId == 0 { | ||
return errMissingID | ||
} | ||
|
||
test := api.datastore.TestTableStore.GetByID(request.TestId) | ||
if test == nil { | ||
return errTestNotFound | ||
} | ||
|
||
response := apiResponse{true, requestID, test} | ||
data, err := json.Marshal(response) | ||
if err != nil { | ||
return err | ||
} | ||
session.Send(data) | ||
|
||
return nil | ||
} | ||
|
||
func newMessage(sessionId, testId int) []byte { | ||
msg := Message{"message", sessionId, testId} | ||
data, err := json.Marshal(msg) | ||
if err != nil { | ||
panic("unable to marshal json") | ||
} | ||
return data | ||
} | ||
|
||
func (api *API) broadcastHandler(session Session, requestID int, payload []byte) error { | ||
var request broadcastRequest | ||
err := json.Unmarshal(payload, &request) | ||
if err != nil { | ||
return errInvalidRequest | ||
} | ||
|
||
if requestID == 0 { | ||
return errMissingID | ||
} | ||
|
||
test := api.datastore.TestTableStore.GetByID(request.ID) | ||
if test == nil { | ||
return errTestNotFound | ||
} | ||
|
||
session.Subscribe(request.ID, session.ID()) | ||
session.Broadcast(request.ID, newMessage(session.ID(), request.ID)) | ||
|
||
response := apiResponse{OK: true, ReplyTO: requestID, TestTable: test} | ||
data, err := json.Marshal(response) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
session.Send(data) | ||
|
||
return nil | ||
} |
Oops, something went wrong.