TL;DR

  • Python製のOpenAPIライブラリであるopenapi-coreを用いて分割されたファイルを利用できるのかを確認した。

  • openapi-core==0.14.2ではjsonschema.exceptions.RefResolutionErrorが発生し、依存ファイルを読み込めない。

  • Github Issueに同様の問題が報告されている1が、まだ修正はされていない。

目的

OpenAPIのドキュメントを記述するとファイルサイズが大きくなる。そのため ファイルを分割するための仕組みがOpenAPIには定義されている。

openapi-coreはPython製のOpenAPIライブラリである。今回はopenapi-coreで 分割されたファイルを利用できるのかを確認した。

ファイル構成

.
├── example.py
├── index.org
└── spec
    ├── index.yaml
    └── paths.yaml

1 directory, 4 files

example.py

OpenAPIのドキュメントを読み込むための簡易なスクリプト。

import yaml
from openapi_core import create_spec


with open("./spec/index.yaml") as fp:
    data = yaml.safe_load(fp)

print(create_spec(data))
print("OK")

spec/index.yaml

OpenAPIのドキュメントのroot。

openapi: '3.0.3'

info:
  title: Example
  version: '1'

paths:
  $ref: "./paths.yaml"

spec/paths.yaml

spec/index.yaml から読み込まれるドキュメント。

/:
  get:
    responses:
      '200':
        description: example

使用したライブラリとそのバージョン

openapi-core==0.14.2
openapi-schema-validator==0.1.5
openapi-spec-validator==0.3.1

実行

$ python3 example.py

結果

>>> Traceback (most recent call last):
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 774, in resolve_from_url
    document = self.store[url]
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/_utils.py", line 22, in __getitem__
    return self.store[self.normalize(uri)]
KeyError: './paths.yaml'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 777, in resolve_from_url
    document = self.resolve_remote(url)
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 863, in resolve_remote
    with urlopen(uri) as url:
  File "/usr/local/Cellar/[email protected]3.9/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/request.py", line 214, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/local/Cellar/[email protected]/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/request.py", line 501, in open
    req = Request(fullurl, data)
  File "/usr/local/Cellar/[email protected]/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/request.py", line 320, in __init__
    self.full_url = url
  File "/usr/local/Cellar/[email protected]/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/request.py", line 346, in full_url
    self._parse()
  File "/usr/local/Cellar/[email protected]/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/urllib/request.py", line 375, in _parse
    raise ValueError("unknown url type: %r" % self.full_url)
ValueError: unknown url type: './paths.yaml'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/ng/symdon/pages/posts/1626478280/example.py", line 8, in <module>
    print(create_spec(data))
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/openapi_core/spec/shortcuts.py", line 17, in create_spec

  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/openapi_spec_validator/validators.py", line 49, in validate
    for err in self.iter_errors(spec, spec_url=spec_url):
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/openapi_spec_validator/decorators.py", line 59, in wrapper
    for err in errors:
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/openapi_spec_validator/validators.py", line 58, in iter_errors
    for err in validator.iter_errors(spec):
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 328, in iter_errors
    for error in errors:
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/openapi_spec_validator/decorators.py", line 23, in wrapped
    for res in func(validator, schema_element, instance, schema):
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/_validators.py", line 282, in properties
    for error in validator.descend(
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 344, in descend
    for error in self.iter_errors(instance, schema):
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 328, in iter_errors
    for error in errors:
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/openapi_spec_validator/decorators.py", line 35, in wrapped
    with self.instance_resolver.resolving(ref) as target:
  File "/usr/local/Cellar/[email protected]/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/contextlib.py", line 117, in __enter__
    return next(self.gen)
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 754, in resolving
    url, resolved = self.resolve(ref)
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 766, in resolve
    return url, self._remote_cache(url)
  File "/Users/xxx/.venv/py39/lib/python3.9/site-packages/jsonschema/validators.py", line 779, in resolve_from_url
    raise exceptions.RefResolutionError(exc)
jsonschema.exceptions.RefResolutionError: unknown url type: './paths.yaml'
>>>

$refに読み込みができるパスを設定するために別の形式の値を指定する

上記のエラーで unknown url type というエラーメッセージが出力された。 そのためschemaを指定するなど、値を変更しつつ読み込み可能なものが無いか を試した。=file://絶対パス= を指定した場合、読み込むことができた。ただ しこれらの挙動はOpenAPIの仕様2とは一致しない。

エラーが発生しない例

file://絶対パス を指定した場合、読み込むことができた。

paths:
  $ref: "file:///opt/ng/symdon/pages/posts/1626478280/spec/paths.yaml"

エラーが発生する例

例1

paths:
  $ref: "file://paths.yaml"

発生するエラー

jsonschema.exceptions.RefResolutionError: [Errno 2] No such file or directory: '//paths.yaml'

例2

paths:
  $ref: "file://./paths.yaml"

発生するエラー

jsonschema.exceptions.RefResolutionError: [Errno 2] No such file or directory: '/paths.yaml'

例3

paths:
  $ref: "file://spec/paths.yaml"

発生するエラー

jsonschema.exceptions.RefResolutionError: [Errno 2] No such file or directory: '/paths.yaml'

同様の問題を報告しているissue

Githubに同様の問題が報告されていないか、Pull Requestがないかを確認した。 Pull Requestはなかったが、Issueはポストされてい た1。RefResolverの不適切な扱いについて報告さ れている。ここまでわかっていればすぐに修正ができそうである。

結論

できない。

脚注