Skip to content

Commit

Permalink
generators wip
Browse files Browse the repository at this point in the history
  • Loading branch information
casualjim committed Feb 6, 2015
1 parent c5833d3 commit d4effc7
Show file tree
Hide file tree
Showing 28 changed files with 462 additions and 240 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,15 @@ For a V1 I want to have this feature set completed:
- [x] serve spec
- [x] routing
- [x] validation
- [ ] additional validation through an interface
- [ ] authorization
- [x] basic auth
- [x] api key auth
- [ ] oauth2
- [ ] implicit
- [ ] access code
- [ ] password
- [ ] application
- [x] swagger docs UI
- [x] Typed JSON Schema implementation
- [x] JSON Pointer that knows about structs
Expand Down Expand Up @@ -94,10 +99,7 @@ After the v1 implementation extra transports are on the roadmap
- [ ] swagger sockjs (swagger over sockjs)
- [ ] swagger socket.io (swagger over socket.io)
- [ ] swagger 0mq (swagger over 0mq)
- Authorization:
- [ ] oauth2 provider
- [ ] implicit
- [ ] access code
- [ ] password
- [ ] application
- Templates:
- [ ] Allow usage of templates using stdlib templates


4 changes: 4 additions & 0 deletions cmd/swagger/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ var opts struct{}

