Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(oas-normalize): reworking how .validate() works, new .convert() method #920

Merged
merged 9 commits into from
Dec 20, 2024

Conversation

erunion
Copy link
Member

@erunion erunion commented Dec 20, 2024

🐳 Context

Within our new Git backend we've run into a fun problem where with a refactor I'm working on to stop caching upconverted and dereferenced OpenAPI definitions in a database cache, instead opting to pull them straight from our Git filesystem, I have unfortunately did a number on an internal endpoint of ours' performance by reducing it from 300+ ops/sec to ~30.

The reason this happened is because in order for this endpoint to continue to return upconverted OpenAPI definitions (dereferencing is no longer wanted there) I have to run the file we have in Git, which may be an OpenAPI, Swagger, Postman, or YAML file, through the .validate() method in oas-normalize in order to do this conversion.

Because I already know that these API definitions are valid I just want to convert, however unfortunately oas-normalize doesn't have any available methods for this.

🧰 Changes

Important

These are breaking changes to oas-normalize.

In order to resolve the above conundrum I am heavily reworking how the existing .validate() method works to now only validate a given API definition. It will continue to convert Postman collections to OpenAPI in order to validate them as OpenAPI definitons (because we don't have a Postman validator), but will now just return a boolean in the event of the definition being valid, or throwing an error if it isn't.

For the conversion side of this that .validate() no longer handles I am introducing a new .convert() method that will do as it says: convert a given API definition to an OpenAPI JSON object. If it's already OpenAPI, and JSON, it'll return what's already there, but if it's Swagger or Postman it'll do the conversion to OpenAPI there and return the result of that -- the same behavior that .validate() previously contained.

Additionally with this, the .validate() method had a convertToLatest flag that allowed you, if you so wished, to not convert a Swagger definition to OpenAPI after validating it. I am removing this flag because as far as I could tell we were always supplying convertToLatest (it was the default behavior). Instead in its place, if you just want to validate a Swagger definition now, and not convert it to OpenAPI, don't feed it through .convert() after running .validate().

To be honest I always really disliked that .validate() would validate and convert; seemed like a weird dark pattern and potential footgun.

⏱️ Benchmarks

Because the work that spawned this was discovered through the process of writing a benchmark I'm also creating some new benchmarks here for the .validate() and .convert() methods. Diffing the current .validate() benchmark versus the new .convert() one you can see pretty clearly that this new .convert() method will greatly improve our performance over the current implementation and usage of .validate().

.validate() currently in main

validate-before

.validate() now

validate-after

.convert()

Screenshot 2024-12-19 at 4 11 12 PM

@erunion erunion added enhancement New feature or request refactor Issues about tackling technical debt labels Dec 20, 2024
@erunion erunion changed the title feat!(oas-normalize): reworking how .validate() works, new .convert() method feat(oas-normalize): reworking how .validate() works, new .convert() method Dec 20, 2024
Comment on lines +36 to +37
- name: Run benchmarks
run: npm run bench
Copy link
Member Author

@erunion erunion Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be rad if there were a github action to post in a PR comment if the benchmark results show any impacts, positive or neg, to performance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeahh 100%. Like i said in slack, i think you'd have to store the benchmark results somewhere and compare the current run to a known baseline or something.

@@ -1,7 +1,6 @@
import type { ComponentsObject, HttpMethods, OASDocument, TagObject } from '../types.js';

import jsonPointer from 'jsonpointer';
import { getAPIDefinitionType } from 'oas-normalize/lib/utils';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm decoupling oas-normalize from being a dependency in oas because the only thing it's using is this method which checks if a openapi property is present in an object. I don't really want us straying down an is-odd path of loading in a whole ass library for an easy one-liner.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty for this

});
```

### `#bundle()`
### `.load()`
Copy link
Member Author

@erunion erunion Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.load() has long been silently marked as private with a @private JSDoc but we actually use it in a number of apps of ours so might as well make it actually public and documented.

* @private
*/
static async convertPostmanToOpenAPI(schema: any) {
private static async convertPostmanToOpenAPI(schema: any) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making this one actually private because it's only used internally.

@erunion erunion marked this pull request as ready for review December 20, 2024 06:27
Copy link
Member

@domharrington domharrington left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!! Good job 👏

Copy link
Member

@kanadgupta kanadgupta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only blocking suggestion is the type change but otherwise very supportive of this! let's chat offline (maybe after the holiday) about rdme implications b/c i think we need to revisit how we handle API definition uploads in rdme@10

@@ -1,7 +1,6 @@
import type { ComponentsObject, HttpMethods, OASDocument, TagObject } from '../types.js';

import jsonPointer from 'jsonpointer';
import { getAPIDefinitionType } from 'oas-normalize/lib/utils';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty for this

packages/oas-normalize/test/index.test.ts Show resolved Hide resolved
packages/oas-normalize/src/index.ts Outdated Show resolved Hide resolved
packages/oas-normalize/README.md Show resolved Hide resolved
packages/oas-normalize/README.md Show resolved Hide resolved
packages/oas-normalize/README.md Outdated Show resolved Hide resolved
packages/oas-normalize/README.md Show resolved Hide resolved
erunion and others added 2 commits December 20, 2024 09:48
Co-authored-by: Kanad Gupta <git@kanad.dev>
@erunion erunion requested a review from kanadgupta December 20, 2024 17:53
packages/oas-normalize/README.md Outdated Show resolved Hide resolved
@erunion erunion merged commit 7b89f00 into main Dec 20, 2024
6 checks passed
@erunion erunion deleted the perf/oas-normalize branch December 20, 2024 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request refactor Issues about tackling technical debt
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants