Skip to content

Commit

Permalink
Merge pull request #2 from p10ns11y/package/spilt-utils
Browse files Browse the repository at this point in the history
Split packages
  • Loading branch information
p10ns11y authored Nov 8, 2024
2 parents 00e207c + c3fe7b5 commit 1825761
Show file tree
Hide file tree
Showing 26 changed files with 4,156 additions and 7,014 deletions.
14 changes: 8 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,18 @@ jobs:
- name: Build project
run: npx turbo run build

# pnpm will publish all packages in the monorepo
# --provenance for comprehensive metadata about their build process, enhancing their security and traceability
- name: Publish adaptate to npm
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_USER_TOKEN }}
run: npm publish --access public
run: pnpm publish --recursive --provenance --access public

- name: Publish @adaptate/core to npm
working-directory: packages/core
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --access public
# - name: Publish @adaptate/core to npm
# working-directory: packages/core
# env:
# NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# run: npm publish --provenance --access public

# - name: Publish @adaptate/core to GitHub Packages
# working-directory: packages/core
Expand Down
151 changes: 60 additions & 91 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Dynamic and Adaptable Model Validator Using Zod, Interoperable with OpenAPI

![Coverage Badge](/coverage-badge.svg)

## Overview

`adaptate` is a dynamic and adaptable model validator that leverages the power of Zod for schema validation and is interoperable with OpenAPI. This library allows you to define, validate, and transform schemas seamlessly.
Expand Down Expand Up @@ -170,104 +172,21 @@ const updatedSchema = applyConditionalRequirements(schema, config, data);

</details>

<details>
<summary>Converting OpenAPI Schema to Zod Schema (most commonly needed)</summary>

