func TestValidatesExamplesAgainstSchema(t *testing.T) { tests := []string{ "response", "response-ref", } for _, tt := range tests { doc, err := loads.Spec(filepath.Join("fixtures", "validation", "valid-example-"+tt+".json")) if assert.NoError(t, err) { validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateExamplesValidAgainstSchema() assert.Empty(t, res.Errors, tt+" should not have errors") } doc, err = loads.Spec(filepath.Join("fixtures", "validation", "invalid-example-"+tt+".json")) if assert.NoError(t, err) { validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateExamplesValidAgainstSchema() assert.NotEmpty(t, res.Errors, tt+" should have errors") assert.Len(t, res.Errors, 1, tt+" should have 1 error") } } }
func TestGenerateServer_Parameters(t *testing.T) { specDoc, err := loads.Spec("../fixtures/codegen/todolist.discriminators.yml") if assert.NoError(t, err) { method, path, op, ok := analysis.New(specDoc.Spec()).OperationForName("modelOp") if assert.True(t, ok) { bldr := codeGenOpBuilder{ Name: "modelOp", Method: method, Path: path, APIPackage: "restapi", ModelsPackage: "models", Principal: "", Target: ".", Doc: specDoc, Analyzed: analysis.New(specDoc.Spec()), Operation: *op, Authed: false, DefaultScheme: "http", ExtraSchemas: make(map[string]GenSchema), } genOp, err := bldr.MakeOperation() if assert.NoError(t, err) { assert.True(t, genOp.Responses[200].Schema.IsBaseType) var buf bytes.Buffer err := parameterTemplate.Execute(&buf, genOp) if assert.NoError(t, err) { res := buf.String() assertInCode(t, "Pet models.Pet", res) assertInCode(t, "body, err := models.UnmarshalPet(r.Body, route.Consumer)", res) assertInCode(t, "o.Pet = body", res) } } } } }
func opBuilder(name, fname string) (codeGenOpBuilder, error) { if fname == "" { fname = "../fixtures/codegen/todolist.simple.yml" } specDoc, err := loads.Spec(fname) if err != nil { return codeGenOpBuilder{}, err } analyzed := analysis.New(specDoc.Spec()) method, path, op, ok := analyzed.OperationForName(name) if !ok { return codeGenOpBuilder{}, errors.New("No operation could be found for " + name) } return codeGenOpBuilder{ Name: name, Method: method, Path: path, APIPackage: "restapi", ModelsPackage: "models", Principal: "models.User", Target: ".", Operation: *op, Doc: specDoc, Analyzed: analyzed, Authed: false, ExtraSchemas: make(map[string]GenSchema), }, nil }
// Expanded expands the ref fields in the spec document and returns a new spec document func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) { swspec := new(spec.Swagger) if err := json.Unmarshal(d.raw, swspec); err != nil { return nil, err } var expandOptions *spec.ExpandOptions if len(options) > 0 { expandOptions = options[1] } else { expandOptions = &spec.ExpandOptions{ RelativeBase: filepath.Dir(d.specFilePath), } } if err := spec.ExpandSpec(swspec, expandOptions); err != nil { return nil, err } dd := &Document{ Analyzer: analysis.New(swspec), spec: swspec, schema: spec.MustLoadSwagger20Schema(), raw: d.raw, origSpec: d.origSpec, } return dd, nil }
// Analyzed creates a new analyzed spec document func Analyzed(data json.RawMessage, version string) (*Document, error) { if version == "" { version = "2.0" } if version != "2.0" { return nil, fmt.Errorf("spec version %q is not supported", version) } swspec := new(spec.Swagger) if err := json.Unmarshal(data, swspec); err != nil { return nil, err } origsqspec := new(spec.Swagger) if err := json.Unmarshal(data, origsqspec); err != nil { return nil, err } d := &Document{ Analyzer: analysis.New(swspec), schema: swag20Schema, spec: swspec, raw: data, origSpec: origsqspec, } return d, nil }
// NewRoutableContext creates a new context for a routable API func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Router) *Context { var an *analysis.Spec if spec != nil { an = analysis.New(spec.Spec()) } ctx := &Context{spec: spec, api: routableAPI, analyzer: an} return ctx }
func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder { return &defaultRouteBuilder{ spec: spec, analyzer: analysis.New(spec.Spec()), api: api, records: make(map[string][]denco.Record), } }
func newRoutableUntypedAPI(spec *loads.Document, api *untyped.API, context *Context) *routableUntypedAPI { var handlers map[string]map[string]http.Handler if spec == nil || api == nil { return nil } analyzer := analysis.New(spec.Spec()) for method, hls := range analyzer.Operations() { um := strings.ToUpper(method) for path, op := range hls { schemes := analyzer.SecurityDefinitionsFor(op) if oh, ok := api.OperationHandlerFor(method, path); ok { if handlers == nil { handlers = make(map[string]map[string]http.Handler) } if b, ok := handlers[um]; !ok || b == nil { handlers[um] = make(map[string]http.Handler) } var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // lookup route info in the context route, _ := context.RouteInfo(r) // bind and validate the request using reflection bound, validation := context.BindAndValidate(r, route) if validation != nil { context.Respond(w, r, route.Produces, route, validation) return } // actually handle the request result, err := oh.Handle(bound) if err != nil { // respond with failure context.Respond(w, r, route.Produces, route, err) return } // respond with success context.Respond(w, r, route.Produces, route, result) }) if len(schemes) > 0 { handler = newSecureAPI(context, handler) } handlers[um][path] = handler } } } return &routableUntypedAPI{ api: api, hlock: new(sync.Mutex), handlers: handlers, defaultProduces: api.DefaultProduces, defaultConsumes: api.DefaultConsumes, } }
func TestBuildDiscriminatorMap(t *testing.T) { specDoc, err := loads.Spec("../fixtures/codegen/todolist.discriminators.yml") if assert.NoError(t, err) { di := discriminatorInfo(analysis.New(specDoc.Spec())) assert.Len(t, di.Discriminators, 1) assert.Len(t, di.Discriminators["#/definitions/Pet"].Children, 2) assert.Len(t, di.Discriminated, 2) } }
// NewContext creates a new context wrapper func NewContext(spec *loads.Document, api *untyped.API, routes Router) *Context { var an *analysis.Spec if spec != nil { an = analysis.New(spec.Spec()) } ctx := &Context{spec: spec, analyzer: an} ctx.api = newRoutableUntypedAPI(spec, api, ctx) return ctx }
func TestValidateDefaultValueAgainstSchema(t *testing.T) { doc, _ := loads.Analyzed(PetStoreJSONMessage, "") validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateDefaultValueValidAgainstSchema() assert.Empty(t, res.Errors) tests := []string{ "parameter", "parameter-ref", "parameter-items", "header", "header-items", "schema", "schema-ref", "schema-additionalProperties", "schema-patternProperties", "schema-items", "schema-allOf", } for _, tt := range tests { doc, err := loads.Spec(filepath.Join("fixtures", "validation", "valid-default-value-"+tt+".json")) if assert.NoError(t, err) { validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateDefaultValueValidAgainstSchema() assert.Empty(t, res.Errors, tt+" should not have errors") } doc, err = loads.Spec(filepath.Join("fixtures", "validation", "invalid-default-value-"+tt+".json")) if assert.NoError(t, err) { validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateDefaultValueValidAgainstSchema() assert.NotEmpty(t, res.Errors, tt+" should have errors") assert.Len(t, res.Errors, 1, tt+" should have 1 error") } } }
func TestValidateBodyFormDataParams(t *testing.T) { doc, err := loads.Spec(filepath.Join("fixtures", "validation", "invalid-formdata-body-params.json")) if assert.NoError(t, err) { validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateDefaultValueValidAgainstSchema() assert.NotEmpty(t, res.Errors) assert.Len(t, res.Errors, 1) } }
func TestValidateReferencesValid(t *testing.T) { doc, err := loads.Spec(filepath.Join("fixtures", "validation", "valid-ref.json")) if assert.NoError(t, err) { validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateReferencesValid() assert.Empty(t, res.Errors) } doc, err = loads.Spec(filepath.Join("fixtures", "validation", "invalid-ref.json")) if assert.NoError(t, err) { validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateReferencesValid() assert.NotEmpty(t, res.Errors) assert.Len(t, res.Errors, 1) } }
// Validate validates the swagger spec func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Result) { var sd *loads.Document switch v := data.(type) { case *loads.Document: sd = v } if sd == nil { errs = sErr(errors.New(500, "spec validator can only validate spec.Document objects")) return } s.spec = sd s.analyzer = analysis.New(sd.Spec()) errs = new(Result) warnings = new(Result) schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats) var obj interface{} if err := json.Unmarshal(sd.Raw(), &obj); err != nil { errs.AddErrors(err) return } errs.Merge(schv.Validate(obj)) // error - if errs.HasErrors() { return // no point in continuing } errs.Merge(s.validateReferencesValid()) // error - if errs.HasErrors() { return // no point in continuing } errs.Merge(s.validateDuplicateOperationIDs()) errs.Merge(s.validateDuplicatePropertyNames()) // error - errs.Merge(s.validateParameters()) // error - errs.Merge(s.validateItems()) // error - errs.Merge(s.validateRequiredDefinitions()) // error - errs.Merge(s.validateDefaultValueValidAgainstSchema()) // error - errs.Merge(s.validateExamplesValidAgainstSchema()) // error - errs.Merge(s.validateNonEmptyPathParamNames()) warnings.Merge(s.validateUniqueSecurityScopes()) // warning warnings.Merge(s.validateReferenced()) // warning return }
// Expanded expands the ref fields in the spec document and returns a new spec document func (d *Document) Expanded() (*Document, error) { swspec := new(spec.Swagger) if err := json.Unmarshal(d.raw, swspec); err != nil { return nil, err } if err := spec.ExpandSpec(swspec); err != nil { return nil, err } dd := &Document{ Analyzer: analysis.New(swspec), spec: swspec, schema: swag20Schema, raw: d.raw, origSpec: d.origSpec, } return dd, nil }
func TestEmptyOperationNames(t *testing.T) { doc, err := loads.Spec("../fixtures/codegen/todolist.simple.yml") if assert.NoError(t, err) { sp := doc.Spec() sp.Paths.Paths["/tasks"].Post.ID = "" sp.Paths.Paths["/tasks"].Post.AddExtension("origName", "createTask") sp.Paths.Paths["/tasks/{id}"].Put.ID = "" sp.Paths.Paths["/tasks/{id}"].Put.AddExtension("origName", "updateTask") analyzed := analysis.New(sp) ops := gatherOperations(analyzed, nil) assert.Len(t, ops, 4) _, exists := ops["PostTasks"] assert.True(t, exists) _, exists = ops["PutTasksID"] assert.True(t, exists) } }
// NewAPI creates the default untyped API func NewAPI(spec *loads.Document) *API { var an *analysis.Spec if spec != nil && spec.Spec() != nil { an = analysis.New(spec.Spec()) } api := &API{ spec: spec, analyzer: an, consumers: make(map[string]runtime.Consumer, 10), producers: make(map[string]runtime.Producer, 10), authenticators: make(map[string]runtime.Authenticator), operations: make(map[string]map[string]runtime.OperationHandler), ServeError: errors.ServeError, Models: make(map[string]func() interface{}), formats: strfmt.NewFormats(), } return api.WithJSONDefaults() }
// Analyzed creates a new analyzed spec document func Analyzed(data json.RawMessage, version string) (*Document, error) { if version == "" { version = "2.0" } if version != "2.0" { return nil, fmt.Errorf("spec version %q is not supported", version) } raw := data trimmed := bytes.TrimSpace(data) if len(trimmed) > 0 { if trimmed[0] != '{' && trimmed[0] != '[' { yml, err := fmts.BytesToYAMLDoc(trimmed) if err != nil { return nil, fmt.Errorf("analyzed: %v", err) } d, err := fmts.YAMLToJSON(yml) if err != nil { return nil, fmt.Errorf("analyzed: %v", err) } raw = d } } swspec := new(spec.Swagger) if err := json.Unmarshal(raw, swspec); err != nil { return nil, err } origsqspec := new(spec.Swagger) if err := json.Unmarshal(raw, origsqspec); err != nil { return nil, err } d := &Document{ Analyzer: analysis.New(swspec), schema: spec.MustLoadSwagger20Schema(), spec: swspec, raw: raw, origSpec: origsqspec, } return d, nil }
func TestRouterBuilder(t *testing.T) { spec, api := petstore.NewAPI(t) analyzed := analysis.New(spec.Spec()) assert.Len(t, analyzed.RequiredConsumes(), 3) assert.Len(t, analyzed.RequiredProduces(), 5) assert.Len(t, analyzed.OperationIDs(), 4) // context := NewContext(spec, api) builder := petAPIRouterBuilder(spec, api, analyzed) getRecords := builder.records["GET"] postRecords := builder.records["POST"] deleteRecords := builder.records["DELETE"] assert.Len(t, getRecords, 2) assert.Len(t, postRecords, 1) assert.Len(t, deleteRecords, 1) assert.Empty(t, builder.records["PATCH"]) assert.Empty(t, builder.records["OPTIONS"]) assert.Empty(t, builder.records["HEAD"]) assert.Empty(t, builder.records["PUT"]) rec := postRecords[0] assert.Equal(t, rec.Key, "/pets") val := rec.Value.(*routeEntry) assert.Len(t, val.Consumers, 1) assert.Len(t, val.Producers, 1) assert.Len(t, val.Consumes, 1) assert.Len(t, val.Produces, 1) assert.Len(t, val.Parameters, 1) recG := getRecords[0] assert.Equal(t, recG.Key, "/pets") valG := recG.Value.(*routeEntry) assert.Len(t, valG.Consumers, 2) assert.Len(t, valG.Producers, 4) assert.Len(t, valG.Consumes, 2) assert.Len(t, valG.Produces, 4) assert.Len(t, valG.Parameters, 2) }
func TestValidateRequiredDefinitions(t *testing.T) { doc, _ := loads.Analyzed(PetStoreJSONMessage, "") validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateRequiredDefinitions() assert.Empty(t, res.Errors) // properties sw := doc.Spec() def := sw.Definitions["Tag"] def.Required = append(def.Required, "type") sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.NotEmpty(t, res.Errors) // pattern properties def.PatternProperties = make(map[string]spec.Schema) def.PatternProperties["ty.*"] = *spec.StringProperty() sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.Empty(t, res.Errors) def.PatternProperties = make(map[string]spec.Schema) def.PatternProperties["^ty.$"] = *spec.StringProperty() sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.NotEmpty(t, res.Errors) // additional properties def.PatternProperties = nil def.AdditionalProperties = &spec.SchemaOrBool{Allows: true} sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.Empty(t, res.Errors) def.AdditionalProperties = &spec.SchemaOrBool{Allows: false} sw.Definitions["Tag"] = def res = validator.validateRequiredDefinitions() assert.NotEmpty(t, res.Errors) }
func testAppGenertor(t testing.TB, specPath, name string) (*appGenerator, error) { specDoc, err := loads.Spec(specPath) if !assert.NoError(t, err) { return nil, err } analyzed := analysis.New(specDoc.Spec()) models, err := gatherModels(specDoc, nil) if !assert.NoError(t, err) { return nil, err } operations := gatherOperations(analyzed, nil) if len(operations) == 0 { return nil, errors.New("no operations were selected") } opts := testGenOpts() apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") return &appGenerator{ Name: appNameOrDefault(specDoc, name, "swagger"), Receiver: "o", SpecDoc: specDoc, Analyzed: analyzed, Models: models, Operations: operations, Target: ".", DumpData: opts.DumpData, Package: apiPackage, APIPackage: apiPackage, ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ServerPackage: mangleName(swag.ToFileName(opts.ServerPackage), "server"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), Principal: opts.Principal, DefaultScheme: "http", DefaultProduces: runtime.JSONMime, DefaultConsumes: runtime.JSONMime, GenOpts: &opts, }, nil }
// NewAPI creates the default untyped API func NewAPI(spec *loads.Document) *API { var an *analysis.Spec if spec != nil && spec.Spec() != nil { an = analysis.New(spec.Spec()) } return &API{ spec: spec, analyzer: an, DefaultProduces: runtime.JSONMime, DefaultConsumes: runtime.JSONMime, consumers: map[string]runtime.Consumer{ runtime.JSONMime: runtime.JSONConsumer(), }, producers: map[string]runtime.Producer{ runtime.JSONMime: runtime.JSONProducer(), }, authenticators: make(map[string]runtime.Authenticator), operations: make(map[string]map[string]runtime.OperationHandler), ServeError: errors.ServeError, Models: make(map[string]func() interface{}), formats: strfmt.NewFormats(), } }
func newAppGenerator(name string, modelNames, operationIDs []string, opts *GenOpts) (*appGenerator, error) { if opts.TemplateDir != "" { if err := templates.LoadDir(opts.TemplateDir); err != nil { return nil, err } } compileTemplates() // Load the spec _, specDoc, err := loadSpec(opts.Spec) if err != nil { return nil, err } analyzed := analysis.New(specDoc.Spec()) models, err := gatherModels(specDoc, modelNames) if err != nil { return nil, err } operations := gatherOperations(analyzed, operationIDs) if len(operations) == 0 { return nil, errors.New("no operations were selected") } defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = "http" } defaultProduces := opts.DefaultProduces if defaultProduces == "" { defaultProduces = runtime.JSONMime } defaultConsumes := opts.DefaultConsumes if defaultConsumes == "" { defaultConsumes = runtime.JSONMime } apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") return &appGenerator{ Name: appNameOrDefault(specDoc, name, "swagger"), Receiver: "o", SpecDoc: specDoc, Analyzed: analyzed, Models: models, Operations: operations, Target: opts.Target, // Package: filepath.Base(opts.Target), DumpData: opts.DumpData, Package: apiPackage, APIPackage: apiPackage, ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ServerPackage: mangleName(swag.ToFileName(opts.ServerPackage), "server"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), Principal: opts.Principal, DefaultScheme: defaultScheme, DefaultProduces: defaultProduces, DefaultConsumes: defaultConsumes, GenOpts: opts, }, nil }
// GenerateServerOperation generates a parameter model, parameter validator, http handler implementations for a given operation // It also generates an operation handler interface that uses the parameter model for handling a valid request. // Allows for specifying a list of tags to include only certain tags for the generation func GenerateServerOperation(operationNames, tags []string, includeHandler, includeParameters, includeResponses bool, opts GenOpts) error { if opts.TemplateDir != "" { if err := templates.LoadDir(opts.TemplateDir); err != nil { return err } } compileTemplates() // Load the spec _, specDoc, err := loadSpec(opts.Spec) if err != nil { return err } analyzed := analysis.New(specDoc.Spec()) ops := gatherOperations(analyzed, operationNames) for operationName, opRef := range ops { method, path, operation := opRef.Method, opRef.Path, opRef.Op defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = sHTTP } defaultProduces := opts.DefaultProduces if defaultProduces == "" { defaultProduces = runtime.JSONMime } defaultConsumes := opts.DefaultConsumes if defaultConsumes == "" { defaultConsumes = runtime.JSONMime } apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") serverPackage := mangleName(swag.ToFileName(opts.ServerPackage), "server") generator := operationGenerator{ Name: operationName, Method: method, Path: path, APIPackage: apiPackage, ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), ServerPackage: serverPackage, Operation: *operation, SecurityRequirements: analyzed.SecurityRequirementsFor(operation), Principal: opts.Principal, Target: filepath.Join(opts.Target, serverPackage), Base: opts.Target, Tags: tags, IncludeHandler: includeHandler, IncludeParameters: includeParameters, IncludeResponses: includeResponses, DumpData: opts.DumpData, DefaultScheme: defaultScheme, DefaultProduces: defaultProduces, DefaultConsumes: defaultConsumes, Doc: specDoc, Analyzed: analyzed, } if err := generator.Generate(); err != nil { return err } } return nil }
func TestValidateParameters(t *testing.T) { doc, _ := loads.Analyzed(PetStoreJSONMessage, "") validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateParameters() assert.Empty(t, res.Errors) sw := doc.Spec() sw.Paths.Paths["/pets"].Get.Parameters = append(sw.Paths.Paths["/pets"].Get.Parameters, *spec.QueryParam("limit").Typed("string", "")) res = validator.validateParameters() assert.NotEmpty(t, res.Errors) doc, _ = loads.Analyzed(PetStoreJSONMessage, "") sw = doc.Spec() sw.Paths.Paths["/pets"].Post.Parameters = append(sw.Paths.Paths["/pets"].Post.Parameters, *spec.BodyParam("fake", spec.RefProperty("#/definitions/Pet"))) validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res = validator.validateParameters() assert.NotEmpty(t, res.Errors) assert.Len(t, res.Errors, 1) assert.Contains(t, res.Errors[0].Error(), "has more than 1 body param") doc, _ = loads.Analyzed(PetStoreJSONMessage, "") sw = doc.Spec() pp := sw.Paths.Paths["/pets/{id}"] pp.Delete = nil var nameParams []spec.Parameter for _, p := range pp.Parameters { if p.Name == "id" { p.Name = "name" nameParams = append(nameParams, p) } } pp.Parameters = nameParams sw.Paths.Paths["/pets/{name}"] = pp validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res = validator.validateParameters() assert.NotEmpty(t, res.Errors) assert.Len(t, res.Errors, 1) assert.Contains(t, res.Errors[0].Error(), "overlaps with") doc, _ = loads.Analyzed(PetStoreJSONMessage, "") validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) sw = doc.Spec() pp = sw.Paths.Paths["/pets/{id}"] pp.Delete = nil pp.Get.Parameters = nameParams pp.Parameters = nil sw.Paths.Paths["/pets/{id}"] = pp res = validator.validateParameters() assert.NotEmpty(t, res.Errors) assert.Len(t, res.Errors, 2) assert.Contains(t, res.Errors[1].Error(), "is not present in path \"/pets/{id}\"") assert.Contains(t, res.Errors[0].Error(), "has no parameter definition") }
func TestValidateItems(t *testing.T) { doc, _ := loads.Analyzed(PetStoreJSONMessage, "") validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) res := validator.validateItems() assert.Empty(t, res.Errors) // in operation parameters sw := doc.Spec() sw.Paths.Paths["/pets"].Get.Parameters[0].Type = "array" res = validator.validateItems() assert.NotEmpty(t, res.Errors) sw.Paths.Paths["/pets"].Get.Parameters[0].Items = spec.NewItems().Typed("string", "") res = validator.validateItems() assert.Empty(t, res.Errors) sw.Paths.Paths["/pets"].Get.Parameters[0].Items = spec.NewItems().Typed("array", "") res = validator.validateItems() assert.NotEmpty(t, res.Errors) sw.Paths.Paths["/pets"].Get.Parameters[0].Items.Items = spec.NewItems().Typed("string", "") res = validator.validateItems() assert.Empty(t, res.Errors) // in global parameters sw.Parameters = make(map[string]spec.Parameter) sw.Parameters["other"] = *spec.SimpleArrayParam("other", "array", "csv") res = validator.validateItems() assert.Empty(t, res.Errors) //pp := spec.SimpleArrayParam("other", "array", "") //pp.Items = nil //sw.Parameters["other"] = *pp //res = validator.validateItems() //assert.NotEmpty(t, res.Errors) // in shared path object parameters doc, _ = loads.Analyzed(PetStoreJSONMessage, "") validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) sw = doc.Spec() pa := sw.Paths.Paths["/pets"] pa.Parameters = []spec.Parameter{*spec.SimpleArrayParam("another", "array", "csv")} sw.Paths.Paths["/pets"] = pa res = validator.validateItems() assert.Empty(t, res.Errors) pa = sw.Paths.Paths["/pets"] pp := spec.SimpleArrayParam("other", "array", "") pp.Items = nil pa.Parameters = []spec.Parameter{*pp} sw.Paths.Paths["/pets"] = pa res = validator.validateItems() assert.NotEmpty(t, res.Errors) // in body param schema doc, _ = loads.Analyzed(PetStoreJSONMessage, "") validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) sw = doc.Spec() pa = sw.Paths.Paths["/pets"] pa.Post.Parameters[0].Schema = spec.ArrayProperty(nil) res = validator.validateItems() assert.NotEmpty(t, res.Errors) // in response headers doc, _ = loads.Analyzed(PetStoreJSONMessage, "") validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) sw = doc.Spec() pa = sw.Paths.Paths["/pets"] rp := pa.Post.Responses.StatusCodeResponses[200] var hdr spec.Header hdr.Type = "array" rp.Headers = make(map[string]spec.Header) rp.Headers["X-YADA"] = hdr pa.Post.Responses.StatusCodeResponses[200] = rp res = validator.validateItems() assert.NotEmpty(t, res.Errors) // in response schema doc, _ = loads.Analyzed(PetStoreJSONMessage, "") validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default) validator.spec = doc validator.analyzer = analysis.New(doc.Spec()) sw = doc.Spec() pa = sw.Paths.Paths["/pets"] rp = pa.Post.Responses.StatusCodeResponses[200] rp.Schema = spec.ArrayProperty(nil) pa.Post.Responses.StatusCodeResponses[200] = rp res = validator.validateItems() assert.NotEmpty(t, res.Errors) }
func makeGenDefinitionHierarchy(name, pkg, container string, schema spec.Schema, specDoc *loads.Document, includeValidator, includeModel bool) (*GenDefinition, error) { receiver := "m" resolver := newTypeResolver("", specDoc) resolver.ModelName = name analyzed := analysis.New(specDoc.Spec()) di := discriminatorInfo(analyzed) pg := schemaGenContext{ Path: "", Name: name, Receiver: receiver, IndexVar: "i", ValueExpr: receiver, Schema: schema, Required: false, TypeResolver: resolver, Named: true, ExtraSchemas: make(map[string]GenSchema), Discrimination: di, Container: container, IncludeValidator: includeValidator, IncludeModel: includeModel, } if err := pg.makeGenSchema(); err != nil { return nil, err } dsi, ok := di.Discriminators["#/definitions/"+name] if ok { // when these 2 are true then the schema will render as an interface pg.GenSchema.IsBaseType = true pg.GenSchema.IsExported = true pg.GenSchema.DiscriminatorField = dsi.FieldName for _, v := range dsi.Children { if pg.GenSchema.Discriminates == nil { pg.GenSchema.Discriminates = make(map[string]string) } pg.GenSchema.Discriminates[v.FieldValue] = v.GoType } } dse, ok := di.Discriminated["#/definitions/"+name] if ok { pg.GenSchema.DiscriminatorField = dse.FieldName pg.GenSchema.DiscriminatorValue = dse.FieldValue pg.GenSchema.IsSubType = true // find the referenced definitions // check if it has a discriminator defined // when it has a discriminator get the schema and run makeGenSchema for it. // replace the ref with this new genschema swsp := specDoc.Spec() for i, ss := range schema.AllOf { ref := ss.Ref for ref.String() != "" { rsch, err := spec.ResolveRef(swsp, &ref) if err != nil { return nil, err } ref = rsch.Ref if rsch != nil && rsch.Ref.String() != "" { ref = rsch.Ref continue } ref = spec.Ref{} if rsch != nil && rsch.Discriminator != "" { gs, err := makeGenDefinitionHierarchy(strings.TrimPrefix(ss.Ref.String(), "#/definitions/"), pkg, pg.GenSchema.Name, *rsch, specDoc, pg.IncludeValidator, pg.IncludeModel) if err != nil { return nil, err } gs.GenSchema.IsBaseType = true gs.GenSchema.IsExported = true pg.GenSchema.AllOf[i] = gs.GenSchema schPtr := &(pg.GenSchema.AllOf[i]) if schPtr.AdditionalItems != nil { schPtr.AdditionalItems.IsBaseType = true } if schPtr.AdditionalProperties != nil { schPtr.AdditionalProperties.IsBaseType = true } for j := range schPtr.Properties { schPtr.Properties[j].IsBaseType = true schPtr.Properties[j].ValueExpression += "()" } } } } } var defaultImports []string if pg.GenSchema.HasValidations { defaultImports = []string{ "github.com/go-openapi/errors", "github.com/go-openapi/runtime", "github.com/go-openapi/validate", } } var extras []GenSchema var extraKeys []string for k := range pg.ExtraSchemas { extraKeys = append(extraKeys, k) } sort.Strings(extraKeys) for _, k := range extraKeys { extras = append(extras, pg.ExtraSchemas[k]) } return &GenDefinition{ Package: mangleName(filepath.Base(pkg), "definitions"), GenSchema: pg.GenSchema, DependsOn: pg.Dependencies, DefaultImports: defaultImports, ExtraSchemas: extras, }, nil }
// GenerateClient generates a client library for a swagger spec document. func GenerateClient(name string, modelNames, operationIDs []string, opts GenOpts) error { defer func() { typeMapping["binary"] = "io.ReadCloser" }() typeMapping["binary"] = "io.Writer" customFormatters["io.Writer"] = struct{}{} if opts.TemplateDir != "" { if err := templates.LoadDir(opts.TemplateDir); err != nil { return err } } compileTemplates() // Load the spec _, specDoc, err := loadSpec(opts.Spec) if err != nil { return err } analyzed := analysis.New(specDoc.Spec()) models, err := gatherModels(specDoc, modelNames) if err != nil { return err } operations := gatherOperations(analyzed, operationIDs) defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = sHTTP } defaultConsumes := opts.DefaultConsumes if defaultConsumes == "" { defaultConsumes = runtime.JSONMime } defaultProduces := opts.DefaultProduces if defaultProduces == "" { defaultProduces = runtime.JSONMime } generator := appGenerator{ Name: appNameOrDefault(specDoc, name, "rest"), SpecDoc: specDoc, Analyzed: analyzed, Models: models, Operations: operations, Target: opts.Target, DumpData: opts.DumpData, Package: mangleName(swag.ToFileName(opts.ClientPackage), "client"), APIPackage: mangleName(swag.ToFileName(opts.APIPackage), "api"), ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ServerPackage: mangleName(swag.ToFileName(opts.ServerPackage), "server"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), Principal: opts.Principal, DefaultScheme: defaultScheme, DefaultProduces: defaultProduces, DefaultConsumes: defaultConsumes, GenOpts: &opts, } generator.Receiver = "o" return (&clientGenerator{generator}).Generate() }