docs: reorder & testing
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package backendclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"galaxy/gateway/internal/downstream"
|
||||
ordermodel "galaxy/model/order"
|
||||
reportmodel "galaxy/model/report"
|
||||
gamerest "galaxy/model/rest"
|
||||
"galaxy/transcoder"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// ExecuteGameCommand routes one authenticated `user.games.*` command
|
||||
// into backend's `/api/v1/user/games/{game_id}/*` endpoints. Command
|
||||
// and order requests transcode the typed FB-payload into the JSON
|
||||
// shape the engine expects (a `gamerest.Command` with empty actor —
|
||||
// backend rebinds the actor from the runtime player mapping). Report
|
||||
// requests transcode the response Report from JSON back to FB.
|
||||
func (c *RESTClient) ExecuteGameCommand(ctx context.Context, command downstream.AuthenticatedCommand) (downstream.UnaryResult, error) {
|
||||
if c == nil || c.httpClient == nil {
|
||||
return downstream.UnaryResult{}, errors.New("backendclient: execute game command: nil client")
|
||||
}
|
||||
if ctx == nil {
|
||||
return downstream.UnaryResult{}, errors.New("backendclient: execute game command: nil context")
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return downstream.UnaryResult{}, err
|
||||
}
|
||||
if strings.TrimSpace(command.UserID) == "" {
|
||||
return downstream.UnaryResult{}, errors.New("backendclient: execute game command: user_id must not be empty")
|
||||
}
|
||||
|
||||
switch command.MessageType {
|
||||
case ordermodel.MessageTypeUserGamesCommand:
|
||||
req, err := transcoder.PayloadToUserGamesCommand(command.PayloadBytes)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute game command %q: %w", command.MessageType, err)
|
||||
}
|
||||
return c.executeUserGamesCommand(ctx, command.UserID, req)
|
||||
case ordermodel.MessageTypeUserGamesOrder:
|
||||
req, err := transcoder.PayloadToUserGamesOrder(command.PayloadBytes)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute game command %q: %w", command.MessageType, err)
|
||||
}
|
||||
return c.executeUserGamesOrder(ctx, command.UserID, req)
|
||||
case reportmodel.MessageTypeUserGamesReport:
|
||||
req, err := transcoder.PayloadToGameReportRequest(command.PayloadBytes)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute game command %q: %w", command.MessageType, err)
|
||||
}
|
||||
return c.executeUserGamesReport(ctx, command.UserID, req)
|
||||
default:
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute game command: unsupported message type %q", command.MessageType)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RESTClient) executeUserGamesCommand(ctx context.Context, userID string, req *ordermodel.UserGamesCommand) (downstream.UnaryResult, error) {
|
||||
if req.GameID == uuid.Nil {
|
||||
return downstream.UnaryResult{}, errors.New("execute user.games.command: game_id must not be empty")
|
||||
}
|
||||
body, err := buildEngineCommandBody(req.Commands)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("execute user.games.command: %w", err)
|
||||
}
|
||||
target := c.baseURL + "/api/v1/user/games/" + url.PathEscape(req.GameID.String()) + "/commands"
|
||||
respBody, status, err := c.do(ctx, http.MethodPost, target, userID, body)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("execute user.games.command: %w", err)
|
||||
}
|
||||
return projectUserGamesAckResponse(status, respBody, transcoder.EmptyUserGamesCommandResponsePayload)
|
||||
}
|
||||
|
||||
func (c *RESTClient) executeUserGamesOrder(ctx context.Context, userID string, req *ordermodel.UserGamesOrder) (downstream.UnaryResult, error) {
|
||||
if req.GameID == uuid.Nil {
|
||||
return downstream.UnaryResult{}, errors.New("execute user.games.order: game_id must not be empty")
|
||||
}
|
||||
body, err := buildEngineCommandBody(req.Commands)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("execute user.games.order: %w", err)
|
||||
}
|
||||
target := c.baseURL + "/api/v1/user/games/" + url.PathEscape(req.GameID.String()) + "/orders"
|
||||
respBody, status, err := c.do(ctx, http.MethodPost, target, userID, body)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("execute user.games.order: %w", err)
|
||||
}
|
||||
return projectUserGamesAckResponse(status, respBody, transcoder.EmptyUserGamesOrderResponsePayload)
|
||||
}
|
||||
|
||||
func (c *RESTClient) executeUserGamesReport(ctx context.Context, userID string, req *reportmodel.GameReportRequest) (downstream.UnaryResult, error) {
|
||||
if req.GameID == uuid.Nil {
|
||||
return downstream.UnaryResult{}, errors.New("execute user.games.report: game_id must not be empty")
|
||||
}
|
||||
target := fmt.Sprintf("%s/api/v1/user/games/%s/reports/%d", c.baseURL, url.PathEscape(req.GameID.String()), req.Turn)
|
||||
respBody, status, err := c.do(ctx, http.MethodGet, target, userID, nil)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("execute user.games.report: %w", err)
|
||||
}
|
||||
return projectUserGamesReportResponse(status, respBody)
|
||||
}
|
||||
|
||||
// buildEngineCommandBody serialises a slice of typed commands into the
|
||||
// JSON shape expected by backend's command/order handlers (a
|
||||
// `gamerest.Command` with the actor field left empty — backend rebinds
|
||||
// it from the runtime player mapping before forwarding to the engine).
|
||||
func buildEngineCommandBody(commands []ordermodel.DecodableCommand) (gamerest.Command, error) {
|
||||
raw := make([]json.RawMessage, len(commands))
|
||||
for i, cmd := range commands {
|
||||
encoded, err := json.Marshal(cmd)
|
||||
if err != nil {
|
||||
return gamerest.Command{}, fmt.Errorf("encode command %d: %w", i, err)
|
||||
}
|
||||
raw[i] = encoded
|
||||
}
|
||||
return gamerest.Command{Actor: "", Commands: raw}, nil
|
||||
}
|
||||
|
||||
// projectUserGamesAckResponse turns a backend response for command /
|
||||
// order routes into a UnaryResult. Engine returns 204 on success, so
|
||||
// any 2xx status is treated as ok and answered with the empty typed
|
||||
// FB envelope produced by ackBuilder.
|
||||
func projectUserGamesAckResponse(statusCode int, payload []byte, ackBuilder func() []byte) (downstream.UnaryResult, error) {
|
||||
switch {
|
||||
case statusCode >= 200 && statusCode < 300:
|
||||
return downstream.UnaryResult{
|
||||
ResultCode: userCommandResultCodeOK,
|
||||
PayloadBytes: ackBuilder(),
|
||||
}, nil
|
||||
case statusCode == http.StatusServiceUnavailable:
|
||||
return downstream.UnaryResult{}, downstream.ErrDownstreamUnavailable
|
||||
case statusCode >= 400 && statusCode <= 599:
|
||||
return projectUserBackendError(statusCode, payload)
|
||||
default:
|
||||
return downstream.UnaryResult{}, fmt.Errorf("unexpected HTTP status %d", statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
// projectUserGamesReportResponse decodes the engine's Report JSON
|
||||
// payload (forwarded verbatim by backend) and re-encodes it as a
|
||||
// FlatBuffers Report for the signed-gRPC client.
|
||||
func projectUserGamesReportResponse(statusCode int, payload []byte) (downstream.UnaryResult, error) {
|
||||
switch {
|
||||
case statusCode == http.StatusOK:
|
||||
var report reportmodel.Report
|
||||
if err := json.Unmarshal(payload, &report); err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("decode engine report: %w", err)
|
||||
}
|
||||
encoded, err := transcoder.ReportToPayload(&report)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("encode report payload: %w", err)
|
||||
}
|
||||
return downstream.UnaryResult{
|
||||
ResultCode: userCommandResultCodeOK,
|
||||
PayloadBytes: encoded,
|
||||
}, nil
|
||||
case statusCode == http.StatusServiceUnavailable:
|
||||
return downstream.UnaryResult{}, downstream.ErrDownstreamUnavailable
|
||||
case statusCode >= 400 && statusCode <= 599:
|
||||
return projectUserBackendError(statusCode, payload)
|
||||
default:
|
||||
return downstream.UnaryResult{}, fmt.Errorf("unexpected HTTP status %d", statusCode)
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,10 @@ func TestPushClientDeliversClientEventsAndAdvancesCursor(t *testing.T) {
|
||||
require.Eventually(t, func() bool { return svc.Service.SubscriberCount() == 1 }, time.Second, 10*time.Millisecond)
|
||||
|
||||
userID := uuid.New()
|
||||
require.NoError(t, svc.Service.PublishClientEvent(context.Background(), userID, nil, "lobby.invite.received", map[string]any{"x": 1.0}, "evt-1", "req-1", "trace-1"))
|
||||
require.NoError(t, svc.Service.PublishClientEvent(context.Background(), userID, nil, backendpush.JSONEvent{
|
||||
EventKind: "lobby.invite.received",
|
||||
Payload: map[string]any{"x": 1.0},
|
||||
}, "evt-1", "req-1", "trace-1"))
|
||||
|
||||
select {
|
||||
case got := <-out:
|
||||
|
||||
@@ -98,45 +98,6 @@ func (c *RESTClient) LookupSession(ctx context.Context, deviceSessionID string)
|
||||
}
|
||||
}
|
||||
|
||||
// RevokeSession asks backend to revoke a single device session by id.
|
||||
func (c *RESTClient) RevokeSession(ctx context.Context, deviceSessionID string) error {
|
||||
if strings.TrimSpace(deviceSessionID) == "" {
|
||||
return errors.New("backendclient: revoke session: device_session_id must not be empty")
|
||||
}
|
||||
target := c.baseURL + "/api/v1/internal/sessions/" + url.PathEscape(deviceSessionID) + "/revoke"
|
||||
_, status, err := c.do(ctx, http.MethodPost, target, "", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("backendclient: revoke session: %w", err)
|
||||
}
|
||||
if status == http.StatusOK || status == http.StatusNoContent {
|
||||
return nil
|
||||
}
|
||||
if status == http.StatusNotFound {
|
||||
return errSessionNotFound()
|
||||
}
|
||||
return fmt.Errorf("backendclient: revoke session: unexpected HTTP status %d", status)
|
||||
}
|
||||
|
||||
// RevokeAllSessionsForUser asks backend to revoke every active device
|
||||
// session belonging to userID.
|
||||
func (c *RESTClient) RevokeAllSessionsForUser(ctx context.Context, userID string) error {
|
||||
if strings.TrimSpace(userID) == "" {
|
||||
return errors.New("backendclient: revoke-all sessions: user_id must not be empty")
|
||||
}
|
||||
target := c.baseURL + "/api/v1/internal/sessions/users/" + url.PathEscape(userID) + "/revoke-all"
|
||||
_, status, err := c.do(ctx, http.MethodPost, target, "", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("backendclient: revoke-all sessions: %w", err)
|
||||
}
|
||||
if status == http.StatusOK || status == http.StatusNoContent {
|
||||
return nil
|
||||
}
|
||||
if status == http.StatusNotFound {
|
||||
return errSessionNotFound()
|
||||
}
|
||||
return fmt.Errorf("backendclient: revoke-all sessions: unexpected HTTP status %d", status)
|
||||
}
|
||||
|
||||
// do executes a JSON request and reads the response body. userID, when
|
||||
// non-empty, is sent as the X-User-Id header (required for `/api/v1/user/*`).
|
||||
func (c *RESTClient) do(ctx context.Context, method, target, userID string, body any) ([]byte, int, error) {
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"galaxy/gateway/internal/downstream"
|
||||
lobbymodel "galaxy/model/lobby"
|
||||
ordermodel "galaxy/model/order"
|
||||
reportmodel "galaxy/model/report"
|
||||
usermodel "galaxy/model/user"
|
||||
)
|
||||
|
||||
@@ -18,9 +20,12 @@ func UserRoutes(client *RESTClient) map[string]downstream.Client {
|
||||
target = userCommandClient{rest: client}
|
||||
}
|
||||
return map[string]downstream.Client{
|
||||
usermodel.MessageTypeGetMyAccount: target,
|
||||
usermodel.MessageTypeUpdateMyProfile: target,
|
||||
usermodel.MessageTypeUpdateMySettings: target,
|
||||
usermodel.MessageTypeGetMyAccount: target,
|
||||
usermodel.MessageTypeUpdateMyProfile: target,
|
||||
usermodel.MessageTypeUpdateMySettings: target,
|
||||
usermodel.MessageTypeListMySessions: target,
|
||||
usermodel.MessageTypeRevokeMySession: target,
|
||||
usermodel.MessageTypeRevokeAllMySessions: target,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +43,22 @@ func LobbyRoutes(client *RESTClient) map[string]downstream.Client {
|
||||
}
|
||||
}
|
||||
|
||||
// GameRoutes returns the authenticated `user.games.*` downstream
|
||||
// routes served by backend (which in turn forwards to the running
|
||||
// game engine container). When client is nil every route resolves to
|
||||
// a dependency-unavailable client.
|
||||
func GameRoutes(client *RESTClient) map[string]downstream.Client {
|
||||
target := downstream.Client(unavailableClient{})
|
||||
if client != nil {
|
||||
target = gameCommandClient{rest: client}
|
||||
}
|
||||
return map[string]downstream.Client{
|
||||
ordermodel.MessageTypeUserGamesCommand: target,
|
||||
ordermodel.MessageTypeUserGamesOrder: target,
|
||||
reportmodel.MessageTypeUserGamesReport: target,
|
||||
}
|
||||
}
|
||||
|
||||
type unavailableClient struct{}
|
||||
|
||||
func (unavailableClient) ExecuteCommand(context.Context, downstream.AuthenticatedCommand) (downstream.UnaryResult, error) {
|
||||
@@ -60,8 +81,17 @@ func (c lobbyCommandClient) ExecuteCommand(ctx context.Context, command downstre
|
||||
return c.rest.ExecuteLobbyCommand(ctx, command)
|
||||
}
|
||||
|
||||
type gameCommandClient struct {
|
||||
rest *RESTClient
|
||||
}
|
||||
|
||||
func (c gameCommandClient) ExecuteCommand(ctx context.Context, command downstream.AuthenticatedCommand) (downstream.UnaryResult, error) {
|
||||
return c.rest.ExecuteGameCommand(ctx, command)
|
||||
}
|
||||
|
||||
var (
|
||||
_ downstream.Client = unavailableClient{}
|
||||
_ downstream.Client = userCommandClient{}
|
||||
_ downstream.Client = lobbyCommandClient{}
|
||||
_ downstream.Client = gameCommandClient{}
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"galaxy/gateway/internal/downstream"
|
||||
@@ -59,6 +60,22 @@ func (c *RESTClient) ExecuteUserCommand(ctx context.Context, command downstream.
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute user command %q: %w", command.MessageType, err)
|
||||
}
|
||||
return c.executeUserAccountUpdateSettings(ctx, command.UserID, req)
|
||||
case usermodel.MessageTypeListMySessions:
|
||||
if _, err := transcoder.PayloadToListMySessionsRequest(command.PayloadBytes); err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute user command %q: %w", command.MessageType, err)
|
||||
}
|
||||
return c.executeUserSessionsList(ctx, command.UserID)
|
||||
case usermodel.MessageTypeRevokeMySession:
|
||||
req, err := transcoder.PayloadToRevokeMySessionRequest(command.PayloadBytes)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute user command %q: %w", command.MessageType, err)
|
||||
}
|
||||
return c.executeUserSessionsRevoke(ctx, command.UserID, req)
|
||||
case usermodel.MessageTypeRevokeAllMySessions:
|
||||
if _, err := transcoder.PayloadToRevokeAllMySessionsRequest(command.PayloadBytes); err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute user command %q: %w", command.MessageType, err)
|
||||
}
|
||||
return c.executeUserSessionsRevokeAll(ctx, command.UserID)
|
||||
default:
|
||||
return downstream.UnaryResult{}, fmt.Errorf("backendclient: execute user command: unsupported message type %q", command.MessageType)
|
||||
}
|
||||
@@ -88,6 +105,124 @@ func (c *RESTClient) executeUserAccountUpdateSettings(ctx context.Context, userI
|
||||
return projectUserResponse(status, body)
|
||||
}
|
||||
|
||||
func (c *RESTClient) executeUserSessionsList(ctx context.Context, userID string) (downstream.UnaryResult, error) {
|
||||
body, status, err := c.do(ctx, http.MethodGet, c.baseURL+"/api/v1/user/sessions", userID, nil)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("execute user.sessions.list: %w", err)
|
||||
}
|
||||
return projectUserSessionsListResponse(status, body)
|
||||
}
|
||||
|
||||
func (c *RESTClient) executeUserSessionsRevoke(ctx context.Context, userID string, req *usermodel.RevokeMySessionRequest) (downstream.UnaryResult, error) {
|
||||
if strings.TrimSpace(req.DeviceSessionID) == "" {
|
||||
return downstream.UnaryResult{}, errors.New("execute user.sessions.revoke: device_session_id must not be empty")
|
||||
}
|
||||
target := c.baseURL + "/api/v1/user/sessions/" + url.PathEscape(req.DeviceSessionID) + "/revoke"
|
||||
body, status, err := c.do(ctx, http.MethodPost, target, userID, nil)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("execute user.sessions.revoke: %w", err)
|
||||
}
|
||||
return projectUserSessionRevokeResponse(status, body)
|
||||
}
|
||||
|
||||
func (c *RESTClient) executeUserSessionsRevokeAll(ctx context.Context, userID string) (downstream.UnaryResult, error) {
|
||||
body, status, err := c.do(ctx, http.MethodPost, c.baseURL+"/api/v1/user/sessions/revoke-all", userID, nil)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("execute user.sessions.revoke_all: %w", err)
|
||||
}
|
||||
return projectUserSessionsRevokeAllResponse(status, body)
|
||||
}
|
||||
|
||||
func projectUserSessionsListResponse(statusCode int, payload []byte) (downstream.UnaryResult, error) {
|
||||
switch {
|
||||
case statusCode == http.StatusOK:
|
||||
var response usermodel.ListMySessionsResponse
|
||||
if err := decodeStrictJSON(payload, &response); err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("decode success response: %w", err)
|
||||
}
|
||||
payloadBytes, err := transcoder.ListMySessionsResponseToPayload(&response)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("encode success response payload: %w", err)
|
||||
}
|
||||
return downstream.UnaryResult{
|
||||
ResultCode: userCommandResultCodeOK,
|
||||
PayloadBytes: payloadBytes,
|
||||
}, nil
|
||||
case statusCode == http.StatusServiceUnavailable:
|
||||
return downstream.UnaryResult{}, downstream.ErrDownstreamUnavailable
|
||||
case statusCode >= 400 && statusCode <= 599:
|
||||
return projectUserBackendError(statusCode, payload)
|
||||
default:
|
||||
return downstream.UnaryResult{}, fmt.Errorf("unexpected HTTP status %d", statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func projectUserSessionRevokeResponse(statusCode int, payload []byte) (downstream.UnaryResult, error) {
|
||||
switch {
|
||||
case statusCode == http.StatusOK:
|
||||
var session usermodel.DeviceSession
|
||||
if err := decodeStrictJSON(payload, &session); err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("decode success response: %w", err)
|
||||
}
|
||||
payloadBytes, err := transcoder.RevokeMySessionResponseToPayload(&usermodel.RevokeMySessionResponse{Session: session})
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("encode success response payload: %w", err)
|
||||
}
|
||||
return downstream.UnaryResult{
|
||||
ResultCode: userCommandResultCodeOK,
|
||||
PayloadBytes: payloadBytes,
|
||||
}, nil
|
||||
case statusCode == http.StatusServiceUnavailable:
|
||||
return downstream.UnaryResult{}, downstream.ErrDownstreamUnavailable
|
||||
case statusCode >= 400 && statusCode <= 599:
|
||||
return projectUserBackendError(statusCode, payload)
|
||||
default:
|
||||
return downstream.UnaryResult{}, fmt.Errorf("unexpected HTTP status %d", statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func projectUserSessionsRevokeAllResponse(statusCode int, payload []byte) (downstream.UnaryResult, error) {
|
||||
switch {
|
||||
case statusCode == http.StatusOK:
|
||||
var summary usermodel.DeviceSessionRevocationSummary
|
||||
if err := decodeStrictJSON(payload, &summary); err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("decode success response: %w", err)
|
||||
}
|
||||
payloadBytes, err := transcoder.RevokeAllMySessionsResponseToPayload(&usermodel.RevokeAllMySessionsResponse{Summary: summary})
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("encode success response payload: %w", err)
|
||||
}
|
||||
return downstream.UnaryResult{
|
||||
ResultCode: userCommandResultCodeOK,
|
||||
PayloadBytes: payloadBytes,
|
||||
}, nil
|
||||
case statusCode == http.StatusServiceUnavailable:
|
||||
return downstream.UnaryResult{}, downstream.ErrDownstreamUnavailable
|
||||
case statusCode >= 400 && statusCode <= 599:
|
||||
return projectUserBackendError(statusCode, payload)
|
||||
default:
|
||||
return downstream.UnaryResult{}, fmt.Errorf("unexpected HTTP status %d", statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
// projectUserBackendError shares the error-projection path between every
|
||||
// user-command projector. The error envelope is identical regardless of
|
||||
// the success-path payload shape.
|
||||
func projectUserBackendError(statusCode int, payload []byte) (downstream.UnaryResult, error) {
|
||||
errResp, err := decodeUserError(statusCode, payload)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("decode error response: %w", err)
|
||||
}
|
||||
payloadBytes, err := transcoder.ErrorResponseToPayload(errResp)
|
||||
if err != nil {
|
||||
return downstream.UnaryResult{}, fmt.Errorf("encode error response payload: %w", err)
|
||||
}
|
||||
return downstream.UnaryResult{
|
||||
ResultCode: errResp.Error.Code,
|
||||
PayloadBytes: payloadBytes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func projectUserResponse(statusCode int, payload []byte) (downstream.UnaryResult, error) {
|
||||
switch {
|
||||
case statusCode == http.StatusOK:
|
||||
|
||||
Reference in New Issue
Block a user