Skip to content

Commit

Permalink
Operation refactor (#852)
Browse files Browse the repository at this point in the history
* add useOperation and useOas with Oas and Oas.Operation component

* chore: update examples

* chore: refactor oas react logic

* chore: operationgenerator cleanup

* chore: operations for tanstack query

* chore: update

* fix: use of `useOperationHelpers` to abstract operation logic

* chore: fix pluginManager mock

* chore: linting

* fix ts
  • Loading branch information
stijnvanhulle authored Mar 9, 2024
1 parent e2eed44 commit 752f9a0
Show file tree
Hide file tree
Showing 99 changed files with 1,432 additions and 800 deletions.
15 changes: 15 additions & 0 deletions .changeset/brave-parrots-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@kubb/swagger-tanstack-query": patch
"@kubb/swagger-client": patch
"@kubb/swagger-zodios": patch
"@kubb/swagger-faker": patch
"@kubb/swagger-msw": patch
"@kubb/swagger-swr": patch
"@kubb/swagger-zod": patch
"@kubb/swagger-ts": patch
"@kubb/swagger": patch
"@kubb/react": patch
"@kubb/core": patch
---

useOperation and useSchema with a component Oas and Oas.Operation
5 changes: 5 additions & 0 deletions .changeset/orange-dots-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kubb/swagger-zod": patch
---

use of `useOperationHelpers` to abstract operation logic
4 changes: 2 additions & 2 deletions docs/plugins/development/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const definePlugin = createPlugin<PluginOptions>((options) => {
name: pluginName,
options,
pre: [],
resolvePath(baseName, directory, options) {
resolvePath(baseName, mode, options) {
const root = path.resolve(this.config.root, this.config.output.path)

return path.resolve(root, output.path, baseName)
Expand Down Expand Up @@ -176,7 +176,7 @@ Add some extra functionality to your plugin, here you can even use functions whi

This will be called when pluginManager.resolvePath is called, see [Pluginmanager and resolving a path](/reference/pluginManager/#pluginmanager-resolvepath).

- **Type:** `(this: PluginContext, baseName: string, directory?: string | undefined, options?: object) => KubbFile.OptionalPath` <br/>
- **Type:** `(this: PluginContext, baseName: string, mode?: 'file' | 'directory', options?: object) => KubbFile.OptionalPath` <br/>

### resolveName

Expand Down
2 changes: 1 addition & 1 deletion docs/plugins/swagger/hooks/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ See [Oas](https://github.com/readmeio/oas) to understand how to use the `Oas` in
import { useOas } from '@kubb/react'

function Component() {
const oas = useOas()
const { oas } = useOas()

return null
}
Expand Down
4 changes: 2 additions & 2 deletions examples/client/templates/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ export const templates = {
const clientParams = [client.path.template, client.withData ? 'data' : undefined, 'options'].filter(Boolean).join(', ')

return (
<Editor language="typescript">
<>
<File.Import name="axios" path="axios" />
<Function name={name} async export generics={generics} returnType={returnType} params={params} JSDoc={JSDoc}>
{`return axios.${client.method}(${clientParams})`}
</Function>
</Editor>
</>
)
},
} as const
2 changes: 1 addition & 1 deletion examples/faker/petStore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ components:
example: 1
nationalityCode:
type: string
pattern: "^[A-Z]{2}$"
pattern: ^[A-Z]{2}$
xml:
name: user
Tag:
Expand Down
4 changes: 4 additions & 0 deletions examples/react-query-v5/kubb.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import createSwaggerTanstackQuery from '@kubb/swagger-tanstack-query'
import createSwaggerTS from '@kubb/swagger-ts'

import * as queryKey from './templates/queryKey/index'
import * as operations from './templates/operations/index'

/** @type {import('@kubb/core').UserConfig} */
export const config = {
Expand Down Expand Up @@ -57,6 +58,9 @@ export const config = {
},
},
}],
templates: {
operations: operations.templates,
},
}),
],
}
Expand Down
1 change: 1 addition & 0 deletions examples/react-query-v5/src/gen/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './operations'
export * from './useAddPetHook'
export * from './useCreateUserHook'
export * from './useCreateUsersWithListInputHook'
Expand Down
60 changes: 60 additions & 0 deletions examples/react-query-v5/src/gen/hooks/operations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export function updatePet() {
return '/pet'
}
export function addPet() {
return '/pet'
}
export function findPetsByStatus() {
return '/pet/findByStatus'
}
export function findPetsByTags() {
return '/pet/findByTags'
}
export function getPetById() {
return '/pet/{petId}'
}
export function updatePetWithForm() {
return '/pet/{petId}'
}
export function deletePet() {
return '/pet/{petId}'
}
export function uploadFile() {
return '/pet/{petId}/uploadImage'
}
export function getInventory() {
return '/store/inventory'
}
export function placeOrder() {
return '/store/order'
}
export function placeOrderPatch() {
return '/store/order'
}
export function getOrderById() {
return '/store/order/{orderId}'
}
export function deleteOrder() {
return '/store/order/{orderId}'
}
export function createUser() {
return '/user'
}
export function createUsersWithListInput() {
return '/user/createWithList'
}
export function loginUser() {
return '/user/login'
}
export function logoutUser() {
return '/user/logout'
}
export function getUserByName() {
return '/user/{username}'
}
export function updateUser() {
return '/user/{username}'
}
export function deleteUser() {
return '/user/{username}'
}
2 changes: 1 addition & 1 deletion examples/react-query-v5/src/gen/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './models/index'
export * from './hooks/index'
export * from './models/index'
44 changes: 44 additions & 0 deletions examples/react-query-v5/templates/operations/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Editor, Function, File, useFile, usePlugin } from '@kubb/react'
import { useOperations } from '@kubb/swagger/hooks'
import { Operations } from '@kubb/swagger-tanstack-query/components'
import React from 'react'
import { FileMeta, PluginOptions } from '@kubb/swagger-tanstack-query'
import { Operation } from '@kubb/swagger/oas'

