Skip to content

Commit

Permalink
feat: add support for file:// prefix in refs
Browse files Browse the repository at this point in the history
  • Loading branch information
omissis committed Nov 8, 2023
1 parent 7f7ca3b commit 30d044f
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 33 deletions.
81 changes: 48 additions & 33 deletions pkg/generator/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ var (
errUnsupportedRefFormat = errors.New("unsupported $ref format")
errConflictSameFile = errors.New("conflict: same file")
errDefinitionDoesNotExistInSchema = errors.New("definition does not exist in schema")
errCannotGenerateReferencedType = errors.New("cannot generate referenced type")
)

func New(config Config) (*Generator, error) {
Expand Down Expand Up @@ -273,13 +274,27 @@ func (g *schemaGenerator) generateRootType() error {
}

func (g *schemaGenerator) generateReferencedType(ref string) (codegen.Type, error) {
var fileName, scope, defName string
if i := strings.IndexRune(ref, '#'); i == -1 {
fileName = ref
} else {
fileName, scope = ref[0:i], ref[i+1:]
refType, err := schemas.GetRefType(ref)
if err != nil {
return nil, fmt.Errorf("%w: %w", errCannotGenerateReferencedType, err)
}

if refType != schemas.RefTypeFile {
return nil, fmt.Errorf("%w: %w '%s'", errCannotGenerateReferencedType, errUnsupportedRefFormat, ref)
}

ref = strings.TrimPrefix(ref, "file://")

fileName := ref

var scope, defName string

if i := strings.IndexRune(ref, '#'); i != -1 {
var prefix string

fileName, scope = ref[0:i], ref[i+1:]
lowercaseScope := strings.ToLower(scope)

for _, currentPrefix := range []string{
"/$defs/", // Draft-handrews-json-schema-validation-02.
"/definitions/", // Legacy.
Expand All @@ -292,28 +307,46 @@ func (g *schemaGenerator) generateReferencedType(ref string) (codegen.Type, erro
}

if len(prefix) == 0 {
return nil, fmt.Errorf("%w; must point to definition within file: %q", errUnsupportedRefFormat, ref)
return nil, fmt.Errorf(
"%w: value must point to definition within file: '%s'",
errCannotGenerateReferencedType,
ref,
)
}

defName = scope[len(prefix):]
}

schema := g.schema
sg := g

if fileName != "" {
var err error
var serr error

schema, err = g.fileLoader.Load(fileName, g.schemaFileName)
if err != nil {
return nil, fmt.Errorf("could not follow $ref %q to file %q: %w", ref, fileName, err)
schema, serr = g.fileLoader.Load(fileName, g.schemaFileName)
if serr != nil {
return nil, fmt.Errorf("could not follow $ref %q to file %q: %w", ref, fileName, serr)
}

qualified, err := schemas.QualifiedFileName(fileName, g.schemaFileName, g.config.ResolveExtensions)
if err != nil {
return nil, fmt.Errorf("could not resolve qualified file name for %s: %w", fileName, err)
qualified, qerr := schemas.QualifiedFileName(fileName, g.schemaFileName, g.config.ResolveExtensions)
if qerr != nil {
return nil, fmt.Errorf("could not resolve qualified file name for %s: %w", fileName, qerr)
}

if err = g.addFile(qualified, schema); err != nil {
return nil, err
if ferr := g.addFile(qualified, schema); ferr != nil {
return nil, ferr
}

output, oerr := g.findOutputFileForSchemaID(schema.ID)
if oerr != nil {
return nil, oerr
}

sg = &schemaGenerator{
Generator: g.Generator,
schema: schema,
schemaFileName: fileName,
output: output,
}
}

Expand Down Expand Up @@ -355,24 +388,6 @@ func (g *schemaGenerator) generateReferencedType(ref string) (codegen.Type, erro
}()
}

var sg *schemaGenerator

if fileName != "" {
output, err := g.findOutputFileForSchemaID(schema.ID)
if err != nil {
return nil, err
}

sg = &schemaGenerator{
Generator: g.Generator,
schema: schema,
schemaFileName: fileName,
output: output,
}
} else {
sg = g
}

t, err := sg.generateDeclaredType(def, newNameScope(defName))
if err != nil {
return nil, err
Expand Down
45 changes: 45 additions & 0 deletions pkg/schemas/reference.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package schemas

import (
"errors"
"fmt"
"net/url"
)

var (
ErrEmptyReference = errors.New("reference is empty")
ErrUnsupportedRefFormat = errors.New("unsupported $ref format")
ErrCannotParseRef = errors.New("cannot parse $ref")
ErrUnsupportedRefSchema = errors.New("unsupported $ref schema")
ErrGetRefType = errors.New("cannot get $ref type")
)

type RefType string

const (
RefTypeFile RefType = "file"
RefTypeHTTP RefType = "http"
RefTypeHTTPS RefType = "https"
RefTypeUnknown RefType = "unknown"
)

func GetRefType(ref string) (RefType, error) {
urlRef, err := url.Parse(ref)
if err != nil {
return RefTypeUnknown, fmt.Errorf("%w: %w", ErrGetRefType, err)
}

switch urlRef.Scheme {
case "http":
return RefTypeHTTP, nil

case "https":
return RefTypeHTTPS, nil

case "file", "":
return RefTypeFile, nil

default:
return RefTypeUnknown, fmt.Errorf("%w: %w", ErrGetRefType, ErrUnsupportedRefSchema)
}
}
25 changes: 25 additions & 0 deletions tests/data/core/refExternalFileWithScheme.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions tests/data/core/refExternalFileWithScheme.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://example.com/refExternalFileWithScheme",
"type": "object",
"properties": {
"myExternalThing": {
"$ref": "file://./ref.json#/$defs/Thing"
},
"someOtherExternalThing": {
"$ref": "file://./ref.json#/$defs/Thing"
}
}
}

0 comments on commit 30d044f

Please sign in to comment.