Skip to content
Permalink
Browse files

wip: body

  • Loading branch information
mmalecki committed Jan 16, 2020
1 parent bbd1457 commit 925968d523f142abcb7d558a8b89bc01d55ec72f
Showing with 99 additions and 15 deletions.
  1. +99 −15 lib/completers/openapi.js
@@ -1,10 +1,15 @@
const JSONParser = require('stream-json/Parser')
const SwaggerParser = require('swagger-parser')

const OPENAPI_SPEC_PATHS = ['openapi.json', 'openapi/v1', 'openapi/v2']

const postfixWith = postfix => str => str + postfix
const prefixWith = prefix => str => prefix + str

const arrayStartsWith = (a, b) => {
return b.every((v, idx) => v === a[idx])
}

class OpenAPIPlugin {
constructor ({ specEndpoints }) {
this.spec = undefined
@@ -32,27 +37,41 @@ class OpenAPIPlugin {
}

if (spec) {
try {
this.spec = await swaggerParser.dereference(spec)
this.paths = Object.keys(this.spec.paths)
this.schemaByPathMethod = {}
this.schemaPropertiesByPathMethod = {}

for (const [path, methods] of Object.entries(this.spec.paths)) {
for (const method of Object.keys(methods)) {
if (!this.pathsByMethod[method]) this.pathsByMethod[method] = []
this.pathsByMethod[method].push(path)

this.methods = new Array(...new Set(
Object.values(this.spec.paths)
.map(path => Object.keys(path))
.flat()
))
const operation = methods[method]
const content = operation.requestBody && operation.requestBody.content
const schema = content && content['application/json'] && content['application/json'].schema

this.paths = Object.keys(this.spec.paths)
if (schema) {
if (!this.schemaByPathMethod[path]) this.schemaByPathMethod[path] = {}
this.schemaByPathMethod[path][method] = schema

for (const [path, methods] of Object.entries(this.spec.paths)) {
for (const method of Object.keys(methods)) {
if (!this.pathsByMethod[method]) this.pathsByMethod[method] = []
this.pathsByMethod[method].push(path)
if (!this.schemaPropertiesByPathMethod[path])
this.schemaPropertiesByPathMethod[path] = {}

const objectifyProperties = (schema) => {
if (schema.type === 'object') {
let ret = {}
Object.entries(schema.properties).forEach(([prop, propSchema]) => {
ret[prop] = objectifyProperties(propSchema)
})
return ret
}
else return { type: schema.type }
}

this.schemaPropertiesByPathMethod[path][method] = objectifyProperties(schema)
}
}
}
catch (e) {
httpConsole.output.write(`Error parsing OpenAPI spec: ${e.message}\n`)
}
}
}

@@ -77,6 +96,71 @@ class OpenAPIPlugin {
})
}
}

completeBody (cmd, partial, callback) {
const { method, url } = cmd
const parser = new JSONParser()

const properties = this.schemaPropertiesByPathMethod[url] &&
this.schemaPropertiesByPathMethod[url][method]

if (!properties) return callback(null, [])

const onparsed = () => {
if (readingKey) {
let prop = properties

for (const key of currentStack.flat().filter(Boolean)) {
prop = prop[key]
}

if (!prop) return callback(null, [])

const partialKey = readingKey.join('')
const proposals = Object.keys(prop)
.filter(key => key.startsWith(partialKey))

return callback(null,
proposals
.map(prefixWith(partial.slice(0, -partialKey.length)))
.map(postfixWith('": '))
.map((proposal, idx) => {
const descriptor = prop[proposals[idx]]
if (descriptor.type === 'string') return proposal + '"'
if (typeof descriptor === 'object') return proposal + '{'
return proposal
})
)
}
}

let currentStack = []
let readingKey = false

parser.on('data', (data) => {
if (readingKey) {
if (data.name === 'stringChunk') readingKey.push(data.value)
else if (data.name === 'endKey') {
currentStack.pop()
currentStack.push(readingKey.join(''))
readingKey = []
}
}

if (data.name === 'startKey') readingKey = []
else if (data.name === 'startObject') currentStack.push([])
else if (data.name === 'endObject') currentStack.pop()
})

parser.on('error', onparsed)
parser.on('end', onparsed)

parser.end(partial)
// We can't `end` here, because we're dealing with partial JSON and the parser
// will throw an error. It seems cleaner to use the

callback(null, [])
}
}

module.exports = OpenAPIPlugin

0 comments on commit 925968d

Please sign in to comment.
You can’t perform that action at this time.