export const templates = {
...Operations.templates,
editor: function({ children }: React.ComponentProps<typeof Operations.templates.editor>) {
const { key: pluginKey } = usePlugin<PluginOptions>()

const file = useFile({ name: 'operations', extName: '.ts', pluginKey })

return (
<Editor language="typescript">
<File<FileMeta>
baseName={file.baseName}
path={file.path}
meta={file.meta}
>
<File.Source>
{children}
</File.Source>
</File>
</Editor>
)
},
default: function({}: React.ComponentProps<typeof Operations.templates.default>) {
const operations = useOperations()

return (
<>
{operations.map((item) => {
return (
<Function name={item.getOperationId()} export>
return {JSON.stringify(item.path)}
</Function>
)
})}
</>
)
},
} as const
4 changes: 2 additions & 2 deletions examples/react-query-v5/templates/queryKey/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ export const templates = {
].filter(Boolean)

return (
<Editor language="typescript">
<>
<Function.Arrow name={name} export generics={generics} params={params} returnType={returnType} singleLine JSDoc={JSDoc}>
{`[${keys}] as const`}
</Function.Arrow>

<Type name={typeName} export>
{`ReturnType<typeof ${name}>`}
</Type>
</Editor>
</>
)
},
} as const
21 changes: 21 additions & 0 deletions packages/core/mocks/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { pascalCase } from '../src/transformers/casing.ts'

import { PluginManager } from '../src/PluginManager.ts'
import { readSync } from '../src/fs/read.ts'

