Added Swagger
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import keywordMap from "./keyword-map"
|
||||
import getKeywordsForPath from "./get-keywords-for-path"
|
||||
|
||||
export default function getCompletions(editor, session, pos, prefix, cb, ctx, system) {
|
||||
|
||||
const { fn: { getPathForPosition }, specSelectors } = system
|
||||
|
||||
const { isOAS3 } = specSelectors
|
||||
|
||||
if(isOAS3 && !isOAS3()) {
|
||||
// isOAS3 selector exists, and returns false
|
||||
return cb(null, null)
|
||||
}
|
||||
|
||||
const { AST } = ctx
|
||||
var editorValue = editor.getValue()
|
||||
const path = getPathForPosition({ pos, prefix, editorValue, AST})
|
||||
|
||||
const suggestions = getKeywordsForPath({ system, path, keywordMap })
|
||||
cb(null, suggestions)
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import isArray from "lodash/isArray"
|
||||
import isObject from "lodash/isObject"
|
||||
import mapValues from "lodash/mapValues"
|
||||
import isPlainObject from "lodash/isPlainObject"
|
||||
import toArray from "lodash/toArray"
|
||||
import isString from "lodash/isString"
|
||||
import get from "lodash/get"
|
||||
|
||||
export default function getKeywordsForPath({ system, path, keywordMap}) {
|
||||
keywordMap = Object.assign({}, keywordMap)
|
||||
|
||||
// is getting path was not successful stop here and return no candidates
|
||||
if (!isArray(path)) {
|
||||
return [
|
||||
{
|
||||
name: "array",
|
||||
value: " ",
|
||||
score: 300,
|
||||
meta: "Couldn't load suggestions"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if(path[path.length - 2] === "tags" && path.length > 2) {
|
||||
// 'path.length > 2' excludes top-level 'tags'
|
||||
return system.specSelectors.tags().map(tag => ({
|
||||
score: 0,
|
||||
meta: "local",
|
||||
value: tag.get("name"),
|
||||
})).toJS()
|
||||
}
|
||||
|
||||
let reversePath = path.slice(0).reverse()
|
||||
if(reversePath[1] === "security" && isNumeric(reversePath[0])) {
|
||||
// **.security[x]
|
||||
return system.specSelectors.securityDefinitions().keySeq().map(sec => ({
|
||||
score: 0,
|
||||
meta: "local",
|
||||
caption: sec,
|
||||
snippet: `${sec}: []`
|
||||
})).toJS()
|
||||
}
|
||||
|
||||
if(reversePath[0] === "security") {
|
||||
// **.security:
|
||||
return system.specSelectors.securityDefinitions().keySeq().map(sec => ({
|
||||
score: 0,
|
||||
meta: "local",
|
||||
caption: sec,
|
||||
snippet: `\n- ${sec}: []`
|
||||
})).toJS()
|
||||
}
|
||||
|
||||
// traverse down the keywordMap for each key in the path until there is
|
||||
// no key in the path
|
||||
|
||||
var key = path.shift()
|
||||
|
||||
while (key && isObject(keywordMap)) {
|
||||
keywordMap = getChild(keywordMap, key)
|
||||
key = path.shift()
|
||||
}
|
||||
|
||||
// if no keywordMap was found after the traversal return no candidates
|
||||
if (!isObject(keywordMap)) {
|
||||
return []
|
||||
}
|
||||
|
||||
// if keywordMap is an array of strings, return the array as list of
|
||||
// suggestions
|
||||
if (isArray(keywordMap) && keywordMap.every(isString)) {
|
||||
return keywordMap.map(constructAceCompletion.bind(null, "value"))
|
||||
}
|
||||
|
||||
// If keywordMap is describing an array unwrap the inner map so we can
|
||||
// suggest for array items
|
||||
if (isArray(keywordMap)) {
|
||||
if(isArray(keywordMap[0])) {
|
||||
return keywordMap[0].map(item => {
|
||||
return {
|
||||
name: "array",
|
||||
value: "- " + item,
|
||||
score: 300,
|
||||
meta: "array item"
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return [{
|
||||
name: "array",
|
||||
value: "- ",
|
||||
score: 300,
|
||||
meta: "array item"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
// if keywordMap is not an object at this point return no candidates
|
||||
if (!isObject(keywordMap)) {
|
||||
return []
|
||||
}
|
||||
|
||||
// for each key in keywordMap map construct a completion candidate and
|
||||
// return the array
|
||||
return suggestionFromSchema(keywordMap)
|
||||
}
|
||||
|
||||
function getChild(object, key) {
|
||||
var keys = Object.keys(object)
|
||||
var regex
|
||||
var isArrayAccess = /^\d+$/.test(key)
|
||||
|
||||
if(isArrayAccess && isArray(object)) {
|
||||
return object[0]
|
||||
}
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
let childVal = object[keys[i]]
|
||||
|
||||
if (!childVal) {
|
||||
return null
|
||||
}
|
||||
|
||||
regex = new RegExp(childVal.__regex || keys[i])
|
||||
|
||||
if (regex.test(key) && childVal) {
|
||||
if(typeof childVal === "object" && !isArray(childVal)) {
|
||||
return Object.assign({}, childVal)
|
||||
} else {
|
||||
return childVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function suggestionFromSchema(map) {
|
||||
const res = toArray(mapValues(map, (val, key) => {
|
||||
const keyword = get(val, "__value", key)
|
||||
const meta = isPlainObject(val) ? "object" : "keyword"
|
||||
|
||||
return constructAceCompletion(meta, keyword)
|
||||
}))
|
||||
return res
|
||||
}
|
||||
|
||||
function constructAceCompletion(meta, keyword) {
|
||||
if(keyword.slice(0, 2) === "__") {
|
||||
return {}
|
||||
}
|
||||
|
||||
// Give keywords, that extra colon
|
||||
let snippet
|
||||
switch(meta) {
|
||||
case "keyword":
|
||||
snippet = `${keyword}: `
|
||||
break
|
||||
case "object":
|
||||
snippet = `${keyword}:\n `
|
||||
break
|
||||
default:
|
||||
snippet = keyword
|
||||
}
|
||||
|
||||
// snippet's treat `$` as special characters
|
||||
snippet = snippet.replace("$", "\\$")
|
||||
|
||||
return {
|
||||
snippet,
|
||||
caption: keyword,
|
||||
score: 300,
|
||||
meta,
|
||||
}
|
||||
}
|
||||
|
||||
function isNumeric(obj) {
|
||||
return !isNaN(obj)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import * as wrapActions from "./wrap-actions"
|
||||
|
||||
export default function EditorAutosuggestOAS3KeywordsPlugin() {
|
||||
return {
|
||||
statePlugins: {
|
||||
editor: {
|
||||
wrapActions,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
ExternalDocumentation,
|
||||
Info,
|
||||
SecurityRequirement,
|
||||
Server,
|
||||
Tag,
|
||||
Components,
|
||||
Paths
|
||||
} from "./oas3-objects.js"
|
||||
|
||||
export default {
|
||||
openapi: String,
|
||||
info: Info,
|
||||
servers: [Server],
|
||||
paths: Paths,
|
||||
components: Components,
|
||||
security: [SecurityRequirement],
|
||||
tags: [Tag],
|
||||
externalDocs: ExternalDocumentation
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
// Adapted from OAS 3.0.0-rc2
|
||||
|
||||
// comma dangles in this file = cleaner diffs
|
||||
/*eslint comma-dangle: ["error", "always-multiline"]*/
|
||||
|
||||
// anyOf and combine are the same for now.
|
||||
// they are seperated for semantics, and for possible future improvement
|
||||
const anyOf = (...objs) => objs ? Object.assign({}, ...objs) : {}
|
||||
const stringEnum = (arr) => arr
|
||||
|
||||
const Any = null
|
||||
|
||||
export const ExternalDocumentation = {
|
||||
description: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
export const Contact = {
|
||||
name: String,
|
||||
url: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
export const License = {
|
||||
name: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
export const Info = {
|
||||
title: String,
|
||||
description: String,
|
||||
termsOfService: String,
|
||||
contact: Contact,
|
||||
license: License,
|
||||
version: String,
|
||||
}
|
||||
|
||||
export const ServerVariable = {
|
||||
enum: [String],
|
||||
default: String,
|
||||
description: String,
|
||||
}
|
||||
|
||||
export const XML = {
|
||||
name: String,
|
||||
namespace: String,
|
||||
prefix: String,
|
||||
attribute: Boolean,
|
||||
wrapped: Boolean,
|
||||
}
|
||||
|
||||
export const OAuthFlow = {
|
||||
authorizationUrl: String,
|
||||
tokenUrl: String,
|
||||
refreshUrl: String,
|
||||
scopes: {
|
||||
".": String,
|
||||
},
|
||||
}
|
||||
|
||||
export const Reference = {
|
||||
"$ref": String,
|
||||
}
|
||||
|
||||
export const Example = {
|
||||
summary: String,
|
||||
description: String,
|
||||
value: Any,
|
||||
externalValue: String,
|
||||
}
|
||||
|
||||
export const SecurityRequirement = {
|
||||
".": [String],
|
||||
}
|
||||
|
||||
export const Server = {
|
||||
url: String,
|
||||
description: String,
|
||||
variables: {
|
||||
".": ServerVariable,
|
||||
},
|
||||
}
|
||||
|
||||
export const Link = {
|
||||
operationRef: String,
|
||||
operationId: String,
|
||||
parameters: {
|
||||
".": Any,
|
||||
},
|
||||
requestBody: Any,
|
||||
description: String,
|
||||
server: Server,
|
||||
}
|
||||
|
||||
export const Schema = {
|
||||
// Lifted from JSONSchema
|
||||
title: String,
|
||||
multipleOf: String,
|
||||
maximum: String,
|
||||
exclusiveMaximum: String,
|
||||
minimum: String,
|
||||
exclusiveMinimum: String,
|
||||
maxLength: String,
|
||||
minLength: String,
|
||||
pattern: RegExp,
|
||||
maxItems: String,
|
||||
minItems: String,
|
||||
uniqueItems: Boolean,
|
||||
maxProperties: String,
|
||||
minProperties: String,
|
||||
required: Boolean,
|
||||
enum: String,
|
||||
// Adapted from JSONSchema
|
||||
type: String,
|
||||
get allOf () { return this },
|
||||
get oneOf () { return this },
|
||||
get anyOf () { return this },
|
||||
get not () { return this },
|
||||
get items () { return this },
|
||||
get properties () {
|
||||
return {
|
||||
".": this,
|
||||
}
|
||||
},
|
||||
get additionalProperties () { return this },
|
||||
description: String,
|
||||
format: String,
|
||||
default: Any,
|
||||
nullable: Boolean,
|
||||
readOnly: Boolean,
|
||||
writeOnly: Boolean,
|
||||
xml: XML,
|
||||
externalDocs: ExternalDocumentation,
|
||||
example: Any,
|
||||
deprecated: Boolean,
|
||||
}
|
||||
|
||||
export const Encoding = {
|
||||
contentType: String,
|
||||
headers: {
|
||||
".": undefined,
|
||||
},
|
||||
style: stringEnum(["matrix", "label", "form", "simple", "spaceDelimited", "pipeDelimited", "deepObject"]),
|
||||
explode: Boolean,
|
||||
allowReserved: Boolean,
|
||||
}
|
||||
|
||||
export const MediaType = {
|
||||
schema: anyOf(Schema, Reference),
|
||||
example: Any,
|
||||
examples: {
|
||||
".": anyOf(Example, Reference),
|
||||
},
|
||||
encoding: {
|
||||
".": Encoding,
|
||||
},
|
||||
}
|
||||
|
||||
export const Parameter = {
|
||||
name: String,
|
||||
in: stringEnum(["query", "header", "path", "cookie"]),
|
||||
description: String,
|
||||
required: Boolean,
|
||||
deprecated: Boolean,
|
||||
allowEmptyValue: Boolean,
|
||||
style: stringEnum(["matrix", "label", "form", "simple", "spaceDelimited", "pipeDelimited", "deepObject"]),
|
||||
explode: String,
|
||||
allowReserved: Boolean,
|
||||
schema: anyOf(Schema, Reference),
|
||||
example: Any,
|
||||
examples: {
|
||||
".": anyOf(Example, Reference),
|
||||
},
|
||||
content: {
|
||||
".": MediaType,
|
||||
},
|
||||
}
|
||||
|
||||
export const Header = {
|
||||
description: String,
|
||||
required: Boolean,
|
||||
deprecated: Boolean,
|
||||
allowEmptyValue: Boolean,
|
||||
style: stringEnum(["matrix", "label", "form", "simple", "spaceDelimited", "pipeDelimited", "deepObject"]),
|
||||
explode: String,
|
||||
allowReserved: Boolean,
|
||||
schema: anyOf(Schema, Reference),
|
||||
example: Any,
|
||||
examples: {
|
||||
".": anyOf(Example, Reference),
|
||||
},
|
||||
content: {
|
||||
".": MediaType,
|
||||
},
|
||||
}
|
||||
|
||||
export const RequestBody = {
|
||||
description: String,
|
||||
content: {
|
||||
".": MediaType,
|
||||
},
|
||||
}
|
||||
|
||||
export const Response = {
|
||||
description: String,
|
||||
headers: {
|
||||
".": anyOf(Header, Reference),
|
||||
},
|
||||
content: {
|
||||
".": MediaType,
|
||||
},
|
||||
links: {
|
||||
".": anyOf(Link, Reference),
|
||||
},
|
||||
}
|
||||
|
||||
export const Responses = {
|
||||
default: anyOf(Response, Reference),
|
||||
"\\d\\d\\d|\\d\\dX|\\dXX": anyOf(Response, Reference),
|
||||
}
|
||||
|
||||
export const Callback = {
|
||||
// ".": PathItem,
|
||||
}
|
||||
|
||||
export const Tag = {
|
||||
name: String,
|
||||
description: String,
|
||||
externalDocs: ExternalDocumentation,
|
||||
}
|
||||
|
||||
export const OAuthFlows = {
|
||||
implicit: OAuthFlow,
|
||||
password: OAuthFlow,
|
||||
clientCredentials: OAuthFlow,
|
||||
authorizationCode: OAuthFlow,
|
||||
}
|
||||
|
||||
export const SecurityScheme = {
|
||||
type: String,
|
||||
description: String,
|
||||
name: String,
|
||||
in: String,
|
||||
scheme: String,
|
||||
bearerFormat: String,
|
||||
flows: OAuthFlows,
|
||||
openIdConnectUrl: String,
|
||||
}
|
||||
|
||||
const ComponentFixedFieldRegex = "^[a-zA-Z0-9\.\-_]+$"
|
||||
|
||||
export const Components = {
|
||||
schemas: {
|
||||
[ComponentFixedFieldRegex]: anyOf(Schema, Reference),
|
||||
},
|
||||
responses: {
|
||||
[ComponentFixedFieldRegex]: anyOf(Response, Reference),
|
||||
},
|
||||
parameters: {
|
||||
[ComponentFixedFieldRegex]: anyOf(Parameter, Reference),
|
||||
},
|
||||
examples: {
|
||||
[ComponentFixedFieldRegex]: anyOf(Example, Reference),
|
||||
},
|
||||
requestBodies: {
|
||||
[ComponentFixedFieldRegex]: anyOf(RequestBody, Reference),
|
||||
},
|
||||
headers: {
|
||||
[ComponentFixedFieldRegex]: anyOf(Header, Reference),
|
||||
},
|
||||
securitySchemes: {
|
||||
[ComponentFixedFieldRegex]: anyOf(SecurityScheme, Reference),
|
||||
},
|
||||
links: {
|
||||
[ComponentFixedFieldRegex]: anyOf(Link, Reference),
|
||||
},
|
||||
callbacks: {
|
||||
get [ComponentFixedFieldRegex]() { return anyOf(Callback, Reference) },
|
||||
},
|
||||
}
|
||||
|
||||
export const Operation = {
|
||||
tags: [String],
|
||||
summary: String,
|
||||
description: String,
|
||||
externalDocs: ExternalDocumentation,
|
||||
operationId: String,
|
||||
parameters: [anyOf(Parameter, Reference)],
|
||||
requestBody: anyOf(RequestBody, Reference),
|
||||
responses: Responses,
|
||||
get callbacks() {
|
||||
return {
|
||||
".": anyOf(Callback, Reference),
|
||||
}
|
||||
},
|
||||
deprecated: Boolean,
|
||||
security: [SecurityRequirement],
|
||||
servers: [Server],
|
||||
}
|
||||
|
||||
export const Discriminator = {
|
||||
propertyName: String,
|
||||
mapping: {
|
||||
".": String,
|
||||
},
|
||||
}
|
||||
|
||||
export const PathItem = anyOf(Reference, {
|
||||
summary: String,
|
||||
description: String,
|
||||
get: Operation,
|
||||
put: Operation,
|
||||
post: Operation,
|
||||
delete: Operation,
|
||||
options: Operation,
|
||||
head: Operation,
|
||||
patch: Operation,
|
||||
trace: Operation,
|
||||
servers: Server,
|
||||
parameters: anyOf(Parameter, Reference),
|
||||
})
|
||||
|
||||
export const Paths = {
|
||||
"/.": PathItem,
|
||||
}
|
||||
|
||||
// solves `PathItem -> Operation -> Callback -> PathItem` circular reference
|
||||
Callback["."] = PathItem
|
||||
|
||||
// solves `Encoding -> Header -> MediaType -> Encoding` circular reference
|
||||
Encoding.headers["."] = Header
|
||||
@@ -0,0 +1,11 @@
|
||||
import getCompletions from "./get-completions"
|
||||
|
||||
// Add an autosuggest completer
|
||||
export const addAutosuggestionCompleters = (ori, system) => (context) => {
|
||||
return ori(context).concat([{
|
||||
getCompletions(...args) {
|
||||
// Add `context`, then `system` as the last args
|
||||
return getCompletions(...args, context, system)
|
||||
}
|
||||
}])
|
||||
}
|
||||
Reference in New Issue
Block a user