Skip to content

Commit

Permalink
Merge pull request #10 from inyourtime/refac-yaml-endpoint
Browse files Browse the repository at this point in the history
refac: rewrite yaml endpoint
  • Loading branch information
inyourtime authored Dec 10, 2024
2 parents d7409c6 + 3263b07 commit 80e0d1a
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 20 deletions.
34 changes: 34 additions & 0 deletions example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const path = require('node:path')

const Fastify = require('fastify')
const fastifyApiReference = require('@scalar/fastify-api-reference')

const fastifyOpenapiMerge = require('..')

const fastify = Fastify()

fastify.register(fastifyOpenapiMerge, {
specDir: path.join(__dirname, 'specs'),
specDefinition: {
openapi: '3.0.0',
info: {
title: 'CRUD API Example',
version: '1.0.0',
description: 'A sample CRUD API for managing items.'
},
}
})

fastify.register(fastifyApiReference, {
routePrefix: '/docs',
configuration: {
spec: {
url: '/openapi/json'
}
}
})

fastify.listen({ port: 3000 }, (err) => {
if (err) throw err
console.log('Documentation available on http://localhost:3000/docs')
})
83 changes: 83 additions & 0 deletions example/specs/api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
paths:
/items:
get:
summary: Get all items
operationId: getItems
responses:
'200':
description: A list of items
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Item'
post:
summary: Create a new item
operationId: createItem
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ItemInput'
responses:
'201':
description: Item created
content:
application/json:
schema:
$ref: '#/components/schemas/Item'
/items/{id}:
get:
summary: Get an item by ID
operationId: getItemById
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Item details
content:
application/json:
schema:
$ref: '#/components/schemas/Item'
'404':
description: Item not found
put:
summary: Update an item by ID
operationId: updateItem
parameters:
- name: id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ItemInput'
responses:
'200':
description: Item updated
content:
application/json:
schema:
$ref: '#/components/schemas/Item'
delete:
summary: Delete an item by ID
operationId: deleteItem
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'204':
description: Item deleted
23 changes: 23 additions & 0 deletions example/specs/schemas.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
components:
schemas:
Item:
type: object
properties:
id:
type: string
name:
type: string
description:
type: string
required:
- id
- name
ItemInput:
type: object
properties:
name:
type: string
description:
type: string
required:
- name
10 changes: 4 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const path = require('node:path')

const fp = require('fastify-plugin')
const { glob } = require('glob')
const YAML = require('yaml')

const { checkSpecDir } = require('./lib/spec-dir')
const { mergeSpec } = require('./lib/merge-spec')
Expand All @@ -20,11 +19,11 @@ async function fastifyOpenapiMerge (fastify, opts) {

for (const specPath of rootsSpec) {
const files = await glob('**/*.{yaml,yml,json}', {
cwd: specPath, absolute: false, follow: true, nodir: true,
cwd: specPath, absolute: true, follow: true, nodir: true,
})

specFiles.push(...files.map((file) => {
return path.join(specPath, file).split(path.win32.sep).join(path.posix.sep)
return file.split(path.win32.sep).join(path.posix.sep)
}))
}

Expand All @@ -48,13 +47,12 @@ async function fastifyOpenapiMerge (fastify, opts) {
const mergedSpec = await mergeSpec(specFiles, {
customMerge: opts.merge,
specDefinition: opts.specDefinition,
yaml: true
})

const yaml = YAML.stringify(mergedSpec)

return reply
.type('application/x-yaml')
.send(yaml)
.send(mergedSpec)
},
})
}
Expand Down
20 changes: 13 additions & 7 deletions lib/merge-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,25 @@ async function defaultMerge (specs) {
return result.output
}

async function mergeSpec (specPaths, { specDefinition, customMerge } = {}) {
async function mergeSpec (specPaths, opts = {}) {
if (!specPaths.length) {
throw new Error('"specPaths" option array requires one or more paths')
return
}

const _merge = customMerge ?? defaultMerge
const _merge = opts.customMerge ?? defaultMerge
if (typeof _merge !== 'function') {
throw new Error('"customMerge" must be a function')
}

specDefinition = specDefinition || defaultSpecDefinition
opts.specDefinition = opts.specDefinition || defaultSpecDefinition

const specs = []

for (const specPath of specPaths) {
const content = fs.readFileSync(specPath, 'utf-8')
const parse = YAML.parse(content) ?? {}

if (!customMerge) {
if (!opts.customMerge) {
specs.push({ oas: parse })
} else {
specs.push(parse)
Expand All @@ -51,10 +51,16 @@ async function mergeSpec (specPaths, { specDefinition, customMerge } = {}) {

const result = await _merge(specs)

return {
const merged = {
...result,
...specDefinition,
...opts.specDefinition,
}

if (opts.yaml === true) {
return YAML.stringify(merged)
}

return merged
}

module.exports = { mergeSpec }
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"yaml": "^2.6.1"
},
"devDependencies": {
"@scalar/fastify-api-reference": "^1.25.76",
"@types/node": "^22.10.1",
"c8": "^10.1.2",
"eslint": "^9.16.0",
Expand Down
11 changes: 4 additions & 7 deletions test/merge-spec.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,10 @@ test('custom merge not a function', async (t) => {
})

test('merge with empty spec', async (t) => {
t.plan(2)
try {
await mergeSpec([])
} catch (e) {
t.assert.ok(e)
t.assert.strictEqual(e.message, '"specPaths" option array requires one or more paths')
}
t.plan(1)

const mergedSpec = await mergeSpec([])
t.assert.ifError(mergedSpec)
})

test('merge with YAML file', async (t) => {
Expand Down

0 comments on commit 80e0d1a

Please sign in to comment.