You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							205 lines
						
					
					
						
							5.7 KiB
						
					
					
				
			
		
		
	
	
							205 lines
						
					
					
						
							5.7 KiB
						
					
					
				'use strict'
 | 
						|
 | 
						|
const fs = require('fs')
 | 
						|
const path = require('path')
 | 
						|
const Ref = require('json-schema-resolver')
 | 
						|
const cloner = require('rfdc')({ proto: true, circles: false })
 | 
						|
const { rawRequired } = require('../symbols')
 | 
						|
const { xConsume } = require('../constants')
 | 
						|
 | 
						|
function addHook (fastify, pluginOptions) {
 | 
						|
  const routes = []
 | 
						|
  const sharedSchemasMap = new Map()
 | 
						|
 | 
						|
  fastify.addHook('onRoute', (routeOptions) => {
 | 
						|
    routes.push(routeOptions)
 | 
						|
  })
 | 
						|
 | 
						|
  fastify.addHook('onRegister', async (instance) => {
 | 
						|
    // we need to wait the ready event to get all the .getSchemas()
 | 
						|
    // otherwise it will be empty
 | 
						|
    // TODO: better handle for schemaId
 | 
						|
    // when schemaId is the same in difference instance
 | 
						|
    // the latter will lost
 | 
						|
    instance.addHook('onReady', (done) => {
 | 
						|
      const allSchemas = instance.getSchemas()
 | 
						|
      for (const schemaId of Object.keys(allSchemas)) {
 | 
						|
        if (!sharedSchemasMap.has(schemaId)) {
 | 
						|
          sharedSchemasMap.set(schemaId, allSchemas[schemaId])
 | 
						|
        }
 | 
						|
      }
 | 
						|
      done()
 | 
						|
    })
 | 
						|
  })
 | 
						|
 | 
						|
  return {
 | 
						|
    routes,
 | 
						|
    Ref () {
 | 
						|
      const externalSchemas = cloner(Array.from(sharedSchemasMap.values()))
 | 
						|
      return Ref(Object.assign(
 | 
						|
        { applicationUri: 'todo.com' },
 | 
						|
        pluginOptions.refResolver,
 | 
						|
        { clone: true, externalSchemas })
 | 
						|
      )
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function shouldRouteHide (schema, opts) {
 | 
						|
  const { hiddenTag, hideUntagged } = opts
 | 
						|
 | 
						|
  if (schema && schema.hide) {
 | 
						|
    return true
 | 
						|
  }
 | 
						|
 | 
						|
  const tags = (schema && schema.tags) || []
 | 
						|
 | 
						|
  if (tags.length === 0 && hideUntagged) {
 | 
						|
    return true
 | 
						|
  }
 | 
						|
 | 
						|
  if (tags.includes(hiddenTag)) {
 | 
						|
    return schema.tags.includes(hiddenTag)
 | 
						|
  }
 | 
						|
 | 
						|
  return false
 | 
						|
}
 | 
						|
 | 
						|
// The swagger standard does not accept the url param with ':'
 | 
						|
// so '/user/:id' is not valid.
 | 
						|
// This function converts the url in a swagger compliant url string
 | 
						|
// => '/user/{id}'
 | 
						|
// custom verbs at the end of a url are okay => /user::watch but should be rendered as /user:watch in swagger
 | 
						|
const COLON = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
 | 
						|
function formatParamUrl (str) {
 | 
						|
  let i, char
 | 
						|
  let state = 'skip'
 | 
						|
  let path = ''
 | 
						|
  let param = ''
 | 
						|
  let level = 0
 | 
						|
  // count for regex if no param exist
 | 
						|
  let regexp = 0
 | 
						|
  for (i = 0; i < str.length; i++) {
 | 
						|
    char = str[i]
 | 
						|
    switch (state) {
 | 
						|
      case 'colon': {
 | 
						|
        // we only accept a-zA-Z0-9_ in param
 | 
						|
        if (COLON.indexOf(char) !== -1) {
 | 
						|
          param += char
 | 
						|
        } else if (char === '(') {
 | 
						|
          state = 'regexp'
 | 
						|
          level++
 | 
						|
        } else {
 | 
						|
          // end
 | 
						|
          state = 'skip'
 | 
						|
          path += '{' + param + '}'
 | 
						|
          path += char
 | 
						|
          param = ''
 | 
						|
        }
 | 
						|
        break
 | 
						|
      }
 | 
						|
      case 'regexp': {
 | 
						|
        if (char === '(') {
 | 
						|
          level++
 | 
						|
        } else if (char === ')') {
 | 
						|
          level--
 | 
						|
        }
 | 
						|
        // we end if the level reach zero
 | 
						|
        if (level === 0) {
 | 
						|
          state = 'skip'
 | 
						|
          if (param === '') {
 | 
						|
            regexp++
 | 
						|
            param = 'regexp' + String(regexp)
 | 
						|
          }
 | 
						|
          path += '{' + param + '}'
 | 
						|
          param = ''
 | 
						|
        }
 | 
						|
        break
 | 
						|
      }
 | 
						|
      default: {
 | 
						|
        // we check if we need to change state
 | 
						|
        if (char === ':' && str[i + 1] === ':') {
 | 
						|
          // double colon -> single colon
 | 
						|
          path += char
 | 
						|
          // skip one more
 | 
						|
          i++
 | 
						|
        } else if (char === ':') {
 | 
						|
          // single colon -> state colon
 | 
						|
          state = 'colon'
 | 
						|
        } else if (char === '(') {
 | 
						|
          state = 'regexp'
 | 
						|
          level++
 | 
						|
        } else if (char === '*') {
 | 
						|
          // * -> wildcard
 | 
						|
          // should be exist once only
 | 
						|
          path += '{wildcard}'
 | 
						|
        } else {
 | 
						|
          path += char
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  // clean up
 | 
						|
  if (state === 'colon' && param !== '') {
 | 
						|
    path += '{' + param + '}'
 | 
						|
  }
 | 
						|
  return path
 | 
						|
}
 | 
						|
 | 
						|
function resolveLocalRef (jsonSchema, externalSchemas) {
 | 
						|
  if (typeof jsonSchema.type !== 'undefined' && typeof jsonSchema.properties !== 'undefined') {
 | 
						|
    // for the shorthand querystring/params/headers declaration
 | 
						|
    const propertiesMap = Object.keys(jsonSchema.properties).reduce((acc, headers) => {
 | 
						|
      const rewriteProps = {}
 | 
						|
      rewriteProps.required = (Array.isArray(jsonSchema.required) && jsonSchema.required.indexOf(headers) >= 0) || false
 | 
						|
      // save raw required for next restore in the content/<media-type>
 | 
						|
      if (jsonSchema.properties[headers][xConsume]) {
 | 
						|
        rewriteProps[rawRequired] = jsonSchema.properties[headers].required
 | 
						|
      }
 | 
						|
      const newProps = Object.assign({}, jsonSchema.properties[headers], rewriteProps)
 | 
						|
 | 
						|
      return Object.assign({}, acc, { [headers]: newProps })
 | 
						|
    }, {})
 | 
						|
 | 
						|
    return propertiesMap
 | 
						|
  }
 | 
						|
 | 
						|
  // for oneOf, anyOf, allOf support in querystring/params/headers
 | 
						|
  if (jsonSchema.oneOf || jsonSchema.anyOf || jsonSchema.allOf) {
 | 
						|
    const schemas = jsonSchema.oneOf || jsonSchema.anyOf || jsonSchema.allOf
 | 
						|
    return schemas.reduce(function (acc, schema) {
 | 
						|
      const json = resolveLocalRef(schema, externalSchemas)
 | 
						|
      return { ...acc, ...json }
 | 
						|
    }, {})
 | 
						|
  }
 | 
						|
 | 
						|
  // $ref is in the format: #/definitions/<resolved definition>/<optional fragment>
 | 
						|
  const localRef = jsonSchema.$ref.split('/')[2]
 | 
						|
  return resolveLocalRef(externalSchemas[localRef], externalSchemas)
 | 
						|
}
 | 
						|
 | 
						|
function readPackageJson () {
 | 
						|
  try {
 | 
						|
    return JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json')))
 | 
						|
  } catch (err) {
 | 
						|
    return {}
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function resolveSwaggerFunction (opts, cache, routes, Ref, done) {
 | 
						|
  if (typeof opts.openapi === 'undefined' || opts.openapi === null) {
 | 
						|
    return require('../spec/swagger')(opts, cache, routes, Ref, done)
 | 
						|
  } else {
 | 
						|
    return require('../spec/openapi')(opts, cache, routes, Ref, done)
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
module.exports = {
 | 
						|
  addHook,
 | 
						|
  shouldRouteHide,
 | 
						|
  readPackageJson,
 | 
						|
  formatParamUrl,
 | 
						|
  resolveLocalRef,
 | 
						|
  resolveSwaggerFunction
 | 
						|
}
 |