func main() {
parser := flags.NewParser(&opts, flags.Default)

parser.AddCommand("validate", "validate the swagger document", "validate the provided swagger document against a swagger spec", &commands.ValidateSpec{})

// parser.AddCommand("editor", "edit the swagger.json document", "serve the swagger editor with the specified spec file", commands.NewEditor())
parser.AddCommand("ui", "api-docs for the swagger.json document", "serve the swagger ui application with the specified spec file", commands.NewUI())

parser.AddCommand("generate", "genererate go code", "generate go code for the swagger spec file", &commands.Generate{})
parser.Parse()
}
13 changes: 12 additions & 1 deletion design.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ When a result is produced it will do the same thing by making use of the `Accept
When the muxer registers routes it also builds a suite of validation plans, one for each operation.
Validation allows for adding custom validations for types through implementing a Validatable interface. This interface does not override but extends the validations provided by the swagger schema.

There is a mapping from validation name to status code, this mapping is also prioritized so that in the event of multiple validation errors that would required different status codes we get a consistent result.
There is a mapping from validation name to status code, this mapping is also prioritized so that in the event of multiple validation errors that would required different status codes we get a consistent result. This prioritization can be done by the user by providing a ServeError function.

```go
type Validatable interface {
Expand All @@ -130,5 +130,16 @@ By the time the operation handler is executed we're sure the request is **author
The result it gets from the operation handler will be turned into a response. Should the result of the operation handler be an error or a series of errors it will determine an appropriate status code and render the error result.


# Codegen

Most of the codegen will try to reuse the templates from the swagger-codegen project. These are mustache templates and could be downloaded on demand.
`swagger generate add-templates objc` would download the templates for generating an objective c client.

The go server api generator however won't reuse those templates but define its own set, because currently no proper go support exists in that project. Once I'm happy with what they generate I'll contribute them back to the swagger-codegen project.

But both the server and the client generator use mustache and not the go template system, not because there is a defficiency in the go template system, but mostly to keep taking advantage of community contributions in the swagger codegen project.

A generated client needs to have support for uploading files as multipart entries. The model generating code is shared between client and server. The things that operate with those models will be different.
A generated client could implement validation on the client side for the request parameters and received response. The meat of the client is not actually implemented as generated code but a single submit function that knows how to perform all the shared operations and then issue the request.
A client typically has only one consumer and producer registered. The content type for the request is the media type of the consumer, the accept header is the media type of the producer.

2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ And it's 100% open source software.
Install:
go get -u github.com/casualjim/go-swagger/swagger
go get -u github.com/casualjim/go-swagger/cmd/swagger
The implementation also provides a number of command line tools to help working with swagger.
Expand Down
184 changes: 143 additions & 41 deletions errors/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,39 @@ package errors
import "fmt"

const (
invalidType = `%s is an invalid type name`
typeFail = `%s in %s must be of type %s`
typeFailWithData = `%s in %s must be of type %s: %q`
typeFailWithError = `%s in %s must be of type %s, because: %s`
requiredFail = `%s in %s is required`
tooLongMessage = `%s in %s should be at most %d chars long`
tooShortMessage = `%s in %s should be at least %d chars long`
patternFail = `%s in %s should match '%s'`
enumFail = `%s in %s should be one of %v`
mulitpleOfFail = `%s in %s should be a multiple of %v`
maxIncFail = `%s in %s should be less than or equal to %v`
maxExcFail = `%s in %s should be less than %v`
minIncFail = `%s in %s should be greater than or equal to %v`
minExcFail = `%s in %s should be greater than %v`
uniqueFail = `%s in %s shouldn't contain duplicates`
maxItemsFail = `%s in %s should have at most %d items`
minItemsFail = `%s in %s should have at least %d items`
invalidType = `%s is an invalid type name`
typeFail = `%s in %s must be of type %s`
typeFailWithData = `%s in %s must be of type %s: %q`
typeFailWithError = `%s in %s must be of type %s, because: %s`
requiredFail = `%s in %s is required`
tooLongMessage = `%s in %s should be at most %d chars long`
tooShortMessage = `%s in %s should be at least %d chars long`
patternFail = `%s in %s should match '%s'`
enumFail = `%s in %s should be one of %v`
mulitpleOfFail = `%s in %s should be a multiple of %v`
maxIncFail = `%s in %s should be less than or equal to %v`
maxExcFail = `%s in %s should be less than %v`
minIncFail = `%s in %s should be greater than or equal to %v`
minExcFail = `%s in %s should be greater than %v`
uniqueFail = `%s in %s shouldn't contain duplicates`
maxItemsFail = `%s in %s should have at most %d items`
minItemsFail = `%s in %s should have at least %d items`
typeFailNoIn = `%s must be of type %s`
typeFailWithDataNoIn = `%s must be of type %s: %q`
typeFailWithErrorNoIn = `%s must be of type %s, because: %s`
requiredFailNoIn = `%s is required`
tooLongMessageNoIn = `%s should be at most %d chars long`
tooShortMessageNoIn = `%s should be at least %d chars long`
patternFailNoIn = `%s should match '%s'`
enumFailNoIn = `%s should be one of %v`
mulitpleOfFailNoIn = `%s should be a multiple of %v`
maxIncFailNoIn = `%s should be less than or equal to %v`
maxExcFailNoIn = `%s should be less than %v`
minIncFailNoIn = `%s should be greater than or equal to %v`
minExcFailNoIn = `%s should be greater than %v`
uniqueFailNoIn = `%s shouldn't contain duplicates`
maxItemsFailNoIn = `%s should have at most %d items`
minItemsFailNoIn = `%s should have at least %d items`
)

// CompositeValidationError an error to wrap a bunch of other errors
Expand Down Expand Up @@ -53,143 +69,229 @@ func InvalidTypeName(typeName string) *Validation {
// InvalidType creates an error for when the type is invalid
func InvalidType(name, in, typeName string, value interface{}) *Validation {
var message string
switch value.(type) {
case string:
message = fmt.Sprintf(typeFailWithData, name, in, typeName, value)
case error:
message = fmt.Sprintf(typeFailWithError, name, in, typeName, value)
default:
message = fmt.Sprintf(typeFail, name, in, typeName)

if in != "" {
switch value.(type) {
case string:
message = fmt.Sprintf(typeFailWithData, name, in, typeName, value)
case error:
message = fmt.Sprintf(typeFailWithError, name, in, typeName, value)
default:
message = fmt.Sprintf(typeFail, name, in, typeName)
}
} else {
switch value.(type) {
case string:
message = fmt.Sprintf(typeFailWithDataNoIn, name, typeName, value)
case error:
message = fmt.Sprintf(typeFailWithErrorNoIn, name, typeName, value)
default:
message = fmt.Sprintf(typeFailNoIn, name, typeName)
}
}

return &Validation{
code: 422,
Name: name,
In: in,
Value: value,
message: message,
}

}

// DuplicateItems error for when an array contains duplicates
func DuplicateItems(name, in string) *Validation {
msg := fmt.Sprintf(uniqueFail, name, in)
if in == "" {
msg = fmt.Sprintf(uniqueFailNoIn, name)
}
return &Validation{
code: 422,
Name: name,
In: in,
message: fmt.Sprintf(uniqueFail, name, in),
message: msg,
}
}

// TooManyItems error for when an array contains too many items
func TooManyItems(name, in string, max int64) *Validation {
msg := fmt.Sprintf(maxItemsFail, name, in, max)
if in == "" {
msg = fmt.Sprintf(maxItemsFailNoIn, name, max)
}

return &Validation{
code: 422,
Name: name,
In: in,
message: fmt.Sprintf(maxItemsFail, name, in, max),
message: msg,
}
}

// TooFewItems error for when an array contains too few items
func TooFewItems(name, in string, min int64) *Validation {
msg := fmt.Sprintf(minItemsFail, name, in, min)
if in == "" {
msg = fmt.Sprintf(minItemsFailNoIn, name, min)
}
return &Validation{
code: 422,
Name: name,
In: in,
message: fmt.Sprintf(minItemsFail, name, in, min),
message: msg,
}
}

// ExceedsMaximum error for when maxinum validation fails
func ExceedsMaximum(name, in string, max float64, exclusive bool) *Validation {
message := maxIncFail
if exclusive {
message = maxExcFail
var message string
if in == "" {
m := maxIncFailNoIn
if exclusive {
m = maxExcFailNoIn
}
message = fmt.Sprintf(m, name, max)
} else {
m := maxIncFail
if exclusive {
m = maxExcFail
}
message = fmt.Sprintf(m, name, in, max)
}
return &Validation{
code: 422,
Name: name,
In: in,
Value: max,
message: fmt.Sprintf(message, name, in, max),
message: message,
}
}

// ExceedsMinimum error for when maxinum validation fails
func ExceedsMinimum(name, in string, min float64, exclusive bool) *Validation {
message := minIncFail
if exclusive {
message = minExcFail
var message string
if in == "" {
m := minIncFailNoIn
if exclusive {
m = minExcFailNoIn
}
message = fmt.Sprintf(m, name, min)
} else {
m := minIncFail
if exclusive {
m = minExcFail
}
message = fmt.Sprintf(m, name, in, min)
}
return &Validation{
code: 422,
Name: name,
In: in,
Value: min,
message: fmt.Sprintf(message, name, in, min),
message: message,
}
}

// NotMultipleOf error for when multiple of validation fails
func NotMultipleOf(name, in string, multiple float64) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(mulitpleOfFailNoIn, name, multiple)
} else {
msg = fmt.Sprintf(mulitpleOfFail, name, in, multiple)
}
return &Validation{
code: 422,
Name: name,
In: in,
Value: multiple,
message: fmt.Sprintf(mulitpleOfFail, name, in, multiple),
message: msg,
}
}

// EnumFail error for when an enum validation fails
func EnumFail(name, in string, value interface{}, values []interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(enumFailNoIn, name, values)
} else {
msg = fmt.Sprintf(enumFail, name, in, values)
}

return &Validation{
code: 422,
Name: name,
In: in,
Value: value,
Values: values,
message: fmt.Sprintf(enumFail, name, in, values),
message: msg,
}
}

// Required error for when a value is missing
func Required(name, in string) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(requiredFailNoIn, name)
} else {
msg = fmt.Sprintf(requiredFail, name, in)
}
return &Validation{
code: 422,
Name: name,
In: in,
message: fmt.Sprintf(requiredFail, name, in),
message: msg,
}
}

// TooLong error for when a string is too long
func TooLong(name, in string, max int64) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(tooLongMessageNoIn, name, max)
} else {
msg = fmt.Sprintf(tooLongMessage, name, in, max)
}
return &Validation{
code: 422,
Name: name,
In: in,
message: fmt.Sprintf(tooLongMessage, name, in, max),
message: msg,
}
}

// TooShort error for when a string is too short
func TooShort(name, in string, min int64) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(tooShortMessageNoIn, name, min)
} else {
msg = fmt.Sprintf(tooShortMessage, name, in, min)
}

return &Validation{
code: 422,
Name: name,
In: in,
message: fmt.Sprintf(tooShortMessage, name, in, min),
message: msg,
}
}

// FailedPattern error for when a string fails a regex pattern match
// the pattern that is returned is the ECMA syntax version of the pattern not the golang version.
func FailedPattern(name, in, pattern string) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(patternFailNoIn, name, pattern)
} else {
msg = fmt.Sprintf(patternFail, name, in, pattern)
}

return &Validation{
code: 422,
Name: name,
In: in,
message: fmt.Sprintf(patternFail, name, in, pattern),
message: msg,
}
}
Loading

0 comments on commit d4effc7

Please sign in to comment.