In previous jobs, i have used the modeling tools from eclipse to abstract out program design for set of microservices into a graphical DSL using Papyrus and Acceleo, Now it looks like Sirius offers the ability to go beyond the limitations of Papyrus to create different types of diagrams that can help to capture model information more easily.
In my previous work, I have found that the first step to creating a Model for such a an effort is to examine the existing source code and extracting the elements for a "conceptual model". (A conceptual model captures the important concepts and relationships in some domain.)
First we analyze the service.go file from Chapter 10.
As a first step, I highlight the code to identify potential candidates for inclusion in the model.
NOTE: for code generation projects, we are concerned with the items that convey "instance" data and not the boilerplate code that will repeat based on the instance data. Our goal is to extract out the information that will enable us to generate this code.
In the code below, there is great deal of boilerplate code that really is subject to error prone copy paste. What we want to know is what is the smallest set of information that would enable use to generate this code.
I have highlighted the areas of what I call "instance data"
package vault
import (
"encoding/json"
"errors"
"net/http"
"github.com/go-kit/kit/endpoint"
"golang.org/x/crypto/bcrypt"
"golang.org/x/net/context"
)
// Service
provides password hashing capabilities.
type Service
interface {
Hash(ctx context.Context,
password string) (string, error)
Validate(ctx
context.Context, password, hash string) (bool, error)
}
// NewService
makes a new Service.
func NewService()
Service {
return vaultService{}
}
type vaultService
struct{}
func (vaultService)
Hash(ctx context.Context, password string) (string, error) {
hash,
err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err
!= nil {
return
"", err
}
return
string(hash), nil
}
func (vaultService)
Validate(ctx
context.Context, password, hash string) (bool, error) {
err :=
bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
if err
!= nil {
return
false, nil
}
return
true, nil
}
type hashRequest
struct {
Password
string `json:"password"`
}
type hashResponse
struct {
Hash
string `json:"hash"`
Err string `json:"err,omitempty"`
}
type validateRequest
struct {
Password
string `json:"password"`
Hash string `json:"hash"`
}
type validateResponse
struct {
Valid bool `json:"valid"`
Err string `json:"err,omitempty"`
}
func decodeHashRequest(ctx context.Context, r *http.Request) (interface{},
error) {
var req
hashRequest
err :=
json.NewDecoder(r.Body).Decode(&req)
if err
!= nil {
return
nil, err
}
return
req, nil
}
func decodeValidateRequest(ctx context.Context, r *http.Request)
(interface{}, error) {
var req
validateRequest
err :=
json.NewDecoder(r.Body).Decode(&req)
if err
!= nil {
return
nil, err
}
return
req, nil
}
func encodeResponse(ctx context.Context, w
http.ResponseWriter, response interface{}) error {
return
json.NewEncoder(w).Encode(response)
}
func MakeHashEndpoint(srv
Service)
endpoint.Endpoint {
return
func(ctx context.Context, request interface{}) (interface{}, error) {
req
:= request.(hashRequest)
v,
err := srv.Hash(ctx, req.Password)
if
err != nil {
return
hashResponse{v,
err.Error()}, nil
}
return
hashResponse{v,
""}, nil
}
}
func MakeValidateEndpoint(srv
Service)
endpoint.Endpoint {
return
func(ctx context.Context, request interface{}) (interface{}, error) {
req
:= request.(validateRequest)
v,
err := srv.Validate(ctx,
req.Password, req.Hash)
if
err != nil {
return
validateResponse{false,
err.Error()}, nil
}
return
validateResponse{v,
""}, nil
}
}
// Endpoints represents all endpoints for the vault Service.
type Endpoints struct {
HashEndpoint endpoint.Endpoint
ValidateEndpoint
endpoint.Endpoint
}
// Hash
uses the HashEndpoint
to hash a password.
func (e Endpoints) Hash(ctx context.Context, password string)
(string, error) {
req := hashRequest{Password:
password}
resp,
err := e.HashEndpoint(ctx,
req)
if err
!= nil {
return
"", err
}
hashResp := resp.(hashResponse)
if hashResp.Err !=
"" {
return
"", errors.New(hashResp.Err)
}
return hashResp.Hash, nil
}
// Validate uses the ValidateEndpoint to validate a password
and hash pair.
func (e Endpoints) Validate(ctx context.Context, password,
hash
string) (bool, error) {
req := validateRequest{Password:
password, Hash: hash}
resp,
err := e.ValidateEndpoint(ctx,
req)
if err
!= nil {
return
false, err
}
validateResp := resp.(validateResponse)
if validateResp.Err !=
"" {
return
false, errors.New(validateResp.Err)
}
return validateResp.Valid, nil
No comments:
Post a Comment