Add new files and initial code

This commit is contained in:
mazj
2023-12-25 14:14:43 +08:00
commit 38a3eaad1b
18 changed files with 3306 additions and 0 deletions

18
.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
/out/
/resources/public/js/compiled/
/target/
/*-init.clj
/*.log
# Leiningen
/.lein-*
/.nrepl-port
# Node.js dependencies
/node_modules/
# shadow-cljs cache, port files
/.shadow-cljs/
.clj-kondo
.lsp

196
README.md Normal file
View File

@ -0,0 +1,196 @@
# demo
A [re-frame](https://github.com/day8/re-frame) application designed to ... well, that part is up to
you.
## Getting Started
### Project Overview
* Architecture:
[Single Page Application (SPA)](https://en.wikipedia.org/wiki/Single-page_application)
* Languages
- Front end is [ClojureScript](https://clojurescript.org/) with ([re-frame](https://github.com/day8/re-frame))
* Dependencies
- UI framework: [re-frame](https://github.com/day8/re-frame)
([docs](https://github.com/day8/re-frame/blob/master/docs/README.md),
[FAQs](https://github.com/day8/re-frame/blob/master/docs/FAQs/README.md)) ->
[Reagent](https://github.com/reagent-project/reagent) ->
[React](https://github.com/facebook/react)
- Client-side routing: [bidi](https://github.com/juxt/bidi) and [pushy](https://github.com/clj-commons/pushy)
* Build tools
- CLJS compilation, dependency management, REPL, & hot reload: [`shadow-cljs`](https://github.com/thheller/shadow-cljs)
* Development tools
- Debugging: [CLJS DevTools](https://github.com/binaryage/cljs-devtools),
[re-frisk](https://github.com/flexsurfer/re-frisk)
#### Directory structure
* [`/`](/../../): project config files
* [`dev/`](dev/): source files compiled only with the [dev](#running-the-app) profile
- [`user.cljs`](dev/cljs/user.cljs): symbols for use during development in the
[ClojureScript REPL](#connecting-to-the-browser-repl-from-a-terminal)
* [`resources/public/`](resources/public/): SPA root directory;
[dev](#running-the-app) / [prod](#production) profile depends on the most recent build
- [`index.html`](resources/public/index.html): SPA home page
- Dynamic SPA content rendered in the following `div`:
```html
<div id="app"></div>
```
- Customizable; add headers, footers, links to other scripts and styles, etc.
- Generated directories and files
- Created on build with either the [dev](#running-the-app) or [prod](#production) profile
- `js/compiled/`: compiled CLJS (`shadow-cljs`)
- Not tracked in source control; see [`.gitignore`](.gitignore)
* [`src/demo/`](src/demo/): SPA source files (ClojureScript,
[re-frame](https://github.com/Day8/re-frame))
- [`core.cljs`](src/demo/core.cljs): contains the SPA entry point, `init`
* [`.github/workflows/`](.github/workflows/): contains the
[github actions](https://github.com/features/actions) pipelines.
- [`test.yaml`](.github/workflows/test.yaml): Pipeline for testing.
### Editor/IDE
Use your preferred editor or IDE that supports Clojure/ClojureScript development. See
[Clojure tools](https://clojure.org/community/resources#_clojure_tools) for some popular options.
### Environment Setup
1. Install [JDK 8 or later](https://openjdk.java.net/install/) (Java Development Kit)
2. Install [Node.js](https://nodejs.org/) (JavaScript runtime environment) which should include
[NPM](https://docs.npmjs.com/cli/npm) or if your Node.js installation does not include NPM also install it.
5. Clone this repo and open a terminal in the `demo` project root directory
### Browser Setup
Browser caching should be disabled when developer tools are open to prevent interference with
[`shadow-cljs`](https://github.com/thheller/shadow-cljs) hot reloading.
Custom formatters must be enabled in the browser before
[CLJS DevTools](https://github.com/binaryage/cljs-devtools) can display ClojureScript data in the
console in a more readable way.
#### Chrome/Chromium
1. Open [DevTools](https://developers.google.com/web/tools/chrome-devtools/) (Linux/Windows: `F12`
or `Ctrl-Shift-I`; macOS: `⌘-Option-I`)
2. Open DevTools Settings (Linux/Windows: `?` or `F1`; macOS: `?` or `Fn+F1`)
3. Select `Preferences` in the navigation menu on the left, if it is not already selected
4. Under the `Network` heading, enable the `Disable cache (while DevTools is open)` option
5. Under the `Console` heading, enable the `Enable custom formatters` option
#### Firefox
1. Open [Developer Tools](https://developer.mozilla.org/en-US/docs/Tools) (Linux/Windows: `F12` or
`Ctrl-Shift-I`; macOS: `⌘-Option-I`)
2. Open [Developer Tools Settings](https://developer.mozilla.org/en-US/docs/Tools/Settings)
(Linux/macOS/Windows: `F1`)
3. Under the `Advanced settings` heading, enable the `Disable HTTP Cache (when toolbox is open)`
option
Unfortunately, Firefox does not yet support custom formatters in their devtools. For updates, follow
the enhancement request in their bug tracker:
[1262914 - Add support for Custom Formatters in devtools](https://bugzilla.mozilla.org/show_bug.cgi?id=1262914).
## Development
### Running the App
Start a temporary local web server, build the app with the `dev` profile, and serve the app,
browser test runner and karma test runner with hot reload:
```sh
npm install
npx shadow-cljs watch app
```
Please be patient; it may take over 20 seconds to see any output, and over 40 seconds to complete.
When `[:app] Build completed` appears in the output, browse to
[http://localhost:8280/](http://localhost:8280/).
[`shadow-cljs`](https://github.com/thheller/shadow-cljs) will automatically push ClojureScript code
changes to your browser on save. To prevent a few common issues, see
[Hot Reload in ClojureScript: Things to avoid](https://code.thheller.com/blog/shadow-cljs/2019/08/25/hot-reload-in-clojurescript.html#things-to-avoid).
Opening the app in your browser starts a
[ClojureScript browser REPL](https://clojurescript.org/reference/repl#using-the-browser-as-an-evaluation-environment),
to which you may now connect.
#### Connecting to the browser REPL from your editor
See
[Shadow CLJS User's Guide: Editor Integration](https://shadow-cljs.github.io/docs/UsersGuide.html#_editor_integration).
Note that `npm run watch` runs `npx shadow-cljs watch` for you, and that this project's running build ids is
`app`, `browser-test`, `karma-test`, or the keywords `:app`, `:browser-test`, `:karma-test` in a Clojure context.
Alternatively, search the web for info on connecting to a `shadow-cljs` ClojureScript browser REPL
from your editor and configuration.
For example, in Vim / Neovim with `fireplace.vim`
1. Open a `.cljs` file in the project to activate `fireplace.vim`
2. In normal mode, execute the `Piggieback` command with this project's running build id, `:app`:
```vim
:Piggieback :app
```
#### Connecting to the browser REPL from a terminal
1. Connect to the `shadow-cljs` nREPL:
```sh
lein repl :connect localhost:8777
```
The REPL prompt, `shadow.user=>`, indicates that is a Clojure REPL, not ClojureScript.
2. In the REPL, switch the session to this project's running build id, `:app`:
```clj
(shadow.cljs.devtools.api/nrepl-select :app)
```
The REPL prompt changes to `cljs.user=>`, indicating that this is now a ClojureScript REPL.
3. See [`user.cljs`](dev/cljs/user.cljs) for symbols that are immediately accessible in the REPL
without needing to `require`.
### Running `shadow-cljs` Actions
See a list of [`shadow-cljs CLI`](https://shadow-cljs.github.io/docs/UsersGuide.html#_command_line)
actions:
```sh
npx shadow-cljs --help
```
Please be patient; it may take over 10 seconds to see any output. Also note that some actions shown
may not actually be supported, outputting "Unknown action." when run.
Run a shadow-cljs action on this project's build id (without the colon, just `app`):
```sh
npx shadow-cljs <action> app
```
### Debug Logging
The `debug?` variable in [`config.cljs`](src/cljs/demo/config.cljs) defaults to `true` in
[`dev`](#running-the-app) builds, and `false` in [`prod`](#production) builds.
Use `debug?` for logging or other tasks that should run only on `dev` builds:
```clj
(ns demo.example
(:require [demo.config :as config])
(when config/debug?
(println "This message will appear in the browser console only on dev builds."))
```
## Production
Build the app with the `prod` profile:
```sh
npm install
npm run release
```
Please be patient; it may take over 15 seconds to see any output, and over 30 seconds to complete.
The `resources/public/js/compiled` directory is created, containing the compiled `app.js` and
`manifest.edn` files.

8
dev/user.cljs Normal file
View File

@ -0,0 +1,8 @@
(ns cljs.user
"Commonly used symbols for easy access in the ClojureScript REPL during
development."
(:require
[cljs.repl :refer (Error->map apropos dir doc error->str ex-str ex-triage
find-doc print-doc pst source)]
[clojure.pprint :refer (pprint)]
[clojure.string :as str]))

27
karma.conf.js Normal file
View File

@ -0,0 +1,27 @@
module.exports = function (config) {
var junitOutputDir = process.env.CIRCLE_TEST_REPORTS || "target/junit"
config.set({
browsers: ['ChromeHeadless'],
basePath: 'target',
files: ['karma-test.js'],
frameworks: ['cljs-test'],
plugins: [
'karma-cljs-test',
'karma-chrome-launcher',
'karma-junit-reporter'
],
colors: true,
logLevel: config.LOG_INFO,
client: {
args: ['shadow.test.karma.init']
},
// the default configuration
junitReporter: {
outputDir: junitOutputDir + '/karma', // results will be saved as outputDir/browserName.xml
outputFile: undefined, // if included, results will be saved as outputDir/browserName/outputFile
suite: '' // suite will become the package name attribute in xml testsuite element
}
})
}

1197
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "demo",
"scripts": {
"ancient": "clojure -Sdeps '{:deps {com.github.liquidz/antq {:mvn/version \"RELEASE\"}}}' -m antq.core",
"watch": "npx shadow-cljs watch app browser-test karma-test",
"release": "npx shadow-cljs release app",
"build-report": "npx shadow-cljs run shadow.cljs.build-report app target/build-report.html"
},
"dependencies": {
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"shadow-cljs": "2.26.2"
}
}

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Swagger Merger and PDF Generator</title>
<link href="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/bulma/0.9.3/css/bulma.min.css" type="text/css" rel="stylesheet" />
<script src="/rapipdf.js"></script>
</head>
<body>
<noscript>
demo is a JavaScript app. Please enable JavaScript to continue.
</noscript>
<div id="app"></div>
<rapi-pdf id="gen-pdf" style="display: none;"></rapi-pdf>
<script src="/js/compiled/app.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

33
shadow-cljs.edn Normal file
View File

@ -0,0 +1,33 @@
{:nrepl {:port 8777}
:source-paths ["src" "test"]
:dependencies
[[reagent "1.1.1"]
[re-frame "1.4.2"]
[bidi "2.1.6"]
[clj-commons/pushy "0.3.10"]
[binaryage/devtools "1.0.6"]
[re-frisk "1.6.0"]]
:dev-http
{8280 "resources/public"
8290 "target/browser-test"}
:builds
{:app
{:target :browser
:output-dir "resources/public/js/compiled"
:asset-path "/js/compiled"
:modules
{:app {:init-fn demo.core/init}}
:devtools
{:preloads [
re-frisk.preload]}
:dev
{:compiler-options
{:closure-defines
{ }}}}}}

4
src/demo/config.cljs Normal file
View File

@ -0,0 +1,4 @@
(ns demo.config)
(def debug?
^boolean goog.DEBUG)

26
src/demo/core.cljs Normal file
View File

@ -0,0 +1,26 @@
(ns demo.core
(:require
[reagent.dom :as rdom]
[re-frame.core :as re-frame]
[demo.events :as events]
[demo.routes :as routes]
[demo.views :as views]
[demo.config :as config]
))
(defn dev-setup []
(when config/debug?
(println "dev mode")))
(defn ^:dev/after-load mount-root []
(re-frame/clear-subscription-cache!)
(let [root-el (.getElementById js/document "app")]
(rdom/unmount-component-at-node root-el)
(rdom/render [views/main-panel] root-el)))
(defn init []
(routes/start!)
(re-frame/dispatch-sync [::events/initialize-db])
(dev-setup)
(mount-root))

4
src/demo/db.cljs Normal file
View File

@ -0,0 +1,4 @@
(ns demo.db)
(def default-db
{:name "re-frame"})

1049
src/demo/demo.cljs Normal file

File diff suppressed because it is too large Load Diff

20
src/demo/events.cljs Normal file
View File

@ -0,0 +1,20 @@
(ns demo.events
(:require
[re-frame.core :as re-frame]
[demo.db :as db]
))
(re-frame/reg-event-db
::initialize-db
(fn [_ _]
db/default-db))
(re-frame/reg-event-fx
::navigate
(fn [_ [_ handler]]
{:navigate handler}))
(re-frame/reg-event-fx
::set-active-panel
(fn [{:keys [db]} [_ active-panel]]
{:db (assoc db :active-panel active-panel)}))

278
src/demo/merge.cljs Normal file
View File

@ -0,0 +1,278 @@
(ns demo.merge
(:require [clojure.string :as str]))
#_(def ice-map
{"borderRouteDetection.json" {:desc "运维监控 / 运维工具 / 网络诊断"
:order 26}
"deviceinspection.json" {:desc "运维监控 / 运维工具 / 设备巡检"
:order 25}
"directconnect.json" {:desc "虚拟网络 / 云专线"
:order 11}
"elastic.json" {:desc "系统概况 / 概况"
:order 1}
"elk.json" {:desc "运维监控 / ELK监控"
:order 16}
"emergency.json" {:desc "物理网络 / 交换机 / 特殊操作"
:order 4}
"epg.json" {:desc "虚拟网络 / EPG终端组"
:order 10}
"inspection.json" {:desc "运维监控 / 运维工具 / 健康巡检"
:order 23}
"ipsla.json" {:desc "运维监控 / 运维工具 / IPSLA"
:order 20}
"l47-nocloud-rest.json" {:desc "网络服务 / L47非云业务"
:order 13}
"l47service.json" {:desc "网络服务 / L47设备和云业务"
:order 14}
"mtu.json" {:desc "运维监控 / 运维工具 / MTU检测"
:order 21}
"networks-map-view-rest.json" {:desc "系统概况 / 三网互视"
:order 2}
"ngoam.json" {:desc "运维监控 / 运维工具 / 路径检测"
:order 24}
"northSouthTraffic.json" {:desc "运维监控 / 流量统计 / 南北向流量"
:order 19}
"overlaymapper.json" {:desc "虚拟网络"
:order 8}
"physicalresource.json" {:desc "物理网络 / 配置详情 / 交换机和服务器"
:order 5}
"portmirror.json" {:desc "运维监控 / 运维工具 / SPAN探测"
:order 22}
"resourcepool.json" {:desc "物理网络 / 资源池和资源管理"
:order 6}
"servermanage.json" {:desc "物理网络 / 服务器管理"
:order 7}
"sfc.json" {:desc "网路服务 / 服务链"
:order 15}
"sys.json" {:desc "系统工具、日志和权限"
:order 28}
"telemetry.json" {:desc "运维监控 / TELEMETRY监控"
:order 17}
"traffic-view.json" {:desc "运维监控 / 流量统计 / VXLAN流量"
:order 18}
"troubleshooting.json" {:desc "运维监控 / 运维工具 / 智能巡检"
:order 27}
"underlay.json" {:desc "物理网络"
:order 3}
"vmware.json" {:desc "虚拟网络 / Vmware"
:order 12}
"vnnb.json" {:desc "虚拟网络 / 非云服务"
:order 9}})
(defn make-unique! [from-data prefix file-pfx file-order]
(let [{:keys [swagger info basePath tags schemes paths securityDefinitions definitions]} from-data
concat-path (fn [kw]
(let [k (name kw)
a (str/ends-with? basePath "/")
b (str/starts-with? k "/")]
(cond (and a b)
(keyword (str basePath (subs k 1)))
(and (not a) (not b))
(keyword (str basePath "/" k))
:else
(keyword (str basePath k)))))
trans-name (fn [old-name] (str/upper-case (str file-pfx old-name)))
tags-map (reduce (fn [agg {:keys [name]}] (assoc agg name (trans-name name))) {} tags)
tags (mapv
(fn [{:keys [name]}]
{:name (trans-name name)
:order file-order})
tags)
paths (reduce-kv (fn [m k v] (assoc m (concat-path k) v)) {} paths)
paths (reduce-kv
(fn [m k v] ;path methods
(assoc
m k
(reduce-kv
(fn [m k v] ;method definition
(assoc
m k
(reduce-kv
(fn [m k v] ;parameter schema
(cond (and (= :parameters k)
(vector? v))
(assoc
m k
(mapv (fn [x] ;one parameter map
(reduce-kv
(fn [m k v] ;schema ref
(if (and (= :schema k)
(map? v)
(contains? v :$ref))
(assoc m k
(assoc v
:$ref
(let [ref (get v :$ref)]
(if (and (string? ref)
(re-find #"^#/definitions/.*" ref))
(str "#/definitions/" prefix (subs ref 14))
ref))))
(assoc m k v)))
{}
x))
v))
(and (= :responses k)
(map? v))
;遍历所有的 XXX: map 中的 map并当 key 为 schema 且
;additionalProperties 为 map 且包含 $ref 时进行替换
(assoc
m k
(reduce-kv
(fn [m k v] ;code response
(if (and (map? v)
(contains? v :schema)
(map? (:schema v))
(contains? (:schema v) :additionalProperties)
(map? (:additionalProperties (:schema v)))
(contains? (:additionalProperties (:schema v)) :$ref))
(assoc
m k
(assoc v
:schema
(assoc (:schema v)
:additionalProperties
(assoc (:additionalProperties (:schema v))
:$ref
(let [ref (get (:additionalProperties (:schema v)) :$ref)]
(if (and (string? ref)
(re-find #"^#/definitions/.*" ref))
(str "#/definitions/" prefix (subs ref 14))
ref))))))
(assoc m k v)))
{}
v))
(and (= :tags k)
(vector? v))
(assoc m k
(mapv
(fn [x] (if (contains? tags-map x) (get tags-map x) x))
v))
:else
(assoc m k v)))
{}
v)))
{}
v)))
{}
paths)
definitions (reduce-kv (fn [m k v]
(assoc m (keyword (str prefix (name k))) v))
{}
definitions)
;如果 definitions 的 properties 中某个 key 的 value map 中包含 $ref将其改为 #/definitions/ZZZ-xxx
definitions (reduce-kv
(fn [m k v] ;className properties
(assoc m k
(if (map? v)
(reduce-kv
(fn [m k v] ;type/properties...
(if (and (= :properties k)
(map? v))
(assoc
m k
(reduce-kv
(fn [m k v] ;propertyName property
(cond (and (map? v)
(contains? v :$ref)) ;如果 property 中包含直接 $ref
(assoc
m k
(assoc v
:$ref
(let [ref (get v :$ref)]
(if (and (string? ref)
(re-find #"^#/definitions/.*" ref))
(str "#/definitions/" prefix (subs ref 14))
ref))))
(and (map? v)
(contains? v :items)
(contains? (:items v) :$ref)) ;如果 property 中 items 中包含 $ref
(assoc
m k
(assoc v
:items
(assoc (:items v)
:$ref
(let [ref (get (:items v) :$ref)]
(if (and (string? ref)
(re-find #"^#/definitions/.*" ref))
(str "#/definitions/" prefix (subs ref 14))
ref)))))
:else
(assoc m k v)))
{}
v))
(assoc m k v)))
{}
v)
v)))
{}
definitions)
result {:swagger swagger
:info info
:basePath basePath
:tags tags
:schemes schemes
:paths paths
:securityDefinitions securityDefinitions
:definitions definitions}]
result))
(defn random-prefix []
(.substring (str (random-uuid)) 0 9))
(defn merge! [data {:keys [mapping document-title
document-desc
document-version]}]
(let [datas (mapv
(fn [[name content]]
(let [{:keys [desc order]} (get mapping (keyword name))
file-pfx (if desc (str desc " / ") "")
file-order (or order 0)]
(make-unique! content (random-prefix) file-pfx file-order)))
data)
merged-data (reduce
(fn [agg m1]
(let [{swagger-1 :swagger
info-1 :info
basePath-1 :basePath
schemes-1 :schemes
securityDefinitions-1 :securityDefinitions
tags-1 :tags
paths-1 :paths
definitions-1 :definitions} agg
{basePath-2 :basePath
swagger-2 :swagger
info-2 :info
tags-2 :tags
schemes-2 :schemes
paths-2 :paths
securityDefinitions-2 :securityDefinitions
definitions-2 :definitions} m1]
{:swagger swagger-2
:info info-2
:basePath "/"
:tags (set (into (or tags-1 []) (or tags-2 [])))
:schemes schemes-2
:paths (merge paths-1 paths-2)
:securityDefinitions securityDefinitions-2
:definitions (merge definitions-1 definitions-2)}))
{}
datas)
merged-data (assoc
merged-data
:tags
(->> merged-data
:tags
(vec)
(sort-by :order)
(mapv (fn [x] (dissoc x :order))))
:info
(merge (:info merged-data)
(if-not (or (nil? document-version)
(str/blank? document-version))
{:description (or document-desc "")
:title document-title
:version document-version}
{:description (or document-desc "")
:title document-title})))]
merged-data))

43
src/demo/routes.cljs Normal file
View File

@ -0,0 +1,43 @@
(ns demo.routes
(:require
[bidi.bidi :as bidi]
[pushy.core :as pushy]
[re-frame.core :as re-frame]
[demo.events :as events]))
(defmulti panels identity)
(defmethod panels :default [] [:div "No panel found for this route."])
(def routes
(atom
["/" {"" :home
"about" :about}]))
(defn parse
[url]
(bidi/match-route @routes url))
(defn url-for
[& args]
(apply bidi/path-for (into [@routes] args)))
(defn dispatch
[route]
(let [panel (keyword (str (name (:handler route)) "-panel"))]
(re-frame/dispatch [::events/set-active-panel panel])))
(defonce history
(pushy/pushy dispatch parse))
(defn navigate!
[handler]
(pushy/set-token! history (url-for handler)))
(defn start!
[]
(pushy/start! history))
(re-frame/reg-fx
:navigate
(fn [handler]
(navigate! handler)))

13
src/demo/subs.cljs Normal file
View File

@ -0,0 +1,13 @@
(ns demo.subs
(:require
[re-frame.core :as re-frame]))
(re-frame/reg-sub
::name
(fn [db]
(:name db)))
(re-frame/reg-sub
::active-panel
(fn [db _]
(:active-panel db)))

261
src/demo/views.cljs Normal file
View File

@ -0,0 +1,261 @@
(ns demo.views
(:require
[re-frame.core :as re-frame]
[reagent.core :as r]
[demo.events :as events]
[demo.routes :as routes]
[demo.demo :as demo]
[demo.subs :as subs]
[demo.merge :as merge]
[clojure.string :as str]))
(defn read-file
[file callback]
(let [reader (js/FileReader.)]
(set! (.-onload reader)
(fn [e]
(callback (-> e .-originalTarget .-result))))
(.readAsText reader file)))
(defn download-blob-as-file
[blob filename]
(let [a (js/document.createElement "a")
url (js/URL.createObjectURL blob)]
(.setAttribute a "href" url)
(.setAttribute a "download" filename)
(.click a)))
(defn merge-files!
[files config gen-pdf?]
(let [merged-file-contents
(merge/merge! files
(update config
:mapping
#(-> %
(js/JSON.parse)
(js->clj :keywordize-keys true))))
merged-data (clj->js merged-file-contents
:keyword-fn #(subs (str %) 1))
merged-json (js/JSON.stringify merged-data nil 2)]
(if-not gen-pdf?
(download-blob-as-file
(js/Blob. [merged-json]
{:type "application/json"}) (or (:output-file-name config) "merge.json"))
(let [element (.getElementById js/document "gen-pdf")]
(.generatePdf element merged-data)))))
(defonce dropped-files (r/atom {}))
(defonce config (r/atom {:output-file-name "merge.json"
:document-title "Swagger Document"
:document-desc "Swagger Document of API"
:document-version "1.0.0"
:mapping (js/JSON.stringify
(clj->js
{"borderRouteDetection.json" {:desc "运维监控 / 运维工具 / 网络诊断"
:order 26}
"deviceinspection.json" {:desc "运维监控 / 运维工具 / 设备巡检"
:order 25}
"directconnect.json" {:desc "虚拟网络 / 云专线"
:order 11}
"elastic.json" {:desc "系统概况 / 概况"
:order 1}})
nil 2)}))
(defn handle-upload-file [e]
(let [files (.-files (.-target e))]
(doseq [file files]
(read-file file
(fn [contents]
(swap! dropped-files assoc (.-name file)
(-> (js/JSON.parse contents)
(js->clj :keywordize-keys true))))))))
(defn home-panel []
[:<>
[:nav.navbar.is-info
[:div.container
[:div.navbar-brand
[:a.navbar-item
[:svg {:t "1703483238104"
:class "icon"
:viewBox "0 0 1024 1024"
:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:p-id "4219"
:height "200"
:width "200"}
[:path {:d "M511.18 1018.06C227.739 1016.833 14.337 792.372 6.145 532.686-3.686 224.665 236.339 14.336 490.906 6.144c307.404-9.83 516.505 229.99 526.745 481.485 12.698 306.38-233.472 532.48-506.47 530.432z"
:fill "#6D9900"
:p-id "4220"}]
[:path {:d "M272.589 512c33.997 20.07 44.646 50.586 48.742 85.197 3.072 26.01 1.639 52.019 2.867 78.029 1.844 38.297 12.084 39.321 39.527 39.321 11.878 0 16.384 3.482 14.54 15.155-0.409 1.844 0 3.687 0 5.735 0 33.587 0 33.587-33.791 32.153-47.514-2.048-73.114-29.286-77.62-76.8-3.276-35.43 1.434-71.065-5.12-106.086-5.324-28.672-16.793-39.936-45.875-41.574-7.373-0.41-10.24-2.663-10.035-9.83 0.41-7.169 0-14.132 0-21.3 0.205-9.011-2.048-19.046 1.229-26.624 3.891-8.806 15.36-2.662 23.347-5.12 14.336-4.3 23.552-13.722 27.853-27.648 3.686-11.674 5.939-23.757 6.144-36.045 0.614-32.153-2.048-64.512 4.915-96.051 9.216-42.598 29.082-59.802 72.704-63.693 36.045-3.277 36.045-3.277 36.045 32.563 0 19.661 0 19.866-18.842 20.276-28.877 0.614-32.768 9.42-35.02 36.864-2.049 25.804 0 52.019-2.868 78.028-3.686 35.43-13.926 66.765-48.742 87.45z m478.617 0.205c-38.092-22.733-46.694-58.573-49.356-98.304-1.434-23.143-1.23-46.285-2.458-69.427-1.229-26.624-8.602-33.997-35.226-35.021-19.046-0.615-19.046-0.615-19.046-20.07v-2.868c0-30.925 0-30.925 31.744-30.105 48.947 1.433 74.752 27.238 79.462 75.776 2.663 27.443 1.434 54.886 2.458 82.124 0.41 13.312 2.867 26.215 6.963 38.708 6.554 19.456 16.999 27.443 37.683 27.648 11.06 0 15.36 3.072 14.336 14.54-1.024 12.288-0.614 24.576-0.204 36.864 0.204 7.783-2.458 10.445-10.445 10.855-28.263 1.229-40.755 13.721-45.67 42.189-4.097 23.142-2.663 46.284-2.868 69.222 0 16.998-1.024 33.997-4.71 50.586-9.421 41.37-29.901 58.777-72.09 62.259-36.864 3.072-35.84 2.867-37.273-33.178-0.615-15.36 3.276-19.865 19.046-19.66 27.648 0.204 34.406-7.988 35.635-35.84 1.229-30.72 0.41-61.44 4.915-91.956 5.12-31.13 17.613-57.139 47.104-74.342z"
:fill "#FEFEFE"
:p-id "4221"}]
[:path {:d "M635.7 545.997c-19.252 0.205-35.636-15.565-35.636-33.997 0-17.818 16.18-33.997 34.406-34.202 19.252-0.204 35.636 15.565 35.636 34.202 0 18.022-15.975 33.792-34.407 33.997z m-89.908-34.202c0 20.48-13.926 34.407-34.406 34.407-19.252 0-33.792-14.541-33.792-33.588 0-19.456 15.155-34.61 34.61-34.61 19.252 0 33.588 14.335 33.588 33.791zM389.12 545.997c-20.07 0-34.816-14.746-34.611-34.407 0.205-19.25 14.95-33.587 33.997-33.792 19.046 0 35.225 15.975 35.02 34.816-0.204 18.228-15.77 33.383-34.406 33.383z"
:fill "#FEFEFE"
:p-id "4222"}]]
[:span.ml-2 "SMPG"]]]
[:div.navbar-menu
[:div.navbar-start
[:a.navbar-item {:on-click #(re-frame/dispatch [::events/navigate :home])}
"Home"]
[:a.navbar-item {:on-click #(js/window.open "https://mrin9.github.io/RapiPdf/" "_blank")}
"RapiPdf"]
[:a.navbar-item {:on-click #(js/window.open "https://github.com/corkine" "_blank")}
[:svg.mr-1 {:t "1703483460437"
:class "icon"
:viewBox "0 0 1024 1024"
:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:p-id "5226"
:width 200
:height 200}
[:path {:d "M850.346667 155.008a42.666667 42.666667 0 0 0-22.741334-23.509333c-8.704-3.754667-85.717333-33.322667-200.32 39.168H396.714667c-114.773333-72.618667-191.701333-42.922667-200.32-39.168a42.88 42.88 0 0 0-22.741334 23.466666c-26.197333 66.218667-18.048 136.448-7.850666 176.896C134.272 374.016 128 413.098667 128 469.333333c0 177.877333 127.104 227.882667 226.730667 246.272a189.568 189.568 0 0 0-13.013334 46.549334A44.373333 44.373333 0 0 0 341.333333 768v38.613333c-19.498667-4.138667-41.002667-11.946667-55.168-26.112C238.08 732.416 188.330667 682.666667 128 682.666667v85.333333c25.002667 0 65.365333 40.362667 97.834667 72.832 51.029333 51.029333 129.066667 55.253333 153.386666 55.253333 3.114667 0 5.376-0.085333 6.528-0.128A42.666667 42.666667 0 0 0 426.666667 853.333333v-82.090666c4.266667-24.746667 20.224-49.621333 27.946666-56.362667a42.666667 42.666667 0 0 0-23.125333-74.581333C293.333333 624.554667 213.333333 591.488 213.333333 469.333333c0-53.12 5.632-70.741333 31.573334-99.285333 11.008-12.117333 14.08-29.568 7.978666-44.8-4.821333-11.904-18.773333-65.450667-6.485333-117.546667 20.650667-1.578667 59.904 4.565333 113.706667 40.96C367.104 253.44 375.466667 256 384 256h256a42.666667 42.666667 0 0 0 23.936-7.338667c54.016-36.522667 92.970667-41.770667 113.664-41.130666 12.330667 52.224-1.578667 105.770667-6.4 117.674666a42.666667 42.666667 0 0 0 8.021333 44.928C805.077333 398.464 810.666667 416.085333 810.666667 469.333333c0 122.581333-79.957333 155.52-218.069334 170.922667a42.666667 42.666667 0 0 0-23.125333 74.709333c19.797333 17.066667 27.861333 32.469333 27.861333 53.034667v128h85.333334v-128c0-20.437333-3.925333-38.101333-9.770667-53.12C769.92 695.765333 896 643.712 896 469.333333c0-56.362667-6.272-95.530667-37.76-137.514666 10.197333-40.405333 18.261333-110.506667-7.893333-176.810667z"
:fill "#ffffff"
:p-id "5227"}]]
"About"]]]]]
[:section.hero.is-info
[:div.hero-body
[:div.container
[:h1.title
[:span "Swagger Merge and PDF Generator"]]
[:h2.subtitle
[:span "Concat multiple swagger specs into one, and generate PDF for it!"]]]]]
[:div.container.content.mt-5
[:div.ml-5
[:div.file.is-boxed.mt-3
[:label.file-label
[:input.file-input {:type "file"
:accept ".json"
:multiple :multiple
:on-change handle-upload-file}]
[:span.file-cta
[:span.file-label "Read Swagger .json file(s)"]]]]
[:div.mt-3
(if (empty? @dropped-files)
[:div "No files uploaded"]
[:div
[:div
(for [[k v] @dropped-files]
^{:key k}
[:div.mb-2
[:div
[:strong k]]
[:div
[:pre (pr-str v)]]])]])]
[:div.mt-5.mb-5
[:h4 "Config Settings"]
[:div.field.is-horizontal
[:div.field-label.is-normal
[:label.label "Output File Name"]]
[:div.field-body
[:div.field
[:p.control
[:input.input {:type "text"
:on-change (fn [e]
(let [data (-> e .-target .-value)]
(when-not (or (nil? data) (str/blank? data))
(swap! config assoc :output-file-name data))))
:placeholder "Output File Name"
:default-value (:output-file-name @config)}]]]]]
[:div.field.is-horizontal
[:div.field-label.is-normal
[:label.label "Document Title"]]
[:div.field-body
[:div.field
[:p.control
[:input.input {:type "text"
:on-change (fn [e]
(let [data (-> e .-target .-value)]
(when-not (or (nil? data) (str/blank? data))
(swap! config assoc :document-title data))))
:placeholder "Document Title"
:default-value (:document-title @config)}]]]]]
[:div.field.is-horizontal
[:div.field-label.is-normal
[:label.label "Document Desc"]]
[:div.field-body
[:div.field
[:p.control
[:input.input {:type "text"
:on-change (fn [e]
(let [data (-> e .-target .-value)]
(when-not (or (nil? data) (str/blank? data))
(swap! config assoc :document-desc data))))
:placeholder "Document Description"
:default-value (:document-desc @config)}]]]]]
[:div.field.is-horizontal
[:div.field-label.is-normal
[:label.label "Document Version"]]
[:div.field-body
[:div.field
[:p.control
[:input.input {:type "text"
:on-change (fn [e]
(let [data (-> e .-target .-value)]
(when-not (or (nil? data) (str/blank? data))
(swap! config assoc :document-version data))))
:placeholder "Document Version"
:default-value (:document-version @config)}]]]]]
[:div.field.is-horizontal
[:div.field-label.is-normal
[:label.label "File2TagName Map"]]
[:div.field-body
[:div.field
[:p.control
[:textarea.textarea
{:placeholder "Mapping FileName to TagName Prefix, and set it's order"
:rows 10
:on-change (fn [e]
(let [data (-> e .-target .-value)]
(if (or (nil? data) (str/blank? data))
(swap! config assoc :mapping {})
(try
(swap! config assoc :mapping (js/JSON.parse data))
(catch js/Error _ (println "Invalid JSON format!"))))))
:default-value (:mapping @config)}]]]]]
[:div.field.is-horizontal
[:div.field-label.is-normal]
[:div.field-body
[:div.field
[:article.message.is-warning
[:div.message-body
"Add a prefix to and sort the tags of a specific .json file。e.g. if a.json sets desc to \"A\", then all the tags in its file become \"A / Tag\", and the tags will be sorted by the order field after the files are merged."]]]]]]
[:div.mt-5.mb-5.is-flex.is-justify-content-flex-end
(when (empty? @dropped-files)
[:div
[:button.button.is-info
{:on-click #(merge-files! (demo/try-with-demo) @config true)}
"Try with Demo Swagger Spec!"]])
(when (not-empty @dropped-files)
[:button.button.is-danger
{:on-click #(reset! dropped-files {})}
"Clean Read Files"])
(when (not-empty @dropped-files)
[:button.button.is-info.ml-2
{:on-click (fn []
(println @config)
(merge-files! @dropped-files @config false))}
"Download Merged .json File"])
(when (not-empty @dropped-files)
[:button.button.is-info.ml-2
{:on-click (fn []
(println @config)
(merge-files! @dropped-files @config true))}
"Merge and Generated PDF!"])]]]])
(defmethod routes/panels :home-panel [] [home-panel])
(defn about-panel []
[:div
[:h1 "This is the About Page."]
[:div
[:a {:on-click #(re-frame/dispatch [::events/navigate :home])}
"go to Home Page"]]])
(defmethod routes/panels :about-panel [] [about-panel])
(defn main-panel []
(let [active-panel (re-frame/subscribe [::subs/active-panel])]
(routes/panels @active-panel)))