Skip to content

Commit

Permalink
Merge pull request gophercon#17 from raphael/master
Browse files Browse the repository at this point in the history
Add "Building Microservices From Design With goa" deck
  • Loading branch information
bketelsen authored Jul 15, 2016
2 parents c9fdb9d + 8fa325f commit 2589af7
Show file tree
Hide file tree
Showing 120 changed files with 36,005 additions and 0 deletions.
3 changes: 3 additions & 0 deletions RaphaelSimon-BuildingMicroservicesWithgoa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Building Microservices From Design With goa

Slides for GopherCon 2016 talk.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package acl

import "public_service/clients/acl/client" // HL

type Client struct {
*client.Client // HL
}

func (c *Client) ListACLs(resource *Resource, user *User) ([]*ACL, error) {
resourceHref := BuildResourceHref(resource)
userHref := BuildUserHref(user)
acls, err := c.Client.ListACLs(resourceHref, userHref) // HL
if err != nil {
return nil, ErrBadGateway(err)
}
res := make([]*ACL, len(acls))
for i, acl := range acls {
res[i] = aclFromResponse(acl)
}
return res, nil
}
11 changes: 11 additions & 0 deletions RaphaelSimon-BuildingMicroservicesWithgoa/code/connecting/share.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package design

import . "github.com/goadesign/goa/design"
import . "github.com/goadesign/goa/design/apidsl"
import . "cellar/design/public" // HL

var CellarPayload = Type("BottlePayload", func() {
Attribute("bottles", CollectionOf(BottlePayload), "Cellar bottles", func() { // HL
MinLength(1)
})
})
19 changes: 19 additions & 0 deletions RaphaelSimon-BuildingMicroservicesWithgoa/code/plugin/gorma.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package design

import (
"github.com/goadesign/gorma"
. "github.com/goadesign/gorma/dsl"
)

