jupyterlab/pull-requests

Add OpenAPI description of the extension API

fcollonval opened this issue · 3 comments

Or better #16 (comment)

  • Describe Python object with Pydantic
  • Generate json-schema from it
  • Generate TS from schema

As I may have mentioned over there: having one of the implementations be the source of truth is convenient... for the source of truth. In particular, pydantic has some oddments around optional fields, e.g. not set != _ set by 'null_ that has bitten me a few times. Further, it's another (non-trivial) dependency, while jsonschema, for all its warts, is already guaranteed to be around... nothing much else in the Jupyter python stack (above tornado) is typed yet, so some of the benefits around static typing are hard to realize at present.

In general, I've encountered fewer surprises when a project:

  • maintains a single JSON/YAML/TOML spec that gets updated
  • generates the required typings/stubs on either side of the exchange
  • checks in the generated types
    • as they become very useful in history
    • they shouldn't change much over time, but when they do, it's important and worthy of review

This also prevents some chicken-and-egg scenarios, letting frontend and backend be tested independently of one another (though the tooling itself has to live somewhere). If it is tolerable to have to do a JS build first, the output of labextension build will copy the schema folder, unchanged, I'm pretty sure, making it a good place to live.

  • generates the required typings/stubs on either side of the exchange

@bollwyvl Any recs for a good openapi -> python and/or openapi -> typescript stub generator?

The biggest player is OpenAPITools/openapi-generator. This generates entire opinionated servers/clients. We... don't want those opinions, yet, e.g. Flask. Nor do i want to sling enough Java to make one for tornado.

A related approach is connexion, which forces an app to use the OpenAPI spec to wire up the application, and doesn't turn on if the team forgets to implement a route (in Flask, tornado, or aiohttp). It kinda takes over everything, as one can imagine.

I prefer a lighter touch, of just the data model, and not get wrapped up in a specific application, as none of these things would know what the heck traitlets is babbling on about, anyway. For this, quicktype/quicktype has usually met my needs for python, while i don't even bother and go straight to the node-based stuff in the linked comment.

Except: if I already have a JSON schema validator around in sys.modules or... whatever webpack does... and am working in a language that doesn't really have runtime typechecking (my kingdom for deno/mypyc) ... I just validate the instances at runtime against the JSON schema. For get/post/patch/put, it's just part of the input/output... there's probably some nice decorator patterns possible.

For manager-kinda-things, that have mutable state with traitlets, making a Schema trait, which can be used anywhere I'd use an Any. Could be subclassed to be a SchemaList or a SchemaDict, etc.