export const mockedPluginManager = {
resolveName: ({ name, type }) => {
if (type === 'type') {
Expand All @@ -17,4 +19,23 @@ export const mockedPluginManager = {
on(eventName, args) {},
logLevel: 'info',
},
getFile: ({ name, extName, pluginKey }) => {
const baseName = `${name}${extName}`
let source = ''

try {
source = readSync(baseName)
} catch (_e) {
//
}

return {
path: baseName,
baseName,
meta: {
pluginKey,
},
source,
}
},
} as PluginManager
38 changes: 36 additions & 2 deletions packages/core/src/PluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import PQueue from 'p-queue'

import { readSync } from './fs/read.ts'
import { transformReservedWord } from './transformers/transformReservedWord.ts'
import { EventEmitter } from './utils/EventEmitter.ts'
import { setUniqueName } from './utils/uniqueName.ts'
Expand Down Expand Up @@ -73,6 +74,14 @@ type Events = {
error: [error: Error]
}

type GetFileProps<TOptions = object> = {
name: string
mode?: KubbFile.Mode
extName: KubbFile.Extname
pluginKey: Plugin['key']
options?: TOptions
}

export class PluginManager {
readonly plugins: PluginWithLifeCycle[]
readonly fileManager: FileManager
Expand Down Expand Up @@ -118,12 +127,37 @@ export class PluginManager {
return this
}

getFile<TOptions = object>({ name, mode, extName, pluginKey, options }: GetFileProps<TOptions>): KubbFile.File<{ pluginKey: Plugin['key'] }> {
let source = ''
const baseName = `${name}${extName}` as const
const path = this.resolvePath({ baseName, mode, pluginKey, options })

if (!path) {
throw new Error(`Filepath should be defined for resolvedName "${name}" and pluginKey [${JSON.stringify(pluginKey)}]`)
}

try {
source = readSync(path)
} catch (_e) {
//
}

return {
path,
baseName,
meta: {
pluginKey,
},
source,
}
}

resolvePath = <TOptions = object>(params: ResolvePathParams<TOptions>): KubbFile.OptionalPath => {
if (params.pluginKey) {
const paths = this.hookForPluginSync({
pluginKey: params.pluginKey,
hookName: 'resolvePath',
parameters: [params.baseName, params.directory, params.options as object],
parameters: [params.baseName, params.mode, params.options as object],
})

if (paths && paths?.length > 1 && this.logger.logLevel === LogLevel.debug) {
Expand All @@ -138,7 +172,7 @@ export class PluginManager {
}
return this.hookFirstSync({
hookName: 'resolvePath',
parameters: [params.baseName, params.directory, params.options as object],
parameters: [params.baseName, params.mode, params.options as object],
}).result
}
resolveName = (params: ResolveNameParams): string => {
Expand Down
15 changes: 5 additions & 10 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,6 @@ export type PluginFactoryOptions<
* When calling `resolvePath` you can specify better types.
*/
TResolvePathOptions extends object = object,
/**
* When using @kubb/react(based on React) you can specify here which types should be used when calling render.
* Always extend from `AppMeta` of the core.
*/
TAppMeta = unknown,
> = {
name: TName
/**
Expand All @@ -165,8 +160,8 @@ export type PluginFactoryOptions<
resolvePathOptions: TResolvePathOptions
appMeta: {
pluginManager: PluginManager
plugin: Plugin<PluginFactoryOptions<TName, TOptions, TResolvedOptions, TAPI, TResolvePathOptions, TAppMeta>>
} & TAppMeta
plugin: Plugin<PluginFactoryOptions<TName, TOptions, TResolvedOptions, TAPI, TResolvePathOptions>>
}
}

export type GetPluginFactoryOptions<TPlugin extends UserPlugin> = TPlugin extends UserPlugin<infer X> ? X : never
Expand Down Expand Up @@ -202,7 +197,7 @@ export type UserPlugin<TOptions extends PluginFactoryOptions = PluginFactoryOpti

export type UserPluginWithLifeCycle<TOptions extends PluginFactoryOptions = PluginFactoryOptions> = UserPlugin<TOptions> & PluginLifecycle<TOptions>

type UnknownUserPlugin = UserPlugin<PluginFactoryOptions<any, any, any, any, any, any>>
type UnknownUserPlugin = UserPlugin<PluginFactoryOptions<any, any, any, any, any>>

export type Plugin<TOptions extends PluginFactoryOptions = PluginFactoryOptions> =
& {
Expand Down Expand Up @@ -254,7 +249,7 @@ export type PluginLifecycle<TOptions extends PluginFactoryOptions = PluginFactor
* @type hookFirst
* @example ('./Pet.ts', './src/gen/') => '/src/gen/Pet.ts'
*/
resolvePath?: (this: PluginContext<TOptions>, baseName: string, directory?: string, options?: TOptions['resolvePathOptions']) => KubbFile.OptionalPath
resolvePath?: (this: PluginContext<TOptions>, baseName: string, mode?: KubbFile.Mode, options?: TOptions['resolvePathOptions']) => KubbFile.OptionalPath
/**
* Resolve to a name based on a string.
* Useful when converting to PascalCase or camelCase.
Expand Down Expand Up @@ -293,7 +288,7 @@ export type PluginCache = Record<string, [number, unknown]>
export type ResolvePathParams<TOptions = object> = {
pluginKey?: Plugin['key']
baseName: string
directory?: string | undefined
mode?: KubbFile.Mode
/**
* Options to be passed to 'resolvePath' 3th parameter
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/client/createRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createNode } from '../shared/dom.ts'
import { ReactTemplate } from '../shared/ReactTemplate.tsx'

import type { Logger } from '@kubb/core/logger'
import type { AppContextProps } from '../components/AppContext.ts'
import type { AppContextProps } from '../components/App.tsx'
import type { DOMElement } from '../types.ts'
import type { RootType } from './types.ts'

Expand Down
Loading

0 comments on commit 752f9a0

Please sign in to comment.