var _ = StorageGroup("MyAPIStorage", func() {
Store("postgres", gorma.Postgres, func() {
Model("User", func() {
BuildsFrom(func() { Payload("user", "create") })
RendersTo(User)
Field("id", gorma.Integer, func() {
PrimaryKey()
Description("This is the User Model PK field")
})
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package design

import (
. "github.com/goadesign/goa/design"
. "github.com/goadesign/goa/design/apidsl"
)

// JWT defines a security scheme using JWT. The scheme uses the "Authorization" header to lookup
// the token. It also defines then scope "api".
var JWT = JWTSecurity("jwt", func() { // HL
Header("Authorization") // HL
}) // HL

// Resource jwt uses the JWTSecurity security scheme.
var _ = Resource("jwt", func() {
Description("This resource uses JWT to secure its endpoints")
DefaultMedia(SuccessMedia)

Security(JWT) // HL
// ...
// OMIT
Action("signin", func() {
Description("Creates a valid JWT")
Security(SigninBasicAuth)
Routing(POST("/jwt/signin"))
Response(NoContent, func() {
Headers(func() {
Header("Authorization", String, "Generated JWT")
})
})
Response(Unauthorized)
})

Action("secure", func() {
Description("This action is secured with the jwt scheme")
Routing(GET("/jwt"))
Params(func() {
Param("fail", Boolean, "Force auth failure via JWT validation middleware")
})
Response(OK)
Response(Unauthorized)
})

Action("unsecure", func() {
Description("This action does not require auth")
Routing(GET("/jwt/unsecure"))
NoSecurity() // Override the need for auth
Response(OK)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//************************************************************************//
// API "cellar": Application Contexts
//
// Generated with goagen v0.2.dev, command line:
// $ goagen
// --design=github.com/raphael/gophercon2016/demos/1-design/design
// --out=$(GOPATH)/src/github.com/raphael/gophercon2016/demos/1-design
// --version=v0.2.dev
//
// The content of this file is auto-generated, DO NOT MODIFY
//************************************************************************//

package app

import (
"github.com/goadesign/goa"
"golang.org/x/net/context"
"strconv"
)

// CreateBottleContext provides the bottle create action context.
type CreateBottleContext struct {
context.Context
*goa.ResponseData
*goa.RequestData
Payload *CreateBottlePayload
}

// NewCreateBottleContext parses the incoming request URL and body, performs validations and creates the
// context used by the bottle controller create action.
func NewCreateBottleContext(ctx context.Context, service *goa.Service) (*CreateBottleContext, error) {
var err error
resp := goa.ContextResponse(ctx)
resp.Service = service
req := goa.ContextRequest(ctx)
rctx := CreateBottleContext{Context: ctx, ResponseData: resp, RequestData: req}
return &rctx, err
}

// createBottlePayload is the bottle create action payload.
type createBottlePayload struct {
// Name of bottle
Name *string `json:"name,omitempty" xml:"name,omitempty" form:"name,omitempty"`
// Rating of bottle
Rating *int `json:"rating,omitempty" xml:"rating,omitempty" form:"rating,omitempty"`
// Vintage of bottle
Vintage *int `json:"vintage,omitempty" xml:"vintage,omitempty" form:"vintage,omitempty"`
}

// Validate runs the validation rules defined in the design.
func (payload *createBottlePayload) Validate() (err error) {
if payload.Name == nil {
err = goa.MergeErrors(err, goa.MissingAttributeError(`payload`, "name"))
}
if payload.Vintage == nil {
err = goa.MergeErrors(err, goa.MissingAttributeError(`payload`, "vintage"))
}
if payload.Rating == nil {
err = goa.MergeErrors(err, goa.MissingAttributeError(`payload`, "rating"))
}

if payload.Name != nil {
if len(*payload.Name) < 1 {
err = goa.MergeErrors(err, goa.InvalidLengthError(`payload.name`, *payload.Name, len(*payload.Name), 1, true))
}
}
if payload.Rating != nil {
if *payload.Rating < 1 {
err = goa.MergeErrors(err, goa.InvalidRangeError(`payload.rating`, *payload.Rating, 1, true))
}
}
if payload.Rating != nil {
if *payload.Rating > 5 {
err = goa.MergeErrors(err, goa.InvalidRangeError(`payload.rating`, *payload.Rating, 5, false))
}
}
if payload.Vintage != nil {
if *payload.Vintage < 1900 {
err = goa.MergeErrors(err, goa.InvalidRangeError(`payload.vintage`, *payload.Vintage, 1900, true))
}
}
return
}

// Publicize creates CreateBottlePayload from createBottlePayload
func (payload *createBottlePayload) Publicize() *CreateBottlePayload {
var pub CreateBottlePayload
if payload.Name != nil {
pub.Name = *payload.Name
}
if payload.Rating != nil {
pub.Rating = *payload.Rating
}
if payload.Vintage != nil {
pub.Vintage = *payload.Vintage
}
return &pub
}

// CreateBottlePayload is the bottle create action payload.
type CreateBottlePayload struct {
// Name of bottle
Name string `json:"name" xml:"name" form:"name"`
// Rating of bottle
Rating int `json:"rating" xml:"rating" form:"rating"`
// Vintage of bottle
Vintage int `json:"vintage" xml:"vintage" form:"vintage"`
}

// Validate runs the validation rules defined in the design.
func (payload *CreateBottlePayload) Validate() (err error) {
if payload.Name == "" {
err = goa.MergeErrors(err, goa.MissingAttributeError(`payload`, "name"))
}

if len(payload.Name) < 1 {
err = goa.MergeErrors(err, goa.InvalidLengthError(`payload.name`, payload.Name, len(payload.Name), 1, true))
}
if payload.Rating < 1 {
err = goa.MergeErrors(err, goa.InvalidRangeError(`payload.rating`, payload.Rating, 1, true))
}
if payload.Rating > 5 {
err = goa.MergeErrors(err, goa.InvalidRangeError(`payload.rating`, payload.Rating, 5, false))
}
if payload.Vintage < 1900 {
err = goa.MergeErrors(err, goa.InvalidRangeError(`payload.vintage`, payload.Vintage, 1900, true))
}
return
}

// Created sends a HTTP response with status code 201.
func (ctx *CreateBottleContext) Created() error {
ctx.ResponseData.WriteHeader(201)
return nil
}

// ShowBottleContext provides the bottle show action context.
type ShowBottleContext struct {
context.Context
*goa.ResponseData
*goa.RequestData
ID int
}

// NewShowBottleContext parses the incoming request URL and body, performs validations and creates the
// context used by the bottle controller show action.
func NewShowBottleContext(ctx context.Context, service *goa.Service) (*ShowBottleContext, error) {
var err error
resp := goa.ContextResponse(ctx)
resp.Service = service
req := goa.ContextRequest(ctx)
rctx := ShowBottleContext{Context: ctx, ResponseData: resp, RequestData: req}
paramID := req.Params["id"]
if len(paramID) > 0 {
rawID := paramID[0]
if id, err2 := strconv.Atoi(rawID); err2 == nil {
rctx.ID = id
} else {
err = goa.MergeErrors(err, goa.InvalidParamTypeError("id", rawID, "integer"))
}
}
return &rctx, err
}

// OK sends a HTTP response with status code 200.
func (ctx *ShowBottleContext) OK(r *Bottle) error {
ctx.ResponseData.Header().Set("Content-Type", "application/vnd.gophercon.goa.bottle")
return ctx.ResponseData.Service.Send(ctx.Context, 200, r)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//************************************************************************//
// API "cellar": Application Controllers
//
// Generated with goagen v0.2.dev, command line:
// $ goagen
// --design=github.com/raphael/gophercon2016/demos/1-design/design
// --out=$(GOPATH)/src/github.com/raphael/gophercon2016/demos/1-design
// --version=v0.2.dev
//
// The content of this file is auto-generated, DO NOT MODIFY
//************************************************************************//

package app

import (
"github.com/goadesign/goa"
"golang.org/x/net/context"
"net/http"
)

// initService sets up the service encoders, decoders and mux.
func initService(service *goa.Service) {
// Setup encoders and decoders
service.Encoder.Register(goa.NewJSONEncoder, "application/json")
service.Encoder.Register(goa.NewGobEncoder, "application/gob", "application/x-gob")
service.Encoder.Register(goa.NewXMLEncoder, "application/xml")
service.Decoder.Register(goa.NewJSONDecoder, "application/json")
service.Decoder.Register(goa.NewGobDecoder, "application/gob", "application/x-gob")
service.Decoder.Register(goa.NewXMLDecoder, "application/xml")

// Setup default encoder and decoder
service.Encoder.Register(goa.NewJSONEncoder, "*/*")
service.Decoder.Register(goa.NewJSONDecoder, "*/*")
}

// BottleController is the controller interface for the Bottle actions.
type BottleController interface {
goa.Muxer
Create(*CreateBottleContext) error
Show(*ShowBottleContext) error
}

// MountBottleController "mounts" a Bottle resource controller on the given service.
func MountBottleController(service *goa.Service, ctrl BottleController) {
initService(service)
var h goa.Handler

h = func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
// Check if there was an error loading the request
if err := goa.ContextError(ctx); err != nil {
return err
}
// Build the context
rctx, err := NewCreateBottleContext(ctx, service)
if err != nil {
return err
}
// Build the payload
if rawPayload := goa.ContextRequest(ctx).Payload; rawPayload != nil {
rctx.Payload = rawPayload.(*CreateBottlePayload)
} else {
return goa.MissingPayloadError()
}
return ctrl.Create(rctx)
}
service.Mux.Handle("POST", "/bottles", ctrl.MuxHandler("Create", h, unmarshalCreateBottlePayload))
service.LogInfo("mount", "ctrl", "Bottle", "action", "Create", "route", "POST /bottles")

h = func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
// Check if there was an error loading the request
if err := goa.ContextError(ctx); err != nil {
return err
}
// Build the context
rctx, err := NewShowBottleContext(ctx, service)
if err != nil {
return err
}
return ctrl.Show(rctx)
}
service.Mux.Handle("GET", "/bottles/:id", ctrl.MuxHandler("Show", h, nil))
service.LogInfo("mount", "ctrl", "Bottle", "action", "Show", "route", "GET /bottles/:id")
}

// unmarshalCreateBottlePayload unmarshals the request body into the context request data Payload field.
func unmarshalCreateBottlePayload(ctx context.Context, service *goa.Service, req *http.Request) error {
payload := &createBottlePayload{}
if err := service.DecodeRequest(req, payload); err != nil {
return err
}
if err := payload.Validate(); err != nil {
return err
}
goa.ContextRequest(ctx).Payload = payload.Publicize()
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//************************************************************************//
// API "cellar": Application Resource Href Factories
//
// Generated with goagen v0.2.dev, command line:
// $ goagen
// --design=github.com/raphael/gophercon2016/demos/1-design/design
// --out=$(GOPATH)/src/github.com/raphael/gophercon2016/demos/1-design
// --version=v0.2.dev
//
// The content of this file is auto-generated, DO NOT MODIFY
//************************************************************************//

package app

import "fmt"

// BottleHref returns the resource href.
func BottleHref(id interface{}) string {
return fmt.Sprintf("/bottles/%v", id)
}
Loading

0 comments on commit 2589af7

Please sign in to comment.