// buildProperty converts a swagger 1.2 property to an open API property. func buildProperty(swaggerProperty swagger.NamedModelProperty) (openAPIProperty spec.Schema, err error) { if swaggerProperty.Property.Ref != nil { return spec.Schema{ SchemaProps: spec.SchemaProps{ Ref: spec.MustCreateRef("#/definitions/" + *swaggerProperty.Property.Ref), }, }, nil } openAPIProperty = spec.Schema{ SchemaProps: spec.SchemaProps{ Description: swaggerProperty.Property.Description, Default: getDefaultValue(swaggerProperty.Property.DefaultValue), Enum: make([]interface{}, len(swaggerProperty.Property.Enum)), }, } for i, e := range swaggerProperty.Property.Enum { openAPIProperty.Enum[i] = e } openAPIProperty.Minimum, err = getFloat64OrNil(swaggerProperty.Property.Minimum) if err != nil { return spec.Schema{}, err } openAPIProperty.Maximum, err = getFloat64OrNil(swaggerProperty.Property.Maximum) if err != nil { return spec.Schema{}, err } if swaggerProperty.Property.UniqueItems != nil { openAPIProperty.UniqueItems = *swaggerProperty.Property.UniqueItems } if swaggerProperty.Property.Items != nil { if swaggerProperty.Property.Items.Ref != nil { openAPIProperty.Items = &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Ref: spec.MustCreateRef("#/definitions/" + *swaggerProperty.Property.Items.Ref), }, }, } } else { openAPIProperty.Items = &spec.SchemaOrArray{ Schema: &spec.Schema{}, } openAPIProperty.Items.Schema.Type, openAPIProperty.Items.Schema.Format, err = buildType(swaggerProperty.Property.Items.Type, swaggerProperty.Property.Items.Format) if err != nil { return spec.Schema{}, err } } } openAPIProperty.Type, openAPIProperty.Format, err = buildType(swaggerProperty.Property.Type, swaggerProperty.Property.Format) if err != nil { return spec.Schema{}, err } return openAPIProperty, nil }
func (s *Spec) analyzeSchema(name string, schema spec.Schema, prefix string) { refURI := slashpath.Join(prefix, jsonpointer.Escape(name)) schRef := SchemaRef{ Name: name, Schema: &schema, Ref: spec.MustCreateRef("#" + refURI), } s.allSchemas["#"+refURI] = schRef if schema.Ref.String() != "" { s.references.addSchemaRef(refURI, schRef) } for k, v := range schema.Definitions { s.analyzeSchema(k, v, slashpath.Join(refURI, "definitions")) } for k, v := range schema.Properties { s.analyzeSchema(k, v, slashpath.Join(refURI, "properties")) } for k, v := range schema.PatternProperties { s.analyzeSchema(k, v, slashpath.Join(refURI, "patternProperties")) } for i, v := range schema.AllOf { s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "allOf")) } if len(schema.AllOf) > 0 { s.allOfs["#"+refURI] = SchemaRef{Name: name, Schema: &schema, Ref: spec.MustCreateRef("#" + refURI)} } for i, v := range schema.AnyOf { s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "anyOf")) } for i, v := range schema.OneOf { s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "oneOf")) } if schema.Not != nil { s.analyzeSchema("not", *schema.Not, refURI) } if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { s.analyzeSchema("additionalProperties", *schema.AdditionalProperties.Schema, refURI) } if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { s.analyzeSchema("additionalItems", *schema.AdditionalItems.Schema, refURI) } if schema.Items != nil { if schema.Items.Schema != nil { s.analyzeSchema("items", *schema.Items.Schema, refURI) } for i, sch := range schema.Items.Schemas { s.analyzeSchema(strconv.Itoa(i), sch, slashpath.Join(refURI, "items")) } } }
func getRefSchema(ref string) *spec.Schema { return &spec.Schema{ SchemaProps: spec.SchemaProps{ Ref: spec.MustCreateRef(ref), }, } }
func (isn *inlineSchemaNamer) Name(key string, schema *swspec.Schema, aschema *AnalyzedSchema) error { if swspec.Debug { log.Printf("naming inlined schema at %s", key) } parts := keyParts(key) for _, name := range namesFromKey(parts, aschema, isn.Operations) { if name != "" { // create unique name newName := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name)) // clone schema sch, err := cloneSchema(schema) if err != nil { return err } // replace values on schema if err := rewriteSchemaToRef(isn.Spec, key, swspec.MustCreateRef("#/definitions/"+newName)); err != nil { return fmt.Errorf("name inlined schema: %v", err) } sch.AddExtension("x-go-gen-location", genLocation(parts)) // fmt.Printf("{\n %q,\n \"\",\n spec.MustCreateRef(%q),\n \"\",\n},\n", key, "#/definitions/"+newName) // save cloned schema to definitions saveSchema(isn.Spec, newName, sch) } } return nil }
func knownRefs(base string) []spec.Ref { urls := []string{"bool", "string", "integer", "float", "date", "object", "format"} var result []spec.Ref for _, u := range urls { result = append(result, spec.MustCreateRef(fmt.Sprintf("%s/%s", base, path.Join("known", u)))) } return result }
func complexRefs(base string) []spec.Ref { urls := []string{"object", "array", "map"} var result []spec.Ref for _, u := range urls { result = append(result, spec.MustCreateRef(fmt.Sprintf("%s/%s", base, path.Join("complex", u)))) } return result }
func (s splitKey) PathItemRef() swspec.Ref { if len(s) < 3 { return swspec.Ref{} } pth, method := s[1], s[2] if _, validMethod := validMethods[strings.ToUpper(method)]; !validMethod && !strings.HasPrefix(method, "x-") { return swspec.Ref{} } return swspec.MustCreateRef("#" + path.Join("/", pths, jsonpointer.Escape(pth), strings.ToUpper(method))) }
func specSchemaFromJsonType(schema *jsonschema.Type) *spec.Schema { s := &spec.Schema{} if schema.Type != "" { s.Type = []string{schema.Type} } if schema.Ref != "" { s.Ref = spec.MustCreateRef(schema.Ref) } s.Format = schema.Format s.Required = schema.Required // currently there is no way to determine whether there is MaxLength or MinLength // defined. Need to fix jsonschema library and switch type from int to *int // s.MaxLength = schema.MaxLength // s.MinLength = schema.MinLength s.Pattern = schema.Pattern s.Enum = schema.Enum s.Default = schema.Default s.Title = schema.Title s.Description = schema.Description if schema.Items != nil { s.Items = &spec.SchemaOrArray{} s.Items.Schema = specSchemaFromJsonType(schema.Items) } if schema.Properties != nil { s.Properties = make(map[string]spec.Schema) for key, prop := range schema.Properties { s.Properties[key] = *specSchemaFromJsonType(prop) } } if schema.PatternProperties != nil { s.PatternProperties = make(map[string]spec.Schema) for key, prop := range schema.PatternProperties { s.PatternProperties[key] = *specSchemaFromJsonType(prop) } } switch string(schema.AdditionalProperties) { case "true": s.AdditionalProperties = &spec.SchemaOrBool{Allows: true} case "false": s.AdditionalProperties = &spec.SchemaOrBool{Allows: false} } return s }
func TestNameFromRef(t *testing.T) { values := []struct{ Source, Expected string }{ {"#/definitions/errorModel", "errorModel"}, {"http://somewhere.com/definitions/errorModel", "errorModel"}, {"http://somewhere.com/definitions/errorModel.json", "errorModel"}, {"/definitions/errorModel", "errorModel"}, {"/definitions/errorModel.json", "errorModel"}, {"http://somewhere.com", "somewhereCom"}, {"#", ""}, } for _, v := range values { assert.Equal(t, v.Expected, nameFromRef(spec.MustCreateRef(v.Source))) } }
func TestDefinitionName(t *testing.T) { values := []struct { Source, Expected string Definitions spec.Definitions }{ {"#/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)}, {"http://somewhere.com/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)}, {"#/definitions/errorModel", "errorModel", map[string]spec.Schema{"apples": *spec.StringProperty()}}, {"#/definitions/errorModel", "errorModelOAIGen", map[string]spec.Schema{"errorModel": *spec.StringProperty()}}, {"#/definitions/errorModel", "errorModelOAIGen1", map[string]spec.Schema{"errorModel": *spec.StringProperty(), "errorModelOAIGen": *spec.StringProperty()}}, {"#", "oaiGen", nil}, } for _, v := range values { assert.Equal(t, v.Expected, uniqifyName(v.Definitions, nameFromRef(spec.MustCreateRef(v.Source)))) } }
func buildParameter(restParam restful.ParameterData) (ret spec.Parameter, err error) { ret = spec.Parameter{ ParamProps: spec.ParamProps{ Name: restParam.Name, Description: restParam.Description, Required: restParam.Required, }, } switch restParam.Kind { case restful.BodyParameterKind: ret.In = "body" ret.Schema = &spec.Schema{ SchemaProps: spec.SchemaProps{ Ref: spec.MustCreateRef("#/definitions/" + restParam.DataType), }, } return ret, nil case restful.PathParameterKind: ret.In = "path" if !restParam.Required { return ret, fmt.Errorf("Path parameters should be marked at required for parameter %v", restParam) } case restful.QueryParameterKind: ret.In = "query" case restful.HeaderParameterKind: ret.In = "header" case restful.FormParameterKind: ret.In = "form" default: return ret, fmt.Errorf("Unknown restful operation kind : %v", restParam.Kind) } if !isSimpleDataType(restParam.DataType) { return ret, fmt.Errorf("Restful DataType should be a simple type, but got : %v", restParam.DataType) } ret.Type = restParam.DataType ret.Format = restParam.DataFormat ret.UniqueItems = !restParam.AllowMultiple // TODO(mbohlool): make sure the type of default value matches Type if restParam.DefaultValue != "" { ret.Default = restParam.DefaultValue } return ret, nil }
// buildOperations builds operations for each webservice path func (o *openAPI) buildOperations(route restful.Route, inPathCommonParamsMap map[interface{}]spec.Parameter) (*spec.Operation, error) { ret := &spec.Operation{ OperationProps: spec.OperationProps{ Description: route.Doc, Consumes: route.Consumes, Produces: route.Produces, ID: route.Operation, Schemes: o.protocolList, Responses: &spec.Responses{ ResponsesProps: spec.ResponsesProps{ StatusCodeResponses: make(map[int]spec.Response), }, }, }, } for _, resp := range route.ResponseErrors { ret.Responses.StatusCodeResponses[resp.Code] = spec.Response{ ResponseProps: spec.ResponseProps{ Description: resp.Message, Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Ref: spec.MustCreateRef("#/definitions/" + reflect.TypeOf(resp.Model).String()), }, }, }, } } if len(ret.Responses.StatusCodeResponses) == 0 { ret.Responses.Default = o.config.DefaultResponse } ret.Parameters = make([]spec.Parameter, 0) for _, param := range route.ParameterDocs { _, isCommon := inPathCommonParamsMap[mapKeyFromParam(param)] if !isCommon { openAPIParam, err := buildParameter(param.Data()) if err != nil { return ret, err } ret.Parameters = append(ret.Parameters, openAPIParam) } } sort.Sort(orderedParameters(ret.Parameters)) return ret, nil }
func gatherOperations(specDoc *Spec, operationIDs []string) map[string]opRef { var oprefs opRefs for method, pathItem := range specDoc.Operations() { for pth, operation := range pathItem { vv := *operation oprefs = append(oprefs, opRef{ Key: swag.ToGoName(strings.ToLower(method) + " " + pth), Method: method, Path: pth, ID: vv.ID, Op: &vv, Ref: swspec.MustCreateRef("#" + path.Join("/paths", jsonpointer.Escape(pth), method)), }) } } sort.Sort(oprefs) operations := make(map[string]opRef) for _, opr := range oprefs { nm := opr.ID if nm == "" { nm = opr.Key } oo, found := operations[nm] if found && oo.Method != opr.Method && oo.Path != opr.Path { nm = opr.Key } if len(operationIDs) == 0 || containsString(operationIDs, opr.ID) || containsString(operationIDs, nm) { opr.ID = nm opr.Op.ID = nm operations[nm] = opr } } return operations }
func importExternalReferences(opts *FlattenOpts) error { groupedRefs := reverseIndexForSchemaRefs(opts) for refStr, entry := range groupedRefs { if !entry.Ref.HasFragmentOnly { if swspec.Debug { log.Printf("importing external schema for [%s] from %s", strings.Join(entry.Keys, ", "), refStr) } // resolve to actual schema sch, err := swspec.ResolveRefWithBase(opts.Swagger(), &entry.Ref, opts.ExpandOpts(false)) if err != nil { return err } if sch == nil { return fmt.Errorf("no schema found at %s for [%s]", refStr, strings.Join(entry.Keys, ", ")) } if swspec.Debug { log.Printf("importing external schema for [%s] from %s", strings.Join(entry.Keys, ", "), refStr) } // generate a unique name newName := uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref)) if swspec.Debug { log.Printf("new name for [%s]: %s", strings.Join(entry.Keys, ", "), newName) } // rewrite the external refs to local ones for _, key := range entry.Keys { if err := updateRef(opts.Swagger(), key, swspec.MustCreateRef("#"+path.Join("/definitions", newName))); err != nil { return err } } // add the resolved schema to the definitions saveSchema(opts.Swagger(), newName, sch) } } return nil }
func (o *openAPI) toSchema(typeName string, model interface{}) (_ *spec.Schema, err error) { if openAPIType, openAPIFormat := common.GetOpenAPITypeFormat(typeName); openAPIType != "" { return &spec.Schema{ SchemaProps: spec.SchemaProps{ Type: []string{openAPIType}, Format: openAPIFormat, }, }, nil } else { ref := "#/definitions/" + typeName if model != nil { ref, err = o.buildDefinitionForType(model) if err != nil { return nil, err } } return &spec.Schema{ SchemaProps: spec.SchemaProps{ Ref: spec.MustCreateRef(ref), }, }, nil } }
func TestNameInlinedSchemas(t *testing.T) { bp := filepath.Join(".", "fixtures", "nested_inline_schemas.yml") sp, err := loadSpec(bp) values := []struct { Key string Location string Ref spec.Ref }{ {"#/paths/~1some~1where~1{id}/parameters/1/schema/items", "#/definitions/postSomeWhereIdParamsBody/items", spec.MustCreateRef("#/definitions/postSomeWhereIdParamsBodyItems")}, {"#/paths/~1some~1where~1{id}/parameters/1/schema", "#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/postSomeWhereIdParamsBody")}, {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2/properties/name", "#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name")}, {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/1", "#/definitions/getSomeWhereIdParamsBodyRecord/items/1", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1")}, {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2", "#/definitions/getSomeWhereIdParamsBodyRecord/items/2", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2")}, {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record", "#/definitions/getSomeWhereIdParamsBodyOAIGen/properties/record", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecord")}, {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", "#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyOAIGen")}, {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2/properties/name", "#/definitions/getSomeWhereIdOKBodyRecordItems2/properties/name", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2Name")}, {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/1", "#/definitions/getSomeWhereIdOKBodyRecord/items/1", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems1")}, {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2", "#/definitions/getSomeWhereIdOKBodyRecord/items/2", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2")}, {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record", "#/definitions/getSomeWhereIdOKBody/properties/record", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecord")}, {"#/paths/~1some~1where~1{id}/get/responses/200/schema", "#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody")}, {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2/properties/name", "#/definitions/getSomeWhereIdDefaultBodyRecordItems2/properties/name", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2Name")}, {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/1", "#/definitions/getSomeWhereIdDefaultBodyRecord/items/1", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems1")}, {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2", "#/definitions/getSomeWhereIdDefaultBodyRecord/items/2", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2")}, {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record", "#/definitions/getSomeWhereIdDefaultBody/properties/record", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecord")}, {"#/paths/~1some~1where~1{id}/get/responses/default/schema", "#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBody")}, {"#/definitions/nestedThing/properties/record/items/2/allOf/1/additionalProperties", "#/definitions/nestedThingRecordItems2AllOf1/additionalProperties", spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1AdditionalProperties")}, {"#/definitions/nestedThing/properties/record/items/2/allOf/1", "#/definitions/nestedThingRecordItems2/allOf/1", spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1")}, {"#/definitions/nestedThing/properties/record/items/2/properties/name", "#/definitions/nestedThingRecordItems2/properties/name", spec.MustCreateRef("#/definitions/nestedThingRecordItems2Name")}, {"#/definitions/nestedThing/properties/record/items/1", "#/definitions/nestedThingRecord/items/1", spec.MustCreateRef("#/definitions/nestedThingRecordItems1")}, {"#/definitions/nestedThing/properties/record/items/2", "#/definitions/nestedThingRecord/items/2", spec.MustCreateRef("#/definitions/nestedThingRecordItems2")}, {"#/definitions/datedRecords/items/1", "#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/datedRecordsItems1")}, {"#/definitions/datedTaggedRecords/items/1", "#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/datedTaggedRecordsItems1")}, {"#/definitions/namedThing/properties/name", "#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/namedThingName")}, {"#/definitions/nestedThing/properties/record", "#/definitions/nestedThing/properties/record", spec.MustCreateRef("#/definitions/nestedThingRecord")}, {"#/definitions/records/items/0", "#/definitions/records/items/0", spec.MustCreateRef("#/definitions/recordsItems0")}, {"#/definitions/datedTaggedRecords/additionalItems", "#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/datedTaggedRecordsItemsAdditionalItems")}, {"#/definitions/otherRecords/items", "#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/otherRecordsItems")}, {"#/definitions/tags/additionalProperties", "#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tagsAdditionalProperties")}, } if assert.NoError(t, err) { err := nameInlinedSchemas(&FlattenOpts{ Spec: New(sp), BasePath: bp, }) if assert.NoError(t, err) { for i, v := range values { ptr, err := jsonpointer.New(v.Location[1:]) if assert.NoError(t, err, "at %d for %s", i, v.Key) { vv, _, err := ptr.Get(sp) if assert.NoError(t, err, "at %d for %s", i, v.Key) { switch tv := vv.(type) { case *spec.Schema: assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) case spec.Schema: assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) case *spec.SchemaOrBool: var sRef spec.Ref if tv != nil && tv.Schema != nil { sRef = tv.Schema.Ref } assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) case *spec.SchemaOrArray: var sRef spec.Ref if tv != nil && tv.Schema != nil { sRef = tv.Schema.Ref } assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) default: assert.Fail(t, "unknown type", "got %T", vv) } } } } } for k, rr := range New(sp).allSchemas { if !strings.HasPrefix(k, "#/responses") && !strings.HasPrefix(k, "#/parameters") { if rr.Schema != nil && rr.Schema.Ref.String() == "" && !rr.TopLevel { asch, err := Schema(SchemaOpts{Schema: rr.Schema, Root: sp, BasePath: bp}) if assert.NoError(t, err, "for key: %s", k) { if !asch.IsSimpleSchema { assert.Fail(t, "not a top level schema", "for key: %s", k) } } } } } } }
func (s splitKey) PathRef() swspec.Ref { if !s.IsOperation() { return swspec.Ref{} } return swspec.MustCreateRef("#" + path.Join("/", pths, jsonpointer.Escape(s[1]))) }
func TestImportExternalReferences(t *testing.T) { bp := filepath.Join(".", "fixtures", "external_definitions.yml") sp, err := loadSpec(bp) if assert.NoError(t, err) { values := []struct { Key string Ref spec.Ref }{ {"#/parameters/someParam/schema", spec.MustCreateRef("#/definitions/record")}, {"#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/record")}, {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/record")}, {"#/responses/someResponse/schema", spec.MustCreateRef("#/definitions/record")}, {"#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/record")}, {"#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/tag")}, {"#/definitions/namedAgain", spec.MustCreateRef("#/definitions/named")}, {"#/definitions/datedTag/allOf/1", spec.MustCreateRef("#/definitions/tag")}, {"#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, {"#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, {"#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/tag")}, {"#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/record")}, {"#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tag")}, {"#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/named")}, } for _, v := range values { // technically not necessary to run for each value, but if things go right // this is idempotent, so having it repeat shouldn't matter // this validates that behavior err := importExternalReferences(&FlattenOpts{ Spec: New(sp), BasePath: bp, }) if assert.NoError(t, err) { ptr, err := jsonpointer.New(v.Key[1:]) if assert.NoError(t, err) { vv, _, err := ptr.Get(sp) if assert.NoError(t, err) { switch tv := vv.(type) { case *spec.Schema: assert.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key) case spec.Schema: assert.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key) case *spec.SchemaOrBool: assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key) case *spec.SchemaOrArray: assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key) default: assert.Fail(t, "unknown type", "got %T", vv) } } } } } assert.Len(t, sp.Definitions, 11) assert.Contains(t, sp.Definitions, "tag") assert.Contains(t, sp.Definitions, "named") assert.Contains(t, sp.Definitions, "record") } }
func TestSplitKey(t *testing.T) { type KeyFlag uint64 const ( isOperation KeyFlag = 1 << iota isDefinition isSharedOperationParam isOperationParam isOperationResponse isDefaultResponse isStatusCodeResponse ) values := []struct { Key string Flags KeyFlag PathItemRef spec.Ref PathRef spec.Ref Name string }{ { "#/paths/~1some~1where~1{id}/parameters/1/schema", isOperation | isSharedOperationParam, spec.Ref{}, spec.MustCreateRef("#/paths/~1some~1where~1{id}"), "", }, { "#/paths/~1some~1where~1{id}/get/parameters/2/schema", isOperation | isOperationParam, spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), spec.MustCreateRef("#/paths/~1some~1where~1{id}"), "", }, { "#/paths/~1some~1where~1{id}/get/responses/default/schema", isOperation | isOperationResponse | isDefaultResponse, spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), spec.MustCreateRef("#/paths/~1some~1where~1{id}"), "Default", }, { "#/paths/~1some~1where~1{id}/get/responses/200/schema", isOperation | isOperationResponse | isStatusCodeResponse, spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), spec.MustCreateRef("#/paths/~1some~1where~1{id}"), "OK", }, { "#/definitions/namedAgain", isDefinition, spec.Ref{}, spec.Ref{}, "namedAgain", }, { "#/definitions/datedRecords/items/1", isDefinition, spec.Ref{}, spec.Ref{}, "datedRecords", }, { "#/definitions/datedRecords/items/1", isDefinition, spec.Ref{}, spec.Ref{}, "datedRecords", }, { "#/definitions/datedTaggedRecords/items/1", isDefinition, spec.Ref{}, spec.Ref{}, "datedTaggedRecords", }, { "#/definitions/datedTaggedRecords/additionalItems", isDefinition, spec.Ref{}, spec.Ref{}, "datedTaggedRecords", }, { "#/definitions/otherRecords/items", isDefinition, spec.Ref{}, spec.Ref{}, "otherRecords", }, { "#/definitions/tags/additionalProperties", isDefinition, spec.Ref{}, spec.Ref{}, "tags", }, { "#/definitions/namedThing/properties/name", isDefinition, spec.Ref{}, spec.Ref{}, "namedThing", }, } for i, v := range values { parts := keyParts(v.Key) pref := parts.PathRef() piref := parts.PathItemRef() assert.Equal(t, v.PathRef.String(), pref.String(), "pathRef: %s at %d", v.Key, i) assert.Equal(t, v.PathItemRef.String(), piref.String(), "pathItemRef: %s at %d", v.Key, i) if v.Flags&isOperation != 0 { assert.True(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i) } else { assert.False(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i) } if v.Flags&isDefinition != 0 { assert.True(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i) assert.Equal(t, v.Name, parts.DefinitionName(), "definition name: %s at %d", v.Key, i) } else { assert.False(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i) if v.Name != "" { assert.Equal(t, v.Name, parts.ResponseName(), "response name: %s at %d", v.Key, i) } } if v.Flags&isOperationParam != 0 { assert.True(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i) } else { assert.False(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i) } if v.Flags&isSharedOperationParam != 0 { assert.True(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i) } else { assert.False(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i) } if v.Flags&isOperationResponse != 0 { assert.True(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i) } else { assert.False(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i) } if v.Flags&isDefaultResponse != 0 { assert.True(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i) } else { assert.False(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i) } if v.Flags&isStatusCodeResponse != 0 { assert.True(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i) } else { assert.False(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i) } } }
func TestUpdateRef(t *testing.T) { bp := filepath.Join("fixtures", "external_definitions.yml") sp, err := loadSpec(bp) if assert.NoError(t, err) { values := []struct { Key string Ref spec.Ref }{ {"#/parameters/someParam/schema", spec.MustCreateRef("#/definitions/record")}, {"#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/record")}, {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/record")}, {"#/responses/someResponse/schema", spec.MustCreateRef("#/definitions/record")}, {"#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/record")}, {"#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/record")}, {"#/definitions/namedAgain", spec.MustCreateRef("#/definitions/named")}, {"#/definitions/datedTag/allOf/1", spec.MustCreateRef("#/definitions/tag")}, {"#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, {"#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, {"#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/tag")}, {"#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/record")}, {"#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tag")}, {"#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/named")}, } for _, v := range values { err := updateRef(sp, v.Key, v.Ref) if assert.NoError(t, err) { ptr, err := jsonpointer.New(v.Key[1:]) if assert.NoError(t, err) { vv, _, err := ptr.Get(sp) if assert.NoError(t, err) { switch tv := vv.(type) { case *spec.Schema: assert.Equal(t, v.Ref.String(), tv.Ref.String()) case spec.Schema: assert.Equal(t, v.Ref.String(), tv.Ref.String()) case *spec.SchemaOrBool: assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String()) case *spec.SchemaOrArray: assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String()) default: assert.Fail(t, "unknown type", "got %T", vv) } } } } } } }
func TestFlatten(t *testing.T) { bp := filepath.Join(".", "fixtures", "flatten.yml") sp, err := loadSpec(bp) values := []struct { Key string Location string Ref spec.Ref Expected interface{} }{ { "#/responses/notFound/schema", "#/responses/notFound/schema", spec.MustCreateRef("#/definitions/error"), nil, }, { "#/paths/~1some~1where~1{id}/parameters/0", "#/paths/~1some~1where~1{id}/parameters/0/name", spec.Ref{}, "id", }, { "#/paths/~1other~1place", "#/paths/~1other~1place/get/operationId", spec.Ref{}, "modelOp", }, { "#/paths/~1some~1where~1{id}/get/parameters/0", "#/paths/~1some~1where~1{id}/get/parameters/0/name", spec.Ref{}, "limit", }, { "#/paths/~1some~1where~1{id}/get/parameters/1", "#/paths/~1some~1where~1{id}/get/parameters/1/name", spec.Ref{}, "some", }, { "#/paths/~1some~1where~1{id}/get/parameters/2", "#/paths/~1some~1where~1{id}/get/parameters/2/name", spec.Ref{}, "other", }, { "#/paths/~1some~1where~1{id}/get/parameters/3", "#/paths/~1some~1where~1{id}/get/parameters/3/schema", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBody"), "", }, { "#/paths/~1some~1where~1{id}/get/responses/200", "#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody"), "", }, { "#/definitions/namedAgain", "", spec.MustCreateRef("#/definitions/named"), "", }, { "#/definitions/namedThing/properties/name", "", spec.MustCreateRef("#/definitions/named"), "", }, { "#/definitions/namedThing/properties/namedAgain", "", spec.MustCreateRef("#/definitions/namedAgain"), "", }, { "#/definitions/datedRecords/items/1", "", spec.MustCreateRef("#/definitions/record"), "", }, { "#/definitions/otherRecords/items", "", spec.MustCreateRef("#/definitions/record"), "", }, { "#/definitions/tags/additionalProperties", "", spec.MustCreateRef("#/definitions/tag"), "", }, { "#/definitions/datedTag/allOf/1", "", spec.MustCreateRef("#/definitions/tag"), "", }, { "#/definitions/nestedThingRecordItems2/allOf/1", "", spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1"), "", }, { "#/definitions/nestedThingRecord/items/1", "", spec.MustCreateRef("#/definitions/nestedThingRecordItems1"), "", }, { "#/definitions/nestedThingRecord/items/2", "", spec.MustCreateRef("#/definitions/nestedThingRecordItems2"), "", }, { "#/definitions/nestedThing/properties/record", "", spec.MustCreateRef("#/definitions/nestedThingRecord"), "", }, { "#/definitions/named", "#/definitions/named/type", spec.Ref{}, spec.StringOrArray{"string"}, }, { "#/definitions/error", "#/definitions/error/properties/id/type", spec.Ref{}, spec.StringOrArray{"integer"}, }, { "#/definitions/record", "#/definitions/record/properties/createdAt/format", spec.Ref{}, "date-time", }, { "#/definitions/getSomeWhereIdOKBody", "#/definitions/getSomeWhereIdOKBody/properties/record", spec.MustCreateRef("#/definitions/nestedThing"), nil, }, { "#/definitions/getSomeWhereIdParamsBody", "#/definitions/getSomeWhereIdParamsBody/properties/record", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecord"), nil, }, { "#/definitions/getSomeWhereIdParamsBodyRecord", "#/definitions/getSomeWhereIdParamsBodyRecord/items/1", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1"), nil, }, { "#/definitions/getSomeWhereIdParamsBodyRecord", "#/definitions/getSomeWhereIdParamsBodyRecord/items/2", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2"), nil, }, { "#/definitions/getSomeWhereIdParamsBodyRecordItems2", "#/definitions/getSomeWhereIdParamsBodyRecordItems2/allOf/0/format", spec.Ref{}, "date", }, { "#/definitions/getSomeWhereIdParamsBodyRecordItems2Name", "#/definitions/getSomeWhereIdParamsBodyRecordItems2Name/properties/createdAt/format", spec.Ref{}, "date-time", }, { "#/definitions/getSomeWhereIdParamsBodyRecordItems2", "#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name", spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name"), "date", }, } if assert.NoError(t, err) { err := Flatten(FlattenOpts{Spec: New(sp), BasePath: bp}) if assert.NoError(t, err) { for i, v := range values { pk := v.Key[1:] if v.Location != "" { pk = v.Location[1:] } ptr, err := jsonpointer.New(pk) if assert.NoError(t, err, "at %d for %s", i, v.Key) { d, _, err := ptr.Get(sp) if assert.NoError(t, err) { if v.Ref.String() != "" { switch s := d.(type) { case *spec.Schema: assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key) case spec.Schema: assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key) case *spec.SchemaOrArray: var sRef spec.Ref if s != nil && s.Schema != nil { sRef = s.Schema.Ref } assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) case *spec.SchemaOrBool: var sRef spec.Ref if s != nil && s.Schema != nil { sRef = s.Schema.Ref } assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) default: assert.Fail(t, "unknown type", "got %T at %d for %s", d, i, v.Key) } } else { assert.Equal(t, v.Expected, d) } } } } } } }