The utility is in the early stage and not one to one. For complete and advanced use cases check [json-schema-to-zod](https://snyk.io/advisor/npm-package/json-schema-to-zod)

```ts
import { openAPISchemaToZod } from 'adaptate';

const openAPISchema = {
type: 'object',
required: ['age'],
properties: {
name: { type: 'string' },
age: { type: 'number' },
},
};

const zodSchema = openAPISchemaToZod(openAPISchema);
```

</details>

<details>
<summary>Converting Zod Schema to OpenAPI Schema</summary>

The utility is in the early stage and not one to one. For complete and advanced use cases check [zod-to-json-schema](https://snyk.io/advisor/npm-package/zod-to-json-schema)

```ts
import { z } from 'zod';
import { zodToOpenAPISchema } from 'adaptate';

const zodSchema = z.object({
name: z.string(),
age: z.number(),
});

const openAPISchema = zodToOpenAPISchema(zodSchema);
```

</details>

<details>
<summary>
Loading schema directly from OpenAPI yml spec file (imaginary one)
</summary>
#### Converting OpenAPI Schema to Zod Schema (most commonly needed)

It is not exported something similar for your use case, you could build your own yml loader, spec parser that takes care of the usage of `$ref`.
Refer [@adaptate/utils README](/packages/utils/README.md#converting-openapi-schema-to-zod-schema-most-commonly-needed)

```ts
// loadAndResolveYAML.ts
import fs from 'node:fs';
import path, { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

import SwaggerParser from '@apidevtools/swagger-parser';
import yaml from 'js-yaml';

export async function loadAndResolveYAML(
fileURL: string,
relativePath: string
) {
try {
let fileURLPath = fileURLToPath(fileURL);
let callerDirectoryName = dirname(fileURLPath);
let yamlFilePath = path.resolve(callerDirectoryName, relativePath);
const openapiDocument = yaml.load(
fs.readFileSync(yamlFilePath, 'utf8')
) as string;

const dereferenced = await SwaggerParser.dereference(openapiDocument);

// For debugging uncomment this!
// console.log(JSON.stringify(dereferenced, null, 2));

return dereferenced;
} catch (error) {
console.error('Error:', error);
}
}
```
#### Converting Zod Schema to OpenAPI Schema

Example usage
Refer [@adaptate/utils README](/packages/utils/README.md#converting-zod-schema-to-openapi-schema)

```ts
let dataLoadedFromYAML = await loadAndResolveYAML(
import.meta.url,
'../fixtures/base-schema.yml' // relative path to spec yml file from where it is called
);
let dataZodSchema = openAPISchemaToZod(
dataLoadedFromYAML['components']['schemas']['Category']
);
```
#### Generate zod schemas(modules) from existing OpenAPI yml spec

</details>
Refer [@adaptate/utils README](/packages/utils/README.md#generate-zod-schemas-from-existing-openapi-spec)

## Credits

I have attempted to recreate what I have done at work with the help of ChatGPT Canvas model, the problem is simple and yet enough to test the muscle of code generators where the solution involved recursion and dealing with deep and nested data structures. It produced bugs and those are hard to figure out even for humans in the recursion context such as using correct APIs of the library (`required` instead of `unwrap`). I have tried to generate a minimal project with basic toolings. It did a decent job.
I have attempted to recreate what I have done at work with the help of **ChatGPT Canvas** model, the problem is simple and yet enough to test the muscle of code generators where the solution involved recursion and dealing with deep and nested data structures. It produced bugs and those are hard to figure out even for humans in the recursion context such as using correct APIs of the library (`required` instead of `unwrap`). I have tried to generate a minimal project with basic toolings. It did a decent job.

<details>
<summary> Suggestion vs Final edits</summary>
Expand Down Expand Up @@ -370,6 +289,56 @@ I have attempted to recreate what I have done at work with the help of ChatGPT C
}
```

### Apply Conditional Requirements (needs improvement)

You can apply conditional requirements to a Zod schema using the applyConditionalRequirements function. (Didn't work in improving it, generated by ChatGPT as is)

```ts
import { z } from 'zod';
import { applyConditionalRequirements } from 'adaptate';

const schema = z.object({
firstName: z.string().optional(),
secondName: z.string().optional(),
age: z.number().optional(),
address: z
.object({
street: z.string().optional(),
city: z.string().optional(),
})
.optional(),
title: z.string().optional(),
});

const config = {
age: true,
// explicit
firstName: {
requiredIf: (data: any) => data.age > 18,
},
// or implicit
secondName: (data) => !!data.firstName,
};

const data = { age: 20 };

const updatedSchema = applyConditionalRequirements(schema, config, data);
```

### Converting OpenAPI Schema to Zod Schema (most commonly needed)

Refer [@adaptate/utils README](/packages/utils/README.md#converting-openapi-schema-to-zod-schema-most-commonly-needed)

### Converting Zod Schema to OpenAPI Schema

The utility is in the early stage and not one to one. For complete and advanced use cases check [zod-to-json-schema](https://snyk.io/advisor/npm-package/zod-to-json-schema)

Refer [@adaptate/utils README](/packages/utils/README.md#converting-zod-schema-to-openapi-schema)

### Generate zod schemas(modules) from existing OpenAPI yml spec

Refer [@adaptate/utils README](/packages/utils/README.md#generate-zod-schemas-from-existing-openapi-spec)

</details>

[Full conversation with ChatGPT Canvas](https://chatgpt.com/share/6728eb4e-07f8-8005-b586-c4b8ee0e798c)
Expand All @@ -379,6 +348,6 @@ I have attempted to recreate what I have done at work with the help of ChatGPT C
<details>
<summary>The Background</summary>

At [Oneflow AB](https://oneflow.com), we faced a situation where a component was used on two different pages, each receiving data from different endpoints. This led to discrepancies in the properties of the same model for valid reasons. To avoid breaking the app, I have built a run-time validation library that abstracted business data extensively. Although it wasn't completely dynamic, it supported specifying business entities, types such as `collection` or `entity`, and reusable specifications like `relations`. It also included React-specific hooks that worked seamlessly with error boundaries. This effort aims to create a more generic solution that can be extended to various use cases.
At [Oneflow AB](https://oneflow.com), we faced a situation where a component was used on two different pages, each receiving data from different endpoints. This led to discrepancies in the properties of the same model for valid reasons. To avoid breaking the app, I have built a run-time validation library that abstracted business data extensively. Although it wasn't completely this sophisticated, it supported specifying business entities, types such as `collection` or `entity`, and reusable specifications like `relations` to reduce the verbosity in config definitions. It also included React-specific hooks that worked seamlessly with error boundaries. This effort aims to create a more generic solution that can be extended to various use cases.

</details>
1 change: 1 addition & 0 deletions coverage-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions jest.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export default {
],
},
extensionsToTreatAsEsm: ['.ts', '.tsx'],
coverageReporters: ['json-summary'],
};
Loading

0 comments on commit 1825761

Please sign in to comment.