From 933cc1bf9dd5ddf6d8d93b005abc509271062c0b Mon Sep 17 00:00:00 2001 From: Enzo Notario Date: Fri, 1 Nov 2024 20:41:38 -0300 Subject: [PATCH] fix(spec): generate defaultTag for operations that has no tags (#106) --- docs/composables/useTheme.md | 7 ++++--- package.json | 2 +- src/components/Path/OAPathsByTags.vue | 2 +- src/composables/useOpenapi.ts | 8 +++++--- src/composables/usePaths.ts | 2 +- src/composables/useSidebar.ts | 18 ++++++++++++++++-- src/composables/useTheme.ts | 16 +++++++++++----- src/lib/OpenApi.ts | 8 ++++---- src/lib/generateMissingTags.ts | 21 +++++++++++++++++++++ src/lib/transformSpec.ts | 2 ++ test/composables/openapi.test.ts | 13 +++---------- test/composables/useSidebar.test.ts | 6 ------ test/composables/useTheme.test.ts | 2 ++ 13 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 src/lib/generateMissingTags.ts diff --git a/docs/composables/useTheme.md b/docs/composables/useTheme.md index 5b1a0cf4..cd522163 100644 --- a/docs/composables/useTheme.md +++ b/docs/composables/useTheme.md @@ -90,6 +90,7 @@ export default { showPathsSummary: true, // Show a summary of the paths when grouping by tags. avoidCirculars: false, // Avoid circular references when parsing schemas. lazyRendering: false, // Lazy render Paths and Tags components. + defaultTag: 'Default', // Default tag to use when a path has no tags. }, }) } @@ -150,6 +151,6 @@ export default { ## Spec Configuration -| Function | Description | Default Value | Allowed Values | -|-----------------|------------------------------|-----------------------------------------------------------------------|-------------------------------------------------------------------------------| -| `setSpecConfig` | Sets the spec configuration. | `{ groupByTags: true, collapsePaths: false, showPathsSummary: true, avoidCirculars: false, lazyRendering: false }` | `{ groupByTags: boolean, collapsePaths: boolean, showPathsSummary: boolean, avoidCirculars: boolean, lazyRendering: boolean }` | +| Function | Description | Default Value | Allowed Values | +|-----------------|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| +| `setSpecConfig` | Sets the spec configuration. | `{ groupByTags: true, collapsePaths: false, showPathsSummary: true, avoidCirculars: false, lazyRendering: false, defaultTag: 'Default' }` | `{ groupByTags: boolean, collapsePaths: boolean, showPathsSummary: boolean, avoidCirculars: boolean, lazyRendering: boolean, defaultTag: string }` | diff --git a/package.json b/package.json index c34e8d20..ea8524a3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vitepress-openapi", "type": "module", - "version": "0.0.3-alpha.46", + "version": "0.0.3-alpha.47", "packageManager": "pnpm@9.1.1", "homepage": "https://vitepress-openapi.vercel.app/", "repository": { diff --git a/src/components/Path/OAPathsByTags.vue b/src/components/Path/OAPathsByTags.vue index fae8910d..c8d25dff 100644 --- a/src/components/Path/OAPathsByTags.vue +++ b/src/components/Path/OAPathsByTags.vue @@ -56,7 +56,7 @@ const internalTags = ref([ ...(pathsWithoutTags.length ? [ { - tag: t('Default'), + tag: t(useTheme().getSpecConfig.defaultTag), paths: pathsWithoutTags, isOpen: !themeConfig.getSpecConfig().collapsePaths.value, }, diff --git a/src/composables/useOpenapi.ts b/src/composables/useOpenapi.ts index 44e80aac..0630c868 100644 --- a/src/composables/useOpenapi.ts +++ b/src/composables/useOpenapi.ts @@ -30,6 +30,10 @@ export function useOpenapi({ spec: null, config: null, }) { + if (config) { + useTheme(config) + } + if (spec !== null) { setupOpenApi({ spec, config }) } @@ -49,9 +53,7 @@ export function useOpenapi({ function addSchema({ id, spec, config }) { const openapi = createOpenApiInstance({ spec }) - if (config) { - useTheme(config) - } + schemas.set(id, { ...openapi, id, diff --git a/src/composables/usePaths.ts b/src/composables/usePaths.ts index b81877b8..1b7991a7 100644 --- a/src/composables/usePaths.ts +++ b/src/composables/usePaths.ts @@ -10,7 +10,7 @@ export function usePaths({ const openapi = OpenApi({ spec, transformedSpec: transformSpec(spec) }) function getTags() { - return openapi.getFilteredTests() + return openapi.getFilteredTags() } return { diff --git a/src/composables/useSidebar.ts b/src/composables/useSidebar.ts index 16bc4bf9..80938cc6 100644 --- a/src/composables/useSidebar.ts +++ b/src/composables/useSidebar.ts @@ -1,5 +1,7 @@ import { OpenApi, httpVerbs, useOpenapi } from 'vitepress-openapi' +import { transformSpec } from '../lib/transformSpec' import type { OpenAPI } from './useOpenapi' +import { useTheme } from './useTheme' interface GenerateSidebarGroupsOptions { tags?: string[] | null @@ -17,26 +19,38 @@ const defaultOptions = { spec: null, linkPrefix: '/operations/', tagLinkPrefix: '/tags/', + defaultTag: 'Default', } export function useSidebar({ spec, linkPrefix, tagLinkPrefix, + defaultTag, }: { spec?: OpenAPI linkPrefix?: string tagLinkPrefix?: string + defaultTag?: string } = { ...defaultOptions, }) { + useTheme({ + spec: { + defaultTag: defaultTag || defaultOptions.defaultTag, + }, + }) + const options = { spec: spec || useOpenapi().json, linkPrefix: linkPrefix || defaultOptions.linkPrefix, tagLinkPrefix: tagLinkPrefix || defaultOptions.tagLinkPrefix, } - const openapi = OpenApi({ spec: options.spec }) + const openapi = OpenApi({ + spec: options.spec, + transformedSpec: transformSpec(options.spec), + }) function sidebarItemTemplate(method: string, title: string) { return ` @@ -136,7 +150,7 @@ export function useSidebar({ tags, linkPrefix, }: GenerateSidebarGroupsOptions = {}) { - tags = tags || openapi.getFilteredTests().map(({ name }) => name) + tags = tags || openapi.getFilteredTags().map(({ name }) => name) linkPrefix = linkPrefix || options.tagLinkPrefix if (!openapi.getPaths()) { diff --git a/src/composables/useTheme.ts b/src/composables/useTheme.ts index 6bacc1b0..9b6a435e 100644 --- a/src/composables/useTheme.ts +++ b/src/composables/useTheme.ts @@ -73,11 +73,12 @@ export interface I18nConfig { } export interface SpecConfig { - groupByTags: Ref - collapsePaths: Ref - showPathsSummary: Ref - avoidCirculars: Ref - lazyRendering: Ref + groupByTags?: Ref + collapsePaths?: Ref + showPathsSummary?: Ref + avoidCirculars?: Ref + lazyRendering?: Ref + defaultTag?: string } export interface UseThemeConfig { @@ -168,6 +169,7 @@ const themeConfig: UseThemeConfig = { showPathsSummary: ref(true), avoidCirculars: ref(false), lazyRendering: ref(false), + defaultTag: 'Default', }, } @@ -464,6 +466,10 @@ export function useTheme(config: Partial = {}) { if (config.lazyRendering !== undefined) { themeConfig.spec.lazyRendering.value = config.lazyRendering } + + if (config.defaultTag !== undefined) { + themeConfig.spec.defaultTag = config.defaultTag + } } return { diff --git a/src/lib/OpenApi.ts b/src/lib/OpenApi.ts index 28af012c..8bf85e46 100644 --- a/src/lib/OpenApi.ts +++ b/src/lib/OpenApi.ts @@ -175,7 +175,7 @@ export function OpenApi({ verb, operationId, summary, - tags, + tags: tags ?? [], } }) }) @@ -235,7 +235,7 @@ export function OpenApi({ } function getPathsWithoutTags() { - return filterPaths(operation => !operation?.tags) + return filterPaths(operation => !operation?.tags || operation.tags.length === 0) } function getTags() { @@ -246,7 +246,7 @@ export function OpenApi({ })) } - function getFilteredTests() { + function getFilteredTags() { const operationsTags = getOperationsTags() const tags = getTags() @@ -284,6 +284,6 @@ export function OpenApi({ getPathsByTags, getPathsWithoutTags, getTags, - getFilteredTests, + getFilteredTags, } } diff --git a/src/lib/generateMissingTags.ts b/src/lib/generateMissingTags.ts new file mode 100644 index 00000000..7ede77c3 --- /dev/null +++ b/src/lib/generateMissingTags.ts @@ -0,0 +1,21 @@ +import { useTheme } from 'vitepress-openapi' + +export function generateMissingTags(spec: any) { + const paths = spec.paths + + if (!paths) { + return spec + } + + const defaultTag = useTheme().getSpecConfig().defaultTag + + for (const path of Object.values(paths)) { + for (const verb of Object.keys(path)) { + const operation = path[verb] + const tags = operation.tags || [defaultTag] + operation.tags = tags + } + } + + return spec +} diff --git a/src/lib/transformSpec.ts b/src/lib/transformSpec.ts index bcea768a..94196f29 100644 --- a/src/lib/transformSpec.ts +++ b/src/lib/transformSpec.ts @@ -1,5 +1,6 @@ import { generateMissingOperationIds } from './generateMissingOperationIds' import { generateMissingSummary } from './generateMissingSummary' +import { generateMissingTags } from './generateMissingTags' export function transformSpec(spec) { if (import.meta.env.VITE_DEBUG) { @@ -17,6 +18,7 @@ export function transformSpec(spec) { if (spec?.paths) { spec = generateMissingOperationIds(spec) spec = generateMissingSummary(spec) + spec = generateMissingTags(spec) } return Object.assign({}, spec) diff --git a/test/composables/openapi.test.ts b/test/composables/openapi.test.ts index ed755ab4..f003bfd5 100644 --- a/test/composables/openapi.test.ts +++ b/test/composables/openapi.test.ts @@ -139,7 +139,7 @@ describe('openapi with spec', () => { expect(paths).toMatchObject({}) }) - it('getPathsWithoutTags returns paths without tags', () => { + it('sets defaultTag for operations without tags', () => { const api = createOpenApiInstance({ spec: { ...spec, @@ -159,16 +159,9 @@ describe('openapi with spec', () => { }, }) - const paths = api.getPathsWithoutTags() + expect(api.getPathsWithoutTags()).toMatchObject({}) - expect(paths).toMatchObject({ - '/no-tags': { - get: { - operationId: 'getNoTags', - summary: 'GET /no-tags', - }, - }, - }) + expect(api.getOperationsTags()).toEqual(['users', 'Default']) }) it('getPathsWithoutTags returns empty array if no paths without tags', () => { diff --git a/test/composables/useSidebar.test.ts b/test/composables/useSidebar.test.ts index 8f15738e..a7f25208 100644 --- a/test/composables/useSidebar.test.ts +++ b/test/composables/useSidebar.test.ts @@ -179,10 +179,4 @@ describe('itemsByTags', () => { { text: 'pets', link: '/tags/pets' }, ]) }) - - it('returns an empty array when there are no paths', () => { - const emptySidebar = useSidebar({ spec: {} }) - const result = emptySidebar.itemsByTags() - expect(result).toEqual([]) - }) }) diff --git a/test/composables/useTheme.test.ts b/test/composables/useTheme.test.ts index e7b19432..cda4d0de 100644 --- a/test/composables/useTheme.test.ts +++ b/test/composables/useTheme.test.ts @@ -90,6 +90,7 @@ describe('composition API', () => { showPathsSummary: ref(false), avoidCirculars: ref(false), lazyRendering: ref(false), + defaultTag: 'Default', }) }) @@ -328,6 +329,7 @@ describe('useTheme', () => { showPathsSummary: ref(true), avoidCirculars: ref(false), lazyRendering: ref(false), + defaultTag: 'Default', }) })