Skip to content
This repository has been archived by the owner on Apr 15, 2020. It is now read-only.

Commit

Permalink
Returntypesmapping (#7)
Browse files Browse the repository at this point in the history
* Define rest api status code to return type mapping
  • Loading branch information
springwiz authored Sep 30, 2019
1 parent b862104 commit b7cba4d
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 48 deletions.
20 changes: 18 additions & 2 deletions examples/todos.sysl
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,30 @@ Todos:
!alias Posts:
sequence of Post

!type ErrorResponse:
status <: string

!type ResourceNotFoundError:
status <: string

/todos:
/{id<:int}:
GET:
return Todo
if notfound:
return 404 <: ResourceNotFoundError
else if failed:
return 500 <: ErrorResponse
else:
return 200 <: Todo

/posts:
GET:
return Posts
if notfound:
return 404 <: ResourceNotFoundError
else if failed:
return 500 <: ErrorResponse
else:
return 200 <: Posts

/comments:
GET ?postId=int:
Expand Down
4 changes: 2 additions & 2 deletions todos/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (s *Client) GetComments(ctx context.Context, headers map[string]string, pos
// GetPosts ...
func (s *Client) GetPosts(ctx context.Context, headers map[string]string) (*restlib.HTTPResult, error) {
required := []string{}
responses := []interface{}{&Posts{}}
responses := []interface{}{&Posts{}, &ResourceNotFoundError{}, &ErrorResponse{}}
u, err := url.Parse(fmt.Sprintf("%s/posts", s.url))
if err != nil {
return nil, err
Expand All @@ -62,7 +62,7 @@ func (s *Client) GetPosts(ctx context.Context, headers map[string]string) (*rest
// GetTodosID ...
func (s *Client) GetTodosID(ctx context.Context, headers map[string]string, id int64) (*restlib.HTTPResult, error) {
required := []string{}
responses := []interface{}{&Todo{}}
responses := []interface{}{&Todo{}, &ResourceNotFoundError{}, &ErrorResponse{}}
u, err := url.Parse(fmt.Sprintf("%s/todos/%v", s.url, id))
if err != nil {
return nil, err
Expand Down
58 changes: 50 additions & 8 deletions todos/servicehandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,66 @@ func NewServiceHandler(serviceInterface ServiceInterface) *ServiceHandler {
// GetCommentsHandler ...
func (s *ServiceHandler) GetCommentsHandler(w http.ResponseWriter, r *http.Request) {
PostID := restlib.GetQueryParam(r, "postId")
httpStatus, headerMap, Posts := s.serviceInterface.GetComments(PostID)
Status, headerMap, Posts := s.serviceInterface.GetComments(PostID)
restlib.SetHeaders(w, headerMap)
restlib.SendHTTPResponse(w, httpStatus, Posts)
restlib.SendHTTPResponse(w, Status, Posts)
}

// GetPostsHandler ...
func (s *ServiceHandler) GetPostsHandler(w http.ResponseWriter, r *http.Request) {
httpStatus, headerMap, Posts := s.serviceInterface.GetPosts()
headerMap, Posts, ResourceNotFoundError, ErrorResponse := s.serviceInterface.GetPosts()
var httpStatus int

restlib.SetHeaders(w, headerMap)
restlib.SendHTTPResponse(w, httpStatus, Posts)
if Posts != nil {
httpStatus = 200
restlib.SendHTTPResponse(w, httpStatus, Posts)
return
}

if ResourceNotFoundError != nil {
httpStatus = 404
errResp := s.serviceInterface.GetErrorResponse(httpStatus, "Internal server error", nil)
restlib.SendHTTPResponse(w, httpStatus, errResp)
return
}

if ErrorResponse != nil {
httpStatus = 500
errResp := s.serviceInterface.GetErrorResponse(httpStatus, "Internal server error", nil)
restlib.SendHTTPResponse(w, httpStatus, errResp)
return
}

}

// GetTodosIDHandler ...
func (s *ServiceHandler) GetTodosIDHandler(w http.ResponseWriter, r *http.Request) {
ID := restlib.GetURLParam(r, "id")
httpStatus, headerMap, Todo := s.serviceInterface.GetTodosID(ID)
headerMap, Todo, ResourceNotFoundError, ErrorResponse := s.serviceInterface.GetTodosID(ID)
var httpStatus int

restlib.SetHeaders(w, headerMap)
restlib.SendHTTPResponse(w, httpStatus, Todo)
if Todo != nil {
httpStatus = 200
restlib.SendHTTPResponse(w, httpStatus, Todo)
return
}

if ResourceNotFoundError != nil {
httpStatus = 404
errResp := s.serviceInterface.GetErrorResponse(httpStatus, "Internal server error", nil)
restlib.SendHTTPResponse(w, httpStatus, errResp)
return
}

if ErrorResponse != nil {
httpStatus = 500
errResp := s.serviceInterface.GetErrorResponse(httpStatus, "Internal server error", nil)
restlib.SendHTTPResponse(w, httpStatus, errResp)
return
}

}

// PostCommentsHandler ...
Expand All @@ -64,7 +106,7 @@ func (s *ServiceHandler) PostCommentsHandler(w http.ResponseWriter, r *http.Requ
return
}

httpStatus, headerMap, Post := s.serviceInterface.PostComments(newPost)
Status, headerMap, Post := s.serviceInterface.PostComments(newPost)
restlib.SetHeaders(w, headerMap)
restlib.SendHTTPResponse(w, httpStatus, Post)
restlib.SendHTTPResponse(w, Status, Post)
}
8 changes: 4 additions & 4 deletions todos/serviceinterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ func NewDefaultTodosImpl() *DefaultTodosImpl {
// ServiceInterface for Todos
type ServiceInterface interface {
GetComments(PostID string) (int, map[string]string, *Posts)
GetPosts() (int, map[string]string, *Posts)
GetTodosID(ID string) (int, map[string]string, *Todo)
GetPosts() (map[string]string, *Posts, *ResourceNotFoundError, *ErrorResponse)
GetTodosID(ID string) (map[string]string, *Todo, *ResourceNotFoundError, *ErrorResponse)
PostComments(newPost Post) (int, map[string]string, *Post)
IsAuthorized(r *http.Request, authHeader string) bool
GetErrorResponse(statusCode int, message string, errObj error) interface{}
Expand All @@ -36,13 +36,13 @@ func (d *DefaultTodosImpl) GetComments(PostID string) (int, map[string]string, *

// nolint:gocritic
// GetPosts ...
func (d *DefaultTodosImpl) GetPosts() (int, map[string]string, *Posts) {
func (d *DefaultTodosImpl) GetPosts() (map[string]string, *Posts, *ResourceNotFoundError, *ErrorResponse) {
panic(errors.New("not implemented"))
}

// nolint:gocritic
// GetTodosID ...
func (d *DefaultTodosImpl) GetTodosID(ID string) (int, map[string]string, *Todo) {
func (d *DefaultTodosImpl) GetTodosID(ID string) (map[string]string, *Todo, *ResourceNotFoundError, *ErrorResponse) {
panic(errors.New("not implemented"))
}

Expand Down
10 changes: 10 additions & 0 deletions todos/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ var _ = time.Parse
// Reference imports to suppress unused errors
var _ = date.Parse

// ErrorResponse ...
type ErrorResponse struct {
Status string `json:"status"`
}

// Post ...
type Post struct {
Body string `json:"body"`
Expand All @@ -24,6 +29,11 @@ type Post struct {
UserID int64 `json:"userId"`
}

// ResourceNotFoundError ...
type ResourceNotFoundError struct {
Status string `json:"status"`
}

// Todo ...
type Todo struct {
Completed bool `json:"completed"`
Expand Down
17 changes: 14 additions & 3 deletions transforms/svc_client.sysl
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ CodeGenTransform:
Expression = typeName -> <Expression> (:
NewSlice = typeName -> <NewSlice> (:
TypeName = typeName
let v = values -> <sequence of string> (:
out = '&' + GoName(.).out + '{}'
let v = values -> <sequence of string> (v:
out = '&' + v.type + '{}'
)
SliceValues = Join(v flatten(.out), ',')
)
Expand Down Expand Up @@ -326,11 +326,22 @@ CodeGenTransform:
)
)

!view splitPayload(payload <: set of string) -> sequence of string:
payload -> (:
type = GoName(.).out
)

!view splitReturnTypes(types <: map) -> sequence of string:
types -> (v:
type = GoName(v.value).out
)

!view HttpMethodStatments(ep <: sysl.Endpoint) -> Block:
ep -> (:
let requiredHeaders = ep.value.params where("header" in .attrs.patterns && "required" in .attrs.patterns)
let required = declareHeaders("required", "string", requiredHeaders flatten(.attrs.name))
let responses = declareReturnTypeArray("responses", "interface{}", Split(ep.value.ret.payload, ', '))
let resultTypes = if ep.value.ret.payload == null then splitReturnTypes(ep.value.ret) else splitPayload(Split(ep.value.ret.payload, ', '))
let responses = declareReturnTypeArray("responses", "interface{}", resultTypes)

let stmts = [required, responses, urlVariable(ep), returnIfError("err", "nil, err")]
StatementList = if ep.value.queryvars count ==:
Expand Down
133 changes: 115 additions & 18 deletions transforms/svc_handler.sysl
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,22 @@ CodeGenTransform:
StatementList = [handleError(condition, errorCode, errMsg, "nil")]
)

!view splitPayload(payload <: set of string) -> sequence of string:
payload -> (:
typeName = GoName(.).out
)

!view splitReturnTypes(types <: map) -> sequence of string:
types -> (v:
typeName = GoName(v.value).out
)

!view EpHandlerMethodDecl(eps <: set of sysl.Endpoints, packageName <: string) -> sequence of TopLevelDecl:
eps -> (ep:
let implFuncName = methodDef(ep).methodName
let funcName = implFuncName + "Handler"
let allParams = getAllParams(ep)
let hasNoPayload = if ep.value.ret.payload == null then true else false
Comment = '// ' + funcName + ' ...'
MethodDecl = ep -> <MethodDecl>(:
Receiver = ep -> <Receiver>(:
Expand All @@ -321,16 +332,102 @@ CodeGenTransform:
)
)
)

let hasPayload = if ep.value.ret.payload != "" then true else false

let returnValues = if hasPayload then "httpStatus, headerMap, " + ep.value.ret.payload else "httpStatus, headerMap"
let sendRespCallArgs = if hasPayload then "httpStatus, " + ep.value.ret.payload else "httpStatus"


let httpStatusString = "httpStatus"
let httpStatus = ep -> <Statement> (:
Statement = ep -> <Statement> (:
VarDecl = varDecl(httpStatusString, "int")
)
)
let resultTypes = if hasNoPayload then splitReturnTypes(ep.value.ret) else splitPayload(Split(ep.value.ret.payload, ', '))
let tempReturnValues = if resultTypes count > 0 then "headerMap, " + Join(resultTypes flatten(.typeName),", ") else "headerMap"
let returnValues = if hasNoPayload then tempReturnValues else "Status, " + tempReturnValues

let retStatusStatement = ep -> <StatementList> (:
StatementList = ep.value.ret -> <StatementList> (v:
let condition = v.value + " != nil"
Statement = ep -> <Statement> (:
IfElseStmt = condition -> <IfElseStmt> (:
Expression = makeValueExpr(condition)
Block = condition -> <Block> (:
let sendStatus = condition -> <Statement> (:
Statement = condition -> <Statement> (:
AssignStmt = condition -> <AssignStmt> (:
Variables = httpStatusString
Expression = makeValueExpr(v.key)
)
)
)
let errorRes = condition -> <Statement> (:
Statement = condition -> <Statement> (:
DeclareAndAssignStmt = condition -> <DeclareAndAssignStmt> (:
Variables = "errResp"
Expression = condition -> <Expression> (:
FunctionCall = condition -> <Statement> (:
FunctionName = "s.serviceInterface.GetErrorResponse"
FunctionArgs = condition -> <FunctionArgs> (:
Expression = makeValueExpr(httpStatusString)
let errMsgExpr = condition -> <Expression> (:
Expression = makeValueExpr('"Internal server error"')
)
let errObjExpr = condition -> <Expression> (:
Expression = makeValueExpr("nil")
)
FuncArgsRest = [errMsgExpr, errObjExpr]
)
)
)
)
)
)
let sendResErr = condition -> <Statement> (:
Statement = condition -> <Statement> (:
FunctionCall = condition -> <Statement> (:
FunctionName = "restlib.SendHTTPResponse"
FunctionArgs = condition -> <FunctionArgs> (:
Expression = makeValueExpr("w")
let errRespExpr = condition -> <Expression> (:
Expression = makeValueExpr("errResp")
)
let a = condition -> <FuncArgsRest> (:
Expression = makeValueExpr(httpStatusString)
)
FuncArgsRest = [a, errRespExpr]
)
)
)
)
let sendRes = condition -> <Statement> (:
Statement = condition -> <Statement> (:
FunctionCall = condition -> <Statement> (:
FunctionName = "restlib.SendHTTPResponse"
FunctionArgs = condition -> <FunctionArgs> (:
Expression = makeValueExpr("w")
let a = condition -> <FuncArgsRest> (:
Expression = makeValueExpr(httpStatusString)
)
let b = condition -> <FuncArgsRest> (:
Expression = makeValueExpr(v.value)
)
FuncArgsRest = [a, b]
)
)
)
)
let retStatement = condition -> <Statement> (:
Statement = condition -> <Statement> (:
ReturnStmt = condition -> <ReturnStmt> (:
PayLoad = ""
)
)
)
StatementList = if MatchString("^[4-5][0-9][0-9]$", v.key) then [sendStatus, errorRes, sendResErr, retStatement] else [sendStatus, sendRes, retStatement]
)
)
)
)
)
Block = ep -> <Block>(:

let implCall = ep -> <StatementList> (:
let implCall = ep -> <Statement> (:
Statement = ep -> <Statement> (:
DeclareAndAssignStmt = ep -> <DeclareAndAssignStmt> (:
Variables = returnValues
Expand All @@ -345,8 +442,7 @@ CodeGenTransform:
)
)
)

let setHeadersCall = ep -> <StatementList> (:
let setHeadersCall = ep -> <Statement> (:
Statement = ep -> <Statement> (:
FunctionCall = ep -> <FunctionCall> (:
FunctionName = "restlib.SetHeaders"
Expand All @@ -359,22 +455,23 @@ CodeGenTransform:
)
)
)

let sendRespCall = ep -> <StatementList> (:
let sendResExist = ep -> <Statement> (:
Statement = ep -> <Statement> (:
FunctionCall = ep -> <FunctionCall> (:
let values = if hasNoPayload then splitReturnTypes(ep.value.ret) else splitPayload(Split("status, " + ep.value.ret.payload, ', '))
FunctionCall = ep -> <Statement> (:
FunctionName = "restlib.SendHTTPResponse"
FunctionArgs = ep -> <FunctionArgs> (:
Expression = makeValueExpr("w")
FuncArgsRest = [ep] -> <FuncArgsRest> (:
Expression = makeValueExpr(sendRespCallArgs)
let restParams = values -> <Expression>(v:
Expression = makeValueExpr(v.typeName)
)
)
FuncArgsRest = restParams
)
)
)
)

let funcCalls = [implCall, setHeadersCall, sendRespCall]
let funcCalls = if hasNoPayload then [implCall] | [httpStatus] | [setHeadersCall]| retStatusStatement.StatementList else [implCall] | [setHeadersCall] | [sendResExist]

let commonStmtList = getReqVars(allParams).StatementList | validateHeaders(ep)
let stmtList = if ep.value.params where("header" in .attrs.patterns && "required" in .attrs.patterns && .attrs.name == "Authorization") count > 0 then commonStmtList | verifyAuthHeaders(ep).StatementList else commonStmtList
Expand Down
Loading

0 comments on commit b7cba4d

Please sign in to comment.