예제 #1
0
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")
		}
	}
}
예제 #2
0
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)
				}
			}
		}
	}
}
예제 #3
0
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
}
예제 #4
0
// 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
}
예제 #5
0
// 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
}
예제 #6
0
// 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
}
예제 #7
0
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),
	}
}
예제 #8
0
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,
	}
}
예제 #9
0
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)
	}
}
예제 #10
0
// 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
}
예제 #11
0
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")
		}
	}
}
예제 #12
0
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)
	}
}
예제 #13
0
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)
	}
}
예제 #14
0
// 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
}
예제 #15
0
// 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
}
예제 #16
0
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)
	}
}
예제 #17
0
// 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()
}
예제 #18
0
// 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
}
예제 #19
0
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)
}
예제 #20
0
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)
}
예제 #21
0
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
}
예제 #22
0
// 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(),
	}
}
예제 #23
0
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
}
예제 #24
0
// 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
}
예제 #25
0
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")
}
예제 #26
0
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)
}
예제 #27
0
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
}
예제 #28
0
// 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()
}