ExampleLoader_JoinFunc demonstrates how to use JoinFunc to load a multi-file OpenAPI spec from a virtual path scheme (e.g. git refs like "rev:file.yaml"). When loading specs from non-filesystem sources via LoadFromDataWithPath and ReadFromURIFunc, the base path may use a custom prefix convention. T
()
| 19 | // path resolution uses path.Dir which does not understand such prefixes. JoinFunc |
| 20 | // lets the caller override path resolution to preserve the prefix. |
| 21 | func ExampleLoader_JoinFunc() { |
| 22 | // Set up test files in a temp directory. |
| 23 | dir, _ := os.MkdirTemp("", "joinfunc-example") |
| 24 | defer os.RemoveAll(dir) |
| 25 | |
| 26 | root := `openapi: "3.0.0" |
| 27 | info: |
| 28 | title: Pet API |
| 29 | version: "1.0" |
| 30 | paths: {} |
| 31 | components: |
| 32 | schemas: |
| 33 | Pet: |
| 34 | $ref: "./schemas/pet.yaml" |
| 35 | ` |
| 36 | pet := `type: object |
| 37 | properties: |
| 38 | name: |
| 39 | type: string |
| 40 | ` |
| 41 | os.MkdirAll(filepath.Join(dir, "schemas"), 0o755) |
| 42 | os.WriteFile(filepath.Join(dir, "root.yaml"), []byte(root), 0o644) |
| 43 | os.WriteFile(filepath.Join(dir, "schemas", "pet.yaml"), []byte(pet), 0o644) |
| 44 | |
| 45 | // Use a "rev:" prefix to simulate a virtual path scheme. |
| 46 | const prefix = "rev:" |
| 47 | |
| 48 | loader := openapi3.NewLoader() |
| 49 | loader.IsExternalRefsAllowed = true |
| 50 | |
| 51 | // ReadFromURIFunc strips the prefix and reads from the real filesystem. |
| 52 | loader.ReadFromURIFunc = func(loader *openapi3.Loader, location *url.URL) ([]byte, error) { |
| 53 | p := location.Path |
| 54 | if strings.HasPrefix(p, prefix) { |
| 55 | p = p[len(prefix):] |
| 56 | } |
| 57 | return os.ReadFile(filepath.Join(dir, filepath.FromSlash(p))) |
| 58 | } |
| 59 | |
| 60 | // JoinFunc preserves the prefix when resolving relative $ref paths. |
| 61 | // Without this, path.Dir("rev:root.yaml") returns "." and $ref resolution breaks. |
| 62 | loader.JoinFunc = func(basePath *url.URL, relativePath *url.URL) *url.URL { |
| 63 | if basePath == nil { |
| 64 | return relativePath |
| 65 | } |
| 66 | result := *basePath |
| 67 | base := basePath.Path |
| 68 | if i := strings.IndexByte(base, ':'); i >= 0 { |
| 69 | pfx := base[:i+1] |
| 70 | filePart := base[i+1:] |
| 71 | result.Path = pfx + path.Join(path.Dir(filePart), relativePath.Path) |
| 72 | } else { |
| 73 | result.Path = path.Join(path.Dir(base), relativePath.Path) |
| 74 | } |
| 75 | return &result |
| 76 | } |
| 77 | |
| 78 | rootContent, _ := os.ReadFile(filepath.Join(dir, "root.yaml")) |
nothing calls this directly
no test coverage detected
searching dependent graphs…