Swagger Tool DAWN

内部使用swagger-ui增强脚本,方便复制url为函数

当前为 2020-05-26 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Swagger Tool DAWN
// @version      1.0.2
// @description  内部使用swagger-ui增强脚本,方便复制url为函数
// @author      lixiang
// @match        http://*/swagger-ui.html*
// @require     https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.min.js

// @grant        GM_addStyle
// @run-at       document-end
// @license     MIT
// @namespace https://greasyfork.org/users/559637
// ==/UserScript==
// https://developer.chrome.com/extensions/match_patterns
(async function (open) {
  function splitLast (str) {
    const lastIndex = str.lastIndexOf('/')
    return str.slice(lastIndex + 1)
  }
  function highlight () {
    for (const codeblock of document.getElementsByTagName('code')) {
      hljs.highlightBlock(codeblock)
    }
  }
  function addOutCss (href) {
    var head = document.querySelector('head')
    var link = document.createElement('link')
    link.href = href + ''
    link.rel = 'stylesheet'
    link.type = 'text/css'
    head.appendChild(link)
  }
  function firstUpperCase (str) {
    return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase())
  }
  function splitLastSecond (str) {
    const arr = str.split('/')
    return arr[arr.length - 2]
  }
  function toHump (str) {
    if (!str) { return }
    let newStr = ''
    let big = false
    for (const i in str) {
      let s = str[i]
      if (big) {
        s = s.toLocaleUpperCase()
        big = false
      }
      if (s === '_') { big = true } else newStr += s
    }
    return newStr
  }
  function getSelectValue () {
    const selectUrl = document.querySelector('#selectUrl')
    const index = selectUrl.selectedIndex // 序号,取当前选中选项的序号
    const select = selectUrl.options[index].value
    return select
  }
  function getUrlName (url) {
    const type = getSelectValue()
    let urlName = ''
    if (type === 'first') {
      urlName = toHump(splitLast(url))
    } else if (type === 'second') {
      const urlName2 = firstUpperCase(toHump(splitLast(url)))
      const urlName1 = toHump(splitLastSecond(url))
      urlName = urlName1 + urlName2
    }
    return urlName
  }
  function getPrefix () {
    return document.querySelector('#urlPrefix').value ? '/' + document.querySelector('#urlPrefix').value : ''
  }
  function setClipboardDisplay (e) {
    const text = e.text
    document.querySelector('#cliDisplaySpan').textContent = text
    highlight()
    e.clearSelection()
  }
  function addSheet (params) {
    GM_addStyle(
      `
      #cliDisplay .hljs {
  display: block !important;
  overflow-x: auto !important;
  padding: 0.5em !important;
  color: #abb2bf !important;
  background: #282c34 !important;
}

#cliDisplay .hljs-comment,
#cliDisplay .hljs-quote {
  color: #5c6370 !important;
  font-style: italic !important;
}

#cliDisplay .hljs-doctag,
#cliDisplay .hljs-keyword,
#cliDisplay .hljs-formula {
  color: #c678dd !important;
}

#cliDisplay .hljs-section,
#cliDisplay .hljs-name,
#cliDisplay .hljs-selector-tag,
#cliDisplay .hljs-deletion,
#cliDisplay .hljs-subst {
  color: #e06c75 !important;
}

#cliDisplay .hljs-literal {
  color: #56b6c2 !important;
}

#cliDisplay .hljs-string,
#cliDisplay .hljs-regexp,
#cliDisplay .hljs-addition,
#cliDisplay .hljs-attribute,
#cliDisplay .hljs-meta-string {
  color: #98c379 !important;
}

#cliDisplay .hljs-built_in,
#cliDisplay .hljs-class .hljs-title {
  color: #e6c07b !important;
}

#cliDisplay .hljs-attr,
#cliDisplay .hljs-variable,
#cliDisplay .hljs-template-variable,
#cliDisplay .hljs-type,
#cliDisplay .hljs-selector-class,
#cliDisplay .hljs-selector-attr,
#cliDisplay .hljs-selector-pseudo,
#cliDisplay .hljs-number {
  color: #d19a66 !important;
}

#cliDisplay .hljs-symbol,
#cliDisplay .hljs-bullet,
#cliDisplay .hljs-link,
#cliDisplay .hljs-meta,
#cliDisplay .hljs-selector-id,
#cliDisplay .hljs-title {
  color: #61aeee !important;
}

#cliDisplay .hljs-emphasis {
  font-style: italic !important;
}

#cliDisplay .hljs-strong {
  font-weight: bold !important;
}

#cliDisplay .hljs-link {
  text-decoration: underline !important;
}

      `
    )
    GM_addStyle(
      `
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}
::-webkit-scrollbar-track {
    border-radius: 3px;
    background: #c678dd57;
    -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.08);
}
::-webkit-scrollbar-thumb {
    border-radius: 3px;
    background: rgba(0,0,0,0.12);
    -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
}
.swagger-section #header {
  position:relative;
}
.search_wrapper {
 
}
.setting_card {
       position: absolute;
    background: #fff;
    border: 1px solid rgba(0,0,0,0.2);
    border-radius: 8px;
    box-shadow: 0 1px 2px 0 rgba(60,64,67,.30), 0 2px 6px 2px rgba(60,64,67,.15);
    overflow-x: hidden;
    padding-top: 0;
    height: 120px;
    width: 320px;
    right: 10px;
    top: 60px;
    padding: 10px 10px;
    visibility:hidden;
    z-index:1
}
.setting_item {
      padding: 5px 0;
    margin-bottom: 5px;
    border-bottom: 1px solid #f2f2f2;
}
.setting_icon {
  position:absolute;
  right:10px;
  top:10px;
   cursor: pointer;
}
.setting_icon:hover {
   animation: myRotate 3s linear infinite;
}
@keyframes myRotate{
    0%{ -webkit-transform: rotate(0deg);}
    50%{ -webkit-transform: rotate(180deg);}
    100%{ -webkit-transform: rotate(360deg);}
}
        .card {
          position: relative;
          background: #282c34 !important;

          margin: 1rem 0;
          padding: 1em 1em;
          border-radius: 0.2rem;
          border:none !important;
        max-height: 150px;
    overflow: auto;
        }
        .card:hover {
          box-shadow:2px 2px 9px 0px #1e3b50;
        }
        .header-label {
          color:#666;
              width: 120px;
    display: inline-block;
        }
        select {
          border: 1px solid #ccc;
          padding: 7px 0px;
          border-radius: 3px;
          padding-left: 5px;
          -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
          box-shadow: inset 0 1px 1px rgba(80, 69, 69, 0.075);
          -webkit-transition: border-color ease-in-out 0.15s,
            -webkit-box-shadow ease-in-out 0.15s;
          -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
          transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
        }
        option {
          height: 30px;
          padding: 5px 4px;
        }
        input {
          border: 1px solid #ccc;
          padding: 7px 0px;
          border-radius: 3px;
          padding-left: 5px;
          -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
          -webkit-transition: border-color ease-in-out 0.15s,
            -webkit-box-shadow ease-in-out 0.15s;
          -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
          transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
        }
        input:focus {
          border-color: #66afe9;
          outline: 0;
          -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
            0 0 8px rgba(102, 175, 233, 0.6);
          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
            0 0 8px rgba(102, 175, 233, 0.6);
        }
        button {
          display: inline-block;
          position: relative;
          cursor: pointer;
          padding: 7px 4px;
          color: white;
          font-size: 0.7em;
          text-align: center;
          text-decoration: none;
          text-transform: uppercase;
          vertical-align: middle;
          white-space: nowrap;
          outline: none;
          border: none;
          -webkit-user-select: none;
          -moz-user-select: none;
          -ms-user-select: none;
          user-select: none;
          border-radius: 2px;
          background-color: #89bf04;
        }
        button + button {
          margin-left: 5px;
        }
        button:hover {
          background-color: #aee032;
          text-decoration: none;
          box-shadow: 0 4px 10px 0px rgba(0, 0, 0, 0.225);
        }
    `)
  }
  function readyAsync () {
    return new Promise((resolve, reject) => {
      XMLHttpRequest.prototype.open = function (method, url, async, user, pass) {
        console.log('XMLHttpRequest.prototype.open -> url', url)
        this.addEventListener('readystatechange', () => {
        }, false)
        let timer = null
        let inited = false
        let myUrl = null
        if (url.includes('api-docs')) {
          myUrl = url
          timer = setInterval(() => {
            if (inited) {
              clearInterval(timer)
              inited = false
            } else {
              const elementArr = document.querySelectorAll('.http_method')
              if (elementArr.length !== 0) {
                if (myUrl.includes('api-docs')) {
                  inited = true
                  resolve(myUrl)
                }
              }
            }
          }, 1000)
        }
        open.call(this, method, url, async, user, pass)
      }
    })
  }
  function readyAndWatch (func) {
    XMLHttpRequest.prototype.open = function (method, url, async, user, pass) {
      this.addEventListener('readystatechange', () => {
      }, false)
      let timer = null
      let inited = false
      let myUrl = null
      if (url.includes('api-docs')) {
        myUrl = url
        timer = setInterval(() => {
          if (inited) {
            clearInterval(timer)
            inited = false
          } else {
            const elementArr = document.querySelectorAll('.http_method')
            if (elementArr.length !== 0) {
              if (myUrl.includes('api-docs')) {
                console.log('timer -> myUrl', myUrl)
                inited = true
                func(myUrl)
              }
            }
          }
        }, 1000)
      }
      open.call(this, method, url, async, user, pass)
    }
  }
  function getJsonAsync (url) {
    return new Promise((resolve, reject) => {
      const res = fetch(url)
        .then(res => {
          return res.json()
        })
        .catch(e => {
          reject(e)
        })
      res.then(res => {
        const totalRes = res
        resolve(totalRes)
      })
    })
  }

  function createExpand () {
    document.querySelector('#message-bar').insertAdjacentHTML('afterbegin', `
      <button id='collapse'>合起全部</button>
      <button id='expandAll'>展开全部</button>
    `)
    document.querySelector('#collapse').onclick = collapse
    document.querySelector('#expandAll').onclick = expandAll
    function expandAll () {
      const list = document.querySelector('#resources')
      const items = list.children;
      [...items].forEach(li => {
        li.classList.add('active')
        li.querySelector('ul.endpoints').style.display = 'block'
      })
    }
    function collapse () {
      const list = document.querySelector('#resources')
      const items = list.children;
      [...items].forEach(li => {
        li.classList.remove('active')
        li.querySelector('ul.endpoints').style.display = 'none'
      })
    }
  }

  function createCliDisplay () {
    document.querySelector('#message-bar').insertAdjacentHTML('afterbegin',
        `
          <pre class='card' id='cliDisplay'><code class='code javascript' id='cliDisplaySpan'>剪贴板展示</code></pre>   
        `
    )
  }
  function createCopyUrlAll (totalRes) {
    const blockArr = document.querySelectorAll('#resources > .resource')

    blockArr.forEach(ele => {
      const button = document.createElement('button')
      button.innerHTML = '复制所有url'
      button.classList.add('copy_url_all')

      ele.querySelector('.heading').children[0].prepend(button)

      const paths = new Set();

      [...ele.querySelector('.endpoints').children].forEach(li => {
        const url = li.querySelector('.path').children[0].innerHTML
        const method = li
          .querySelector('.http_method')
          .children[2].innerHTML.toUpperCase()
        const note = li.querySelector('.markdown').children[0].innerHTML
        const obj = {
          url,
          method,
          note
        }
        const objStr = JSON.stringify(obj)
        paths.add(objStr)
      })
      button.dataset.clipboardText = Array.from(paths).join(';')
    })
  }
  function createCopyUrl () {
    const elementArr = document.querySelectorAll('.endpoint')
    elementArr.forEach(ele => {
      const button = document.createElement('button')
      button.innerHTML = '复制url'
      button.classList.add('copy_url')
      ele.querySelector('.http_method').prepend(button)
      const url = ele.querySelector('.path').children[0].innerHTML
      const method = ele.querySelector('.http_method').children[2].innerHTML.toUpperCase()
      const note = ele.querySelector('.markdown').children[0].innerHTML
      const obj = {
        url,
        method,
        note
      }
      const objStr = JSON.stringify(obj)
      button.dataset.clipboardText = objStr
    })
  }
  function createCopyFunc (totalRes) {
    const elementArr = document.querySelectorAll('.endpoint')
    elementArr.forEach(ele => {
      const button = document.createElement('button')
      button.innerHTML = '复制为async函数'
      button.classList.add('copy_func')
      ele.querySelector('.http_method').prepend(button)
      const url = ele.querySelector('.path').children[0].innerHTML

      const pathRes = totalRes.paths[url].get || totalRes.paths[url].post
      const parameters = pathRes.parameters
      if (!parameters) {
        return
      }
      const parameterLast = parameters[parameters.length - 1]

      let ref
      let comment
      if (parameterLast.in === 'body') {
        if (parameterLast.schema.$ref) {
          ref = splitLast(parameterLast.schema.$ref)

          const definition = totalRes.definitions[ref]
          if (definition) {
            const properties = definition.properties

            const parameters = Object.keys(properties).map(key => {
              const property = properties[key]
              const obj = {}
              obj.name = key
              obj.description = property.description
              obj.type = property.type

              return obj
            })

            comment = parameters.reduce((acc, cur) => {
              if (cur.name == 'token') {
                return acc
              }
              acc = `${acc}
                    ${cur.name}:'',//${cur.description} ${cur.type}`
              return acc
            }, '')
          }
        }
      } else {
        comment = parameters.reduce((acc, cur) => {
          if (cur.name == 'token') {
            return acc
          }
          acc = `${acc}
                ${cur.name}:'', //${cur.description} ${cur.type}`
          return acc
        }, '')
      }

      if (!comment) {
        comment = '//无需参数'
      }
      const obj = {
        url,
        comment
      }
      button.dataset.clipboardText = JSON.stringify(obj)
    })
  }
  function createButtonTable (ele) {
    const elementArr = document.querySelectorAll('.endpoint')
    elementArr.forEach(ele => {
      const button = document.createElement('button')
      button.innerHTML = '复制为列表项'
      button.classList.add('copy_table')
      if (ele.querySelector('.description')) {
        ele.querySelector('.description').prepend(button)
        const description = ele.querySelector('.description')
        const div = description.querySelectorAll('div')
        const list = [...div]
        const table = list.reduce((acc, cur) => {
          if (cur.querySelector('.propDesc')) {
            const propName = cur.querySelector('.propName').innerHTML
            const noPropNameList = ['code', 'data', 'message', 'list', 'pageNum', 'pageSize', 'total ']
            if (noPropNameList.includes(propName)) {
              return acc
            }
            const propDesc = cur.querySelector('.propDesc').querySelector('p').innerHTML
            acc = `
            ${acc}
              {
                prop:"${propName}",
                label:"${propDesc}"
              },
              `
            return acc
          } else {
            return acc
          }
        }, '')
        button.dataset.clipboardText = table
      }
    })
  }

  function clipboard () {
    const clipboardUrl = new ClipboardJS('.copy_url', {
      text: function (trigger) {
        const objStr = trigger.dataset.clipboardText
        const obj = JSON.parse(objStr)
        const urlPrefix = getPrefix()
        const urlName = getUrlName(obj.url)
        const url = `export const ${urlName}  = data => request('${urlPrefix}${obj.url}', data,"${obj.method}")//${obj.note}`
        return url
      }
    })

    const clipboardUrlAll = new ClipboardJS('.copy_url_all', {
      text: function (trigger) {
        const arr = trigger.dataset.clipboardText.split(';')
        const paths = new Set()
        arr.forEach(objStr => {
          const obj = JSON.parse(objStr)
          const urlPrefix = getPrefix()
          const urlName = getUrlName(obj.url)
          const url = `export const ${urlName}  = data => request('${urlPrefix}${obj.url}', data,"${obj.method}")//${obj.note}`
          paths.add(url)
        })
        return Array.from(paths).join('\n')
      }
    })

    const clipboardFunc = new ClipboardJS('.copy_func', {
      text: function (trigger) {
        const objStr = trigger.dataset.clipboardText
        const obj = JSON.parse(objStr)
        const comment = obj.comment
        const asyncName = getUrlName(obj.url)
        const fun = `
        async ${asyncName}Async() {
          let params = {
          ${comment}
          }
          let res = await api.${asyncName}(params)
          },
          `
        return fun
      }
    })
    const clipboardTable = new ClipboardJS('.copy_table', {
      text: function (trigger) {
        const table = trigger.dataset.clipboardText
        return table
      }
    })

    clipboardUrl.on('success', setClipboardDisplay)
    clipboardUrlAll.on('success', setClipboardDisplay)
    clipboardFunc.on('success', setClipboardDisplay)
    clipboardTable.on('success', setClipboardDisplay)
  }
  function createSelect () {
    document.querySelector('.setting_card').insertAdjacentHTML('afterbegin',
        `
        <div class='setting_item'>
           <label for="selectUrl" class='header-label'>url截取范围:</label>
        <select name='selectUrl' id='selectUrl'>
          <option value="first"  selected = "selected">最后一级</option>
          <option value="second">最后二级</option>
        </select>
        </div>
        `
    )
  }
  function createInput () {
    document.querySelector('.setting_card').insertAdjacentHTML('afterbegin',
        `
        <div class='setting_item'>
           <label for="prefix" class='header-label'>url前缀:</label>
          <input type="text" id="urlPrefix" name="prefix" placeholder='后端微服务名'>
        </div>
         
        `
    )
  }
  function createSetting () {
    document.querySelector('#header').insertAdjacentHTML('beforeend',
        `
          <div class='setting_icon'>
           <svg t="1589098334770" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2404" width="35" height="35"><path d="M935.69113 523.130435c0-58.590609 36.797217-108.232348 88.30887-128a513.669565 513.669565 0 0 0-56.097391-135.635478 136.97113 136.97113 0 0 1-181.047652-181.092174A515.962435 515.962435 0 0 0 651.152696 22.26087 137.371826 137.371826 0 0 1 523.130435 110.569739 137.171478 137.171478 0 0 1 395.130435 22.26087a513.669565 513.669565 0 0 0-135.635478 56.141913 137.126957 137.126957 0 0 1-28.093218 152.932174 137.371826 137.371826 0 0 1-152.887652 28.16A510.21913 510.21913 0 0 0 22.26087 395.130435 137.371826 137.371826 0 0 1 110.569739 523.130435c0 58.590609-36.797217 108.232348-88.308869 128a513.669565 513.669565 0 0 0 56.141913 135.635478 136.97113 136.97113 0 0 1 180.980869 181.092174A515.650783 515.650783 0 0 0 395.130435 1024a137.371826 137.371826 0 0 1 128.022261-88.30887c58.590609 0 108.232348 36.797217 128 88.30887a513.669565 513.669565 0 0 0 135.635478-56.097391 137.126957 137.126957 0 0 1 28.093217-152.998957 137.371826 137.371826 0 0 1 152.887652-28.137739A510.21913 510.21913 0 0 0 1024 651.130435a137.171478 137.171478 0 0 1-88.30887-128.022261z m-401.719652 152.442435a141.55687 141.55687 0 1 1 0.111305-283.11374 141.55687 141.55687 0 0 1-0.111305 283.11374z" fill="#2DAA9D" p-id="2405"></path><path d="M754.042435 512c0-34.370783 21.593043-63.510261 51.801043-75.108174a301.345391 301.345391 0 0 0-32.901565-79.560348 80.361739 80.361739 0 0 1-106.22887-106.228869 302.703304 302.703304 0 0 0-79.604869-32.946087A80.584348 80.584348 0 0 1 512 269.957565 80.473043 80.473043 0 0 1 436.891826 218.156522c-27.959652 7.123478-54.761739 18.209391-79.560348 32.946087a80.450783 80.450783 0 0 1-16.473043 89.711304 80.584348 80.584348 0 0 1-89.711305 16.517565A299.341913 299.341913 0 0 0 218.156522 436.891826 80.584348 80.584348 0 0 1 269.957565 512c0 34.370783-21.593043 63.510261-51.801043 75.108174 7.123478 27.959652 18.209391 54.761739 32.946087 79.560348a80.361739 80.361739 0 0 1 106.184348 106.228869 302.525217 302.525217 0 0 0 79.604869 32.946087A80.584348 80.584348 0 0 1 512 754.042435c34.370783 0 63.510261 21.593043 75.108174 51.801043 27.959652-7.123478 54.761739-18.18713 79.560348-32.901565a80.450783 80.450783 0 0 1 16.473043-89.755826 80.584348 80.584348 0 0 1 89.711305-16.517565 299.341913 299.341913 0 0 0 32.990608-79.560348A80.473043 80.473043 0 0 1 754.042435 512z m-235.675826 89.421913a83.033043 83.033043 0 1 1 0.044521-166.066087 83.033043 83.033043 0 0 1-0.044521 166.066087z" fill="#A4E8E1" p-id="2406"></path></svg>
          </div>
          <div class='setting_card'>
           
          </div>
        `
    )
    document.querySelector('.setting_icon').onclick = changeShow
    let isShow = false
    function changeShow () {
      if (isShow) {
        isShow = false
        document.querySelector('.setting_card').style.visibility = 'hidden'
      } else {
        isShow = true
        document.querySelector('.setting_card').style.visibility = 'visible'
      }
    }
  }
  function createSearch (params) {
    document.querySelector('#header').insertAdjacentHTML('beforeend',
        `
          <div class='search_wrapper'>
            <label for="search" class='header-label'>搜索:</label>
          <input type="text" id="search" name="search" placeholder='输入汉字'>
          </div>
          
        `
    )
  }
  addSheet()
  async function init (url) {
    const totalRes = await getJsonAsync(url)
    createExpand()
    createSetting()
    createSelect()
    createInput()

    createCliDisplay()
    createCopyFunc(totalRes)
    createCopyUrl()
    createCopyUrlAll()
    createButtonTable()
    clipboard()
    highlight()
  }
  readyAndWatch(init)
}
)(XMLHttpRequest.prototype.open)