Skip to content

Commit

Permalink
feat(openapi-ts#2056)!: support Pinia Colada
Browse files Browse the repository at this point in the history
Work in progress.
  • Loading branch information
mettekou committed Dec 22, 2024
1 parent d4689b1 commit 2fdc7fc
Show file tree
Hide file tree
Showing 16 changed files with 3,778 additions and 494 deletions.
5 changes: 5 additions & 0 deletions packages/openapi-pinia-colada/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.turbo
test
vitest.config.ts
tsconfig*.json
biome.json
7 changes: 7 additions & 0 deletions packages/openapi-pinia-colada/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# openapi-pinia-colada

## 0.0.1

### Patch Changes

- [#2060](https://github.com/openapi-ts/openapi-typescript/pull/2060) [`d4ae6c8`](https://github.com/openapi-ts/openapi-typescript/pull/2060/commits/d4ae6c8ec5549e317f09ee32c6a06f9e9c60d97e) Thanks [@mettekou](https://github.com/mettekou)! - Initial release
91 changes: 91 additions & 0 deletions packages/openapi-pinia-colada/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Contributing

Thanks for being willing to contribute! 🙏

**Working on your first Pull Request (PR)?** You can learn how from this free series [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github).

## Open issues

Please check out the [the open issues](https://github.com/openapi-ts/openapi-typescript/issues). Issues labelled [**Good First Issue**](https://github.com/openapi-ts/openapi-typescript/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) are especially good to start with.

Contributing doesn’t have to be in code. Simply answering questions in open issues or providing workarounds is as important as making pull requests.

## Writing code

### Setup

1. Install [pnpm](https://pnpm.io/)
2. [Fork this repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo) and clone your copy locally
3. Run `pnpm i` to install dependencies

### Testing

This library uses [Vitest](https://vitest.dev/) for testing. There’s a great [VS Code extension](https://marketplace.visualstudio.com/items?itemName=ZixuanChen.vitest-explorer) you can optionally use if you’d like in-editor debugging tools.

To run the entire test suite, run:

```bash
pnpm test
```

To run an individual test:

```bash
pnpm test -- [partial filename]
```

To start the entire test suite in watch mode:

```bash
npx vitest
```

#### TypeScript tests

**Don’t neglect writing TS tests!** In the test suite, you’ll see `// @ts-expect-error` comments. These are critical tests in and of themselves—they are asserting that TypeScript throws an error when it should be throwing an error (the test suite will actually fail in places if a TS error is _not_ raised).

As this is just a minimal fetch wrapper meant to provide deep type inference for API schemas, **testing TS types** is arguably more important than testing the runtime. So please make liberal use of `// @ts-expect-error`, and as a general rule of thumb, write more **unwanted** output tests than _wanted_ output tests.

### Running linting

Linting is handled via [Biome](https://biomejs.dev), a faster ESLint replacement. It was installed with `pnpm i` and can be run with:

```bash
pnpm run lint
```

### Changelogs

The changelog is generated via [changesets](https://github.com/changesets/changesets), and is separate from Git commit messages and pull request titles. To write a human-readable changelog for your changes, run:

```
npx changeset
```

This will ask if it’s a `patch`, `minor`, or `major` change ([semver](https://semver.org/)), along with a plain description of what you did. Commit this new file along with the rest of your PR, and during the next release this will go into the official changelog!

## Opening a Pull Request

Pull requests are **welcome** for this repo!

Bugfixes will always be accepted, though in some cases some small changes may be requested.

However, if adding a feature or breaking change, please **open an issue first to discuss.** This ensures no time or work is wasted writing code that won’t be accepted to the project (see [Project Goals](https://openapi-ts.dev/openapi-fetch/about/#project-goals)). Undiscussed feature work may be rejected at the discretion of the maintainers.

### Writing the commit

Create a new branch for your PR with `git checkout -b your-branch-name`. Add the relevant code as well as docs and tests. When you push everything up (`git push`), navigate back to your repo in GitHub and you should see a prompt to open a new PR.

While best practices for commit messages are encouraged (e.g. start with an imperative verb, keep it short, use the body if needed), this repo doesn’t follow any specific guidelines. Clarity is favored over strict rules. Changelogs are generated separately from git (see [the Changelogs section](#changelogs)).

### Writing the PR notes

**Please fill out the template!** It’s a very lightweight template 🙂.

### Adding docs

If you added a feature, or changed how something worked, please [update the docs](../../docs/)!

### Passing CI

All PRs must fix lint errors, and all tests must pass. PRs will not be merged until all CI checks are “green” (✅).
21 changes: 21 additions & 0 deletions packages/openapi-pinia-colada/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Dylan Meysmans

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
65 changes: 65 additions & 0 deletions packages/openapi-pinia-colada/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# openapi-pinia-colada

openapi-pinia-colada is a type-safe tiny wrapper (1 kb) around [`@pinia/colada`](https://pinia-colada.esm.dev/) to work with OpenAPI schema.

It works by using [`openapi-fetch`](../openapi-fetch) and [`openapi-typescript`](../openapi-typescript) so you get all the following features:

- ✅ No typos in URLs or params.
- ✅ All parameters, request bodies, and responses are type-checked and 100% match your schema
- ✅ No manual typing of your API
- ✅ Eliminates `any` types that hide bugs
- ✅ Eliminates `as` type overrides that can also hide bugs

## Setup

Install this library along with [`openapi-fetch`](../openapi-fetch) and [`openapi-typescript`](../openapi-typescript):

```bash
npm i openapi-pinia-colada openapi-fetch
npm i -D openapi-typescript typescript
```

Next, generate TypeScript types from your OpenAPI schema using openapi-typescript:

```bash
npx openapi-typescript ./path/to/api/v1.yaml -o ./src/lib/api/v1.d.ts
```

## Usage

Once your types have been generated from your schema, you can create a [fetch client](../openapi-fetch), a Pinia Colada client and start querying your API.

```vue
<script lang="ts" setup>
import createFetchClient from "openapi-fetch";
import createClient from "openapi-pinia-colada";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript
const fetchClient = createFetchClient<paths>({
baseUrl: "https://myapi.dev/v1/",
});
const $api = createClient(fetchClient);
const { data, error, isPending } = $api.useQuery(
"get",
"/blogposts/{post_id}",
{
params: {
path: { post_id: 5 },
},
}
);
</script>
<template>
<div v-if="isPending || !data">Loading...</div>
<div v-else-if="error">{{`An error occurred: ${error.message}`}}</div>
<div v-else>{{data.title}}</div>
</template>
```

> You can find more information about `createFetchClient` in the [openapi-fetch documentation](../openapi-fetch).
## 📓 Docs

[View Docs](https://openapi-ts.dev/openapi-pinia-colada/)
17 changes: 17 additions & 0 deletions packages/openapi-pinia-colada/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"extends": ["../../biome.json"],
"files": {
"ignore": ["./test/fixtures/"]
},
"linter": {
"rules": {
"complexity": {
"noBannedTypes": "off"
},
"suspicious": {
"noConfusingVoidType": "off"
}
}
}
}
83 changes: 83 additions & 0 deletions packages/openapi-pinia-colada/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"name": "openapi-pinia-colada",
"description": "Fast, type-safe @pinia/colada client to work with your OpenAPI schema.",
"version": "0.0.1",
"author": {
"name": "Dylan Meysmans",
"email": "[email protected]"
},
"license": "MIT",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./*": "./*"
},
"homepage": "https://openapi-ts.dev",
"repository": {
"type": "git",
"url": "https://github.com/openapi-ts/openapi-typescript",
"directory": "packages/openapi-pinia-colada"
},
"bugs": {
"url": "https://github.com/openapi-ts/openapi-typescript/issues"
},
"keywords": [
"openapi",
"swagger",
"rest",
"api",
"oapi_3",
"oapi_3_1",
"typescript",
"fetch",
"vue",
"pinia-colada",
"pinia"
],
"scripts": {
"build": "pnpm run build:clean && pnpm run build:esm && pnpm run build:cjs",
"build:clean": "del-cli dist",
"build:esm": "tsc -p tsconfig.build.json",
"build:cjs": "esbuild --bundle --platform=node --target=es2019 --outfile=dist/index.cjs --external:typescript src/index.ts",
"dev": "tsc -p tsconfig.build.json --watch",
"format": "biome format . --write",
"lint": "biome check .",
"generate-types": "openapi-typescript test/fixtures/api.yaml -o test/fixtures/api.d.ts",
"pretest": "pnpm run generate-types",
"test": "pnpm run \"/^test:/\"",
"test:js": "vitest run",
"test:ts": "tsc --noEmit",
"version": "pnpm run prepare && pnpm run build"
},
"dependencies": {
"openapi-typescript-helpers": "workspace:^"
},
"devDependencies": {
"@pinia/colada": "^0.13.0",
"@testing-library/vue": "^8.1.0",
"@vitejs/plugin-vue": "^5.2.1",
"del-cli": "^5.1.0",
"esbuild": "^0.24.0",
"execa": "^8.0.1",
"msw": "^2.7.0",
"openapi-fetch": "workspace:^",
"openapi-typescript": "workspace:^",
"vue": "^3.5.12"
},
"peerDependencies": {
"@pinia/colada": "^0.13.0",
"openapi-fetch": "workspace:^"
}
}
69 changes: 69 additions & 0 deletions packages/openapi-pinia-colada/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { type MaybeRef, unref } from "vue";
import {
type UseMutationOptions,
type UseMutationReturn,
type UseQueryOptions,
type UseQueryReturn,
useMutation as piniaColadaUseMutation,
useQuery as piniaColadaUseQuery,
} from "@pinia/colada";
import type { ClientMethod, FetchResponse, MaybeOptionalInit, Client as FetchClient } from "openapi-fetch";
import type { HttpMethod, MediaType, PathsWithMethod, RequiredKeysOf } from "openapi-typescript-helpers";

type InitWithUnknowns<Init> = Init & { [key: string]: unknown };

export const useQuery = <
Method extends HttpMethod,
Paths extends Record<string, Record<HttpMethod, unknown>>,
Media extends MediaType,
Path extends PathsWithMethod<Paths, Method>,
Init extends MaybeOptionalInit<Paths[Path], Method>,
Response extends Required<FetchResponse<Paths[Path][Method], Init, Media>>,
Options extends Omit<UseQueryOptions<Response["data"], Response["error"], Response["data"]>, "key" | "query">,
>(
fetchClient: MaybeRef<FetchClient<Paths, Media>>,
method: MaybeRef<Method>,
url: MaybeRef<PathsWithMethod<Paths, Method>>,
...[init, options]: RequiredKeysOf<Init> extends never
? [MaybeRef<InitWithUnknowns<Init>>?, MaybeRef<Options>?]
: [MaybeRef<InitWithUnknowns<Init>>, MaybeRef<Options>?]
): UseQueryReturn<Response["data"], Response["error"], Init> => {
const fetchClientValue = unref(fetchClient);
const methodValue = unref(method);
const urlValue = unref(url);
const initValue = unref(init);
const optionsValue = unref(options);
const mth = methodValue.toUpperCase() as Uppercase<typeof methodValue>;
const fn = fetchClientValue[mth] as ClientMethod<Paths, typeof method, Media>;

return piniaColadaUseQuery({
key: [methodValue, urlValue as string, initValue as InitWithUnknowns<typeof initValue>],
query: () => fn(urlValue, optionsValue),
...optionsValue,
});
};

export const useMutation = <
Method extends HttpMethod,
Paths extends Record<string, Record<HttpMethod, unknown>>,
Media extends MediaType,
Path extends PathsWithMethod<Paths, Method>,
Init extends MaybeOptionalInit<Paths[Path], Method>,
Response extends Required<FetchResponse<Paths[Path][Method], Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types
Options extends Omit<UseMutationOptions<Response["data"], Response["error"], Init>, "key" | "mutation">,
Vars,
>(
fetchClient: MaybeRef<FetchClient<Paths, Media>>,
method: MaybeRef<Method>,
url: MaybeRef<PathsWithMethod<Paths, Method>>,
options?: Options,
): UseMutationReturn<Response["data"], Vars, Response["error"]> => {
const fetchClientValue = unref(fetchClient);
const methodValue = unref(method);
const urlValue = unref(url);
const optionsValue = unref(options);
const mth = methodValue.toUpperCase() as Uppercase<typeof methodValue>;
const fn = fetchClientValue[mth] as ClientMethod<Paths, typeof method, Media>;

return piniaColadaUseMutation({ mutation: () => fn(urlValue, optionsValue), ...optionsValue });
};
Loading

0 comments on commit 2fdc7fc

Please sign in to comment.