-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): add access layer for get bridge api
- Loading branch information
1 parent
1c0d1e2
commit 6cbb33a
Showing
8 changed files
with
280 additions
and
2 deletions.
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
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
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
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,26 @@ | ||
package handlers | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/shivanshkc/rosenbridge/src/configs" | ||
"github.com/shivanshkc/rosenbridge/src/core" | ||
"github.com/shivanshkc/rosenbridge/src/logger" | ||
"github.com/shivanshkc/rosenbridge/src/utils/httputils" | ||
) | ||
|
||
// BasicHandler serves the base route, which is usually "/api". | ||
func BasicHandler(writer http.ResponseWriter, req *http.Request) { | ||
// Prerequisites. | ||
ctx, conf, log := req.Context(), configs.Get(), logger.Get() | ||
|
||
// Response body. | ||
body := map[string]interface{}{ | ||
"code": core.CodeOK, | ||
"name": conf.Application.Name, | ||
"version": conf.Application.Version, | ||
} | ||
|
||
response := &httputils.ResponseDTO{Status: http.StatusOK, Body: body} | ||
httputils.WriteAndLog(ctx, writer, response, 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,120 @@ | ||
package handlers | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/shivanshkc/rosenbridge/src/configs" | ||
"github.com/shivanshkc/rosenbridge/src/core" | ||
"github.com/shivanshkc/rosenbridge/src/logger" | ||
"github.com/shivanshkc/rosenbridge/src/utils/errutils" | ||
"github.com/shivanshkc/rosenbridge/src/utils/httputils" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
// GetBridgeHandler serves the "Get Bridge" API of Rosenbridge. | ||
func GetBridgeHandler(writer http.ResponseWriter, req *http.Request) { | ||
// Prerequisites. | ||
ctx, conf, log := req.Context(), configs.Get(), logger.Get() | ||
|
||
// Reading and validating client ID. | ||
clientID := mux.Vars(req)["client_id"] | ||
if err := checkClientID(clientID); err != nil { | ||
httputils.WriteErrAndLog(ctx, writer, errutils.BadRequest().WithReasonError(err), log) | ||
return | ||
} | ||
|
||
// Input for the core function. | ||
createBridgeParams := &core.CreateBridgeParams{ | ||
ClientID: clientID, | ||
Writer: writer, | ||
Request: req, | ||
BridgeLimitTotal: &conf.Application.BridgeLimitTotal, | ||
BridgeLimitPerClient: &conf.Application.BridgeLimitPerClient, | ||
} | ||
|
||
// Calling the core function and obtaining the bridge. | ||
bridge, err := core.CreateBridge(ctx, createBridgeParams) | ||
if err != nil { | ||
log.Error(ctx, &logger.Entry{Payload: fmt.Errorf("error in core.GetBridge call: %w", err)}) | ||
httputils.WriteErrAndLog(ctx, writer, err, log) | ||
return | ||
} | ||
|
||
// Setting up the message handler for the bridge. | ||
bridge.SetMessageHandler(func(req *core.BridgeMessage) { | ||
ctx := context.Background() | ||
|
||
// Creating the bridge message. This will be the reply to the client. | ||
bMessage := &core.BridgeMessage{ | ||
Type: core.MessageOutgoingRes, | ||
RequestID: req.RequestID, | ||
} | ||
|
||
// This method will help send the validation errors through the bridge. | ||
sendValidationErr := func(err error) { | ||
errHTTP := errutils.BadRequest().WithReasonError(err) | ||
// Using the error code and reason as the message body. | ||
bMessage.Body = &core.CodeAndReason{Code: errHTTP.Code, Reason: errHTTP.Reason} | ||
// Sending back the error. | ||
if err := bridge.SendMessage(ctx, bMessage); err != nil { | ||
log.Error(ctx, &logger.Entry{Payload: fmt.Errorf("error in bridge.SendMessage call: %w", err)}) | ||
} | ||
} | ||
|
||
// Validating the bridge message. | ||
if err := checkBridgeMessage(req); err != nil { | ||
sendValidationErr(err) | ||
return | ||
} | ||
|
||
// Getting the OutgoingMessageReq from the message. | ||
outMessageReq, err := interface2OutgoingMessageReq(req.Body) | ||
if err != nil { | ||
sendValidationErr(err) | ||
return | ||
} | ||
|
||
// Validating the OutgoingMessageReq. | ||
if err := checkOutgoingMessageReq(outMessageReq); err != nil { | ||
sendValidationErr(err) | ||
return | ||
} | ||
|
||
// Forming the input for the core function. | ||
input := &core.PostMessageParams{ | ||
OutgoingMessageReq: outMessageReq, | ||
RequestID: req.RequestID, | ||
ClientID: clientID, | ||
} | ||
|
||
// Calling the core function. | ||
response, err := core.PostMessage(ctx, input) | ||
if err != nil { | ||
// Converting the error to HTTP error to get code and reason. | ||
errHTTP := errutils.ToHTTPError(err) | ||
// Using the error code and reason as the message body. | ||
bMessage.Body = &core.CodeAndReason{Code: errHTTP.Code, Reason: errHTTP.Reason} | ||
|
||
// Sending back the error. | ||
if err := bridge.SendMessage(ctx, bMessage); err != nil { | ||
log.Error(ctx, &logger.Entry{Payload: fmt.Errorf("error in bridge.SendMessage call: %w", err)}) | ||
} | ||
|
||
// Logging the error at our end. | ||
log.Error(ctx, &logger.Entry{Payload: fmt.Errorf("error in core.PostMessage call: %w", err)}) | ||
return | ||
} | ||
|
||
// Using the response as the message body. | ||
bMessage.Body = response | ||
|
||
// Sending back the response. | ||
if err := bridge.SendMessage(ctx, bMessage); err != nil { | ||
log.Error(ctx, &logger.Entry{Payload: fmt.Errorf("error in bridge.SendMessage call: %w", err)}) | ||
return | ||
} | ||
}) | ||
} |
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,25 @@ | ||
package handlers | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/shivanshkc/rosenbridge/src/core" | ||
) | ||
|
||
// interface2OutgoingMessageReq converts the provided interface value to *core.OutgoingMessageReq. | ||
func interface2OutgoingMessageReq(value interface{}) (*core.OutgoingMessageReq, error) { | ||
// Marshalling the value to byte slice for later unmarshalling. | ||
valueBytes, err := json.Marshal(value) | ||
if err != nil { | ||
return nil, fmt.Errorf("error in json.Marshal call: %w", err) | ||
} | ||
|
||
// Unmarshalling the value bytes into a *core.OutgoingMessageReq type. | ||
omr := &core.OutgoingMessageReq{} | ||
if err := json.Unmarshal(valueBytes, omr); err != nil { | ||
return nil, fmt.Errorf("error in json.Unmarshal call: %w", err) | ||
} | ||
|
||
return omr, nil | ||
} |
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 handlers | ||
|
||
import ( | ||
"github.com/shivanshkc/rosenbridge/src/core" | ||
) | ||
|
||
// checkClientID checks if the provided client ID is valid. | ||
func checkClientID(clientID string) error { | ||
clientIDLen := len(clientID) | ||
// Validating the length of client ID. | ||
if clientIDLen < clientIDMinLen || clientIDLen > clientIDMaxLen { | ||
return errClientID | ||
} | ||
// Validating format of client ID. | ||
if !clientIDRegexp.MatchString(clientID) { | ||
return errClientID | ||
} | ||
return nil | ||
} | ||
|
||
// checkBridgeMessage checks if the provided *core.BridgeMessage is valid. | ||
func checkBridgeMessage(message *core.BridgeMessage) error { | ||
if message.Body == nil { | ||
return errEmptyBridgeMessageBody | ||
} | ||
return nil | ||
} | ||
|
||
// checkOutgoingMessageReq validates an "Outgoing Message Request". | ||
func checkOutgoingMessageReq(req *core.OutgoingMessageReq) error { | ||
if err := checkReceiverIDs(req.ReceiverIDs); err != nil { | ||
return err | ||
} | ||
if err := checkPersist(req.Persist); err != nil { | ||
return err | ||
} | ||
// No error was found. Returning nil. | ||
return nil | ||
} | ||
|
||
// checkReceiverIDs checks if the provided receiver IDs are all valid. | ||
// | ||
// Note that the Receiver ID is the same thing as Client ID. | ||
func checkReceiverIDs(receiverIDs []string) error { | ||
if len(receiverIDs) == 0 { | ||
return errEmptyReceiverIDs | ||
} | ||
for _, rec := range receiverIDs { | ||
if err := checkClientID(rec); err != nil { | ||
return errReceiverID | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// checkPersist checks if the provided "persist" value is valid. | ||
func checkPersist(persist string) error { | ||
if persist != core.PersistTrue && persist != core.PersistFalse && persist != core.PersistIfError { | ||
return errPersist | ||
} | ||
return nil | ||
} |
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,36 @@ | ||
package handlers | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"regexp" | ||
|
||
"github.com/shivanshkc/rosenbridge/src/core" | ||
) | ||
|
||
// Validation params. | ||
const ( | ||
clientIDMinLen = 1 | ||
clientIDMaxLen = 100 | ||
) | ||
|
||
// Validation params that can't be Go constants. | ||
var ( | ||
clientIDRegexp = regexp.MustCompile("^[a-zA-Z0-9-@._]*$") | ||
) | ||
|
||
// All validation errors. | ||
var ( | ||
errClientID = fmt.Errorf("client id length should be between %d and %d, and should match regex %s", | ||
clientIDMinLen, clientIDMaxLen, clientIDRegexp.String()) | ||
|
||
errEmptyReceiverIDs = errors.New("receiver ids cannot be empty") | ||
|
||
errReceiverID = fmt.Errorf("receiver id length should be between %d and %d, and should match regex %s", | ||
clientIDMinLen, clientIDMaxLen, clientIDRegexp.String()) | ||
|
||
errPersist = fmt.Errorf("persist must be one of: %s, %s and %s", | ||
core.PersistTrue, core.PersistFalse, core.PersistIfError) | ||
|
||
errEmptyBridgeMessageBody = errors.New("bridge message body cannot be empty") | ||
) |