Skip to content
This repository was archived by the owner on Aug 28, 2024. It is now read-only.

Commit 56e7033

Browse files
authored
feat: support references to other files (#76)
* test: add a failing multifile test * test: improve multi file tests * feat: compose two files * feat: resolve parts of other files * feat: find files with relative paths in external files * fix: single specification breaks resolveReference * chore: don’t throw an error when a reference is invalid * chore: clean up * docs: update README * refactor: move tests around * chore: clean up * chore: enable failing test * chore: enable reference example again * test: use correct helper method for externalPathItemRef test * chore: improve error message when URIs can’t be found * refactor: move uri not found error message to global errors * chore: remove resolveReferences from Validator class, not needed anymore * chore: clean up * chore: add TODO * fix: slashes in uris are not escaped properly * fix: broken import * refactor: make resolveReferences return an object * feat: properly return all errors * fix: TS issues * fix: tests * refactor: improve errors * docs: add a section about file references * docs(changeset): feat: file references * fix: validate function can’t find external references * chore: clean up * chore: enable one more test * refactor: merge lists of open api versions and specifications * refactor: simplify error message * refactor: simplify validate function * refactor: move all function to the utils folder * fix: demo doesn’t return any result
1 parent 36639d7 commit 56e7033

File tree

96 files changed

+831
-817
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+831
-817
lines changed

.changeset/spotty-chairs-divide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@scalar/openapi-parser": patch
3+
---
4+
5+
feat: file references

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ Modern OpenAPI parser written in TypeScript with support for OpenAPI 3.1, OpenAP
2020

2121
References are hard and the following features aren’t implemented yet (but will be in the future):
2222

23-
- references inside inside referenced files (recursion, yay)
24-
- circular references in referenced files (recursion inside recursion, yay)
25-
- URLs (low priority though)
23+
- URLs
2624

2725
## Installation
2826

@@ -130,6 +128,22 @@ const result = openapi()
130128
.get()
131129
```
132130

131+
## Advanced: File references
132+
133+
You can reference other files, too. To do that, the parser needs to know what files are available.
134+
135+
```ts
136+
import { loadFiles } from '@scalar/openapi-parser'
137+
138+
// load a file and all referenced files
139+
const filesystem = loadFiles('./openapi.yaml')
140+
// instead of just passing a single specification, pass the whole “filesystem”
141+
const result = await resolve(filesystem)
142+
```
143+
144+
You don’t have to use `loadFiles`, though. You just need to stick to the format. That enables you store the files
145+
wherever you want (maybe in a database?) or to use the package in a browser environment.
146+
133147
## Community
134148

135149
We are API nerds. You too? Let’s chat on Discord: <https://discord.gg/scalar>

packages/openapi-parser/src/configuration.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
import Ajv04 from 'ajv-draft-04'
22
import Ajv2020 from 'ajv/dist/2020'
33

4+
import Swagger20 from '../schemas/v2.0/schema.json'
5+
import OpenApi30 from '../schemas/v3.0/schema.json'
6+
import OpenApi31 from '../schemas/v3.1/schema.json'
7+
48
/**
5-
* A list of the supported OpenAPI versions
9+
* A list of the supported OpenAPI specifications
610
*/
7-
export const supportedVersions = ['2.0', '3.0', '3.1'] as const
11+
export const OpenApiSpecifications = {
12+
'2.0': Swagger20,
13+
'3.0': OpenApi30,
14+
'3.1': OpenApi31,
15+
}
816

9-
export type SupportedVersion = (typeof supportedVersions)[number]
17+
export type OpenApiVersion = keyof typeof OpenApiSpecifications
18+
19+
export const OpenApiVersions = Object.keys(
20+
OpenApiSpecifications,
21+
) as OpenApiVersion[]
1022

1123
/**
1224
* Configure available JSON Schema versions
@@ -24,11 +36,8 @@ export const ERRORS = {
2436
// URI_MUST_BE_STRING: 'uri parameter or $id attribute must be a string',
2537
OPENAPI_VERSION_NOT_SUPPORTED:
2638
'Cannot find supported Swagger/OpenAPI version in specification, version must be a string.',
27-
INVALID_REFERENCE: 'Can’t resolve URI: %s',
28-
EXTERNAL_REFERENCE_NOT_SUPPORTED:
29-
'External references are not supported yet: %s',
39+
INVALID_REFERENCE: 'Can’t resolve reference: %s',
40+
EXTERNAL_REFERENCE_NOT_FOUND: 'Can’t resolve external reference: %s',
3041
} as const
3142

3243
export type VALIDATOR_ERROR = keyof typeof ERRORS
33-
34-
export const inlinedRefs = 'x-inlined-refs'

packages/openapi-parser/src/lib/Validator/Validator.ts

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,21 @@
11
import addFormats from 'ajv-formats'
22

3-
import Swagger20 from '../../../schemas/v2.0/schema.json'
4-
import OpenApi30 from '../../../schemas/v3.0/schema.json'
5-
import OpenApi31 from '../../../schemas/v3.1/schema.json'
63
import {
74
ERRORS,
8-
type SupportedVersion,
9-
inlinedRefs,
5+
OpenApiSpecifications,
6+
type OpenApiVersion,
7+
OpenApiVersions,
108
jsonSchemaVersions,
11-
supportedVersions,
129
} from '../../configuration'
1310
import type { AnyObject, Filesystem, ValidateResult } from '../../types'
1411
import { details as getOpenApiVersion } from '../../utils'
15-
import { checkReferences } from './checkReferences'
16-
import { resolveReferences } from './resolveReferences'
17-
import { transformErrors } from './transformErrors'
18-
19-
// All available schemas
20-
const schemas = {
21-
'2.0': Swagger20,
22-
'3.0': OpenApi30,
23-
'3.1': OpenApi31,
24-
}
12+
import { resolveReferences } from '../../utils/resolveReferences'
13+
import { transformErrors } from '../../utils/transformErrors'
2514

2615
export class Validator {
2716
public version: string
2817

29-
public static supportedVersions = supportedVersions
18+
public static supportedVersions = OpenApiVersions
3019

3120
// Object with function *or* object { errors: string }
3221
protected ajvValidators: Record<
@@ -36,8 +25,6 @@ export class Validator {
3625
}
3726
> = {}
3827

39-
protected externalRefs: Record<string, AnyObject> = {}
40-
4128
protected errors: string
4229

4330
protected specificationVersion: string
@@ -46,12 +33,6 @@ export class Validator {
4633

4734
public specification: AnyObject
4835

49-
resolveReferences(filesystem?: Filesystem) {
50-
return resolveReferences(
51-
filesystem.find((file) => file.isEntrypoint === true).specification,
52-
)
53-
}
54-
5536
/**
5637
* Checks whether a specification is valid and all references can be resolved.
5738
*/
@@ -77,11 +58,6 @@ export class Validator {
7758
}
7859
}
7960

80-
// TODO: Do we want to keep external references in the spec?
81-
if (Object.keys(this.externalRefs).length > 0) {
82-
specification[inlinedRefs] = this.externalRefs
83-
}
84-
8561
// Meta data about the specification
8662
const { version, specificationType, specificationVersion } =
8763
getOpenApiVersion(specification)
@@ -105,11 +81,6 @@ export class Validator {
10581
const validateSchema = await this.getAjvValidator(version)
10682
const schemaResult = validateSchema(specification)
10783

108-
// Check if the references are valid
109-
if (schemaResult) {
110-
return checkReferences(entrypoint.specification)
111-
}
112-
11384
// Error handling
11485
if (validateSchema.errors) {
11586
if (validateSchema.errors.length > 0) {
@@ -120,6 +91,22 @@ export class Validator {
12091
}
12192
}
12293

94+
// Check if the references are valid
95+
if (schemaResult) {
96+
const resolvedReferences = resolveReferences(filesystem)
97+
98+
if (resolvedReferences.errors.length > 0) {
99+
return {
100+
valid: false,
101+
errors: resolvedReferences.errors,
102+
}
103+
} else {
104+
return {
105+
...resolvedReferences,
106+
}
107+
}
108+
}
109+
123110
// Whoops … no errors? Actually, that should never happen.
124111
return {
125112
valid: false,
@@ -136,14 +123,14 @@ export class Validator {
136123
/**
137124
* Ajv JSON schema validator
138125
*/
139-
async getAjvValidator(version: SupportedVersion) {
126+
async getAjvValidator(version: OpenApiVersion) {
140127
// Schema loaded already
141128
if (this.ajvValidators[version]) {
142129
return this.ajvValidators[version]
143130
}
144131

145132
// Load OpenAPI Schema
146-
const schema = schemas[version]
133+
const schema = OpenApiSpecifications[version]
147134

148135
// Load JSON Schema
149136
const AjvClass = jsonSchemaVersions[schema.$schema]

packages/openapi-parser/src/lib/Validator/_backup/resolveFromFilesystem.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)