From 61568a650e854d54d5c1920d305ac46aa9d47d04 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 29 Apr 2016 12:22:23 +0200 Subject: [PATCH] add api doc viewer (moved from package pve2-api-doc) --- Makefile | 14 +- PVEAPI.js | 279 +++++++++++++++++++++++++++++++++++++++ doc-debian/pve-docs.conf | 3 + extractapi.pl | 44 ++++++ 4 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 PVEAPI.js create mode 100755 extractapi.pl diff --git a/Makefile b/Makefile index 840d1a2..60c1d93 100644 --- a/Makefile +++ b/Makefile @@ -159,6 +159,12 @@ pve-admin-guide.epub: ${PVE_ADMIN_GUIDE_SOURCES} a2x -f epub pve-admin-guide.adoc test -n "$${NOVIEW}" || $(BROWSER) $@ & +apidata.js: extractapi.pl + ./extractapi.pl >$@ + +apidoc.js: apidata.js PVEAPI.js + cat apidata.js PVEAPI.js >$@ + .PHONY: dinstall dinstall: ${GEN_DEB} dpkg -i ${GEN_DEB} @@ -179,7 +185,7 @@ DOC_DEB_FILES= \ pve-admin-guide.epub \ index.html -${DOC_DEB}: index.adoc ${PVE_ADMIN_GUIDE_SOURCES} +${DOC_DEB}: index.adoc ${PVE_ADMIN_GUIDE_SOURCES} apidoc.js apidoc.html $(MAKE) NOVIEW=1 pve-admin-guide.pdf pve-admin-guide.html pve-admin-guide.epub $(MAKE) NOVIEW=1 $(addsuffix .1.html, ${COMMAND_LIST}) $(addsuffix .8.html, ${SERVICE_LIST}) $(addsuffix .5.html, ${CONFIG_LIST}) asciidoc -a "date=$(shell date)" -a "revnumber=${DOCRELEASE}" index.adoc @@ -192,6 +198,10 @@ ${DOC_DEB}: index.adoc ${PVE_ADMIN_GUIDE_SOURCES} # install doc files install -m 0644 ${DOC_DEB_FILES} build/usr/share/${DOC_PACKAGE} install -m 0644 index.html build/usr/share/${DOC_PACKAGE} + # install api doc viewer + mkdir build/usr/share/${DOC_PACKAGE}/pve2-api-doc + install -m 0644 apidoc.html build/usr/share/${DOC_PACKAGE}/pve2-api-doc/index.html + install -m 0644 apidoc.js build/usr/share/${DOC_PACKAGE}/pve2-api-doc/ cd build; dpkg-buildpackage -rfakeroot -b -us -uc lintian ${DOC_DEB} @@ -223,5 +233,5 @@ update: clean make all clean: - rm -rf *~ *.html *.pdf *.epub *.tmp *.1 *.5 *.8 *.deb *.changes build + rm -rf *~ *.html *.pdf *.epub *.tmp *.1 *.5 *.8 *.deb *.changes build apidata.js apidoc.js diff --git a/PVEAPI.js b/PVEAPI.js new file mode 100644 index 0000000..ff3cd44 --- /dev/null +++ b/PVEAPI.js @@ -0,0 +1,279 @@ +// avoid errors when running without development tools +if (!Ext.isDefined(Ext.global.console)) { + var console = { + dir: function() {}, + log: function() {} + }; +} + +Ext.onReady(function() { + + Ext.define('pve-param-schema', { + extend: 'Ext.data.Model', + fields: [ + 'name', 'type', 'typetext', 'description', 'enum', + 'minimum', 'maximum', 'minLength', 'maxLength', + 'pattern', 'title', 'requires', 'format', 'default', + 'disallow', 'extends', 'links', + { + name: 'optional', + type: 'boolean' + } + ] + }); + + var store = Ext.create('Ext.data.TreeStore', { + model: Ext.define('pve-api-doc', { + extend: 'Ext.data.Model', + fields: [ + 'path', 'info', 'text', + ] + }), + proxy: { + type: 'memory', + data: pveapi + }, + sorters: [{ + property: 'leaf', + direction: 'ASC' + }, { + property: 'text', + direction: 'ASC' + }] + }); + + var render_text = function(value, metaData, record) { + var pdef = record.data; + + metaData.style = 'white-space:pre-wrap;' + + return Ext.htmlEncode(value); + }; + + var render_type = function(value, metaData, record) { + var pdef = record.data; + + return pdef['enum'] ? 'enum' : (pdef.type || 'string'); + }; + + var render_format = function(value, metaData, record) { + var pdef = record.data; + + metaData.style = 'white-space:normal;' + + if (pdef.typetext) + return pdef.typetext; + + if (pdef['enum']) + return pdef['enum'].join(' | '); + + if (pdef.format) + return pdef.format; + + if (pdef.pattern) + return pdef.pattern; + + return ''; + }; + + var render_docu = function(data) { + var md = data.info; + + // console.dir(data); + + var items = []; + + var clicmdhash = { + GET: 'get', + POST: 'create', + PUT: 'set', + DELETE: 'delete' + }; + + Ext.Array.each(['GET', 'POST', 'PUT', 'DELETE'], function(method) { + var info = md[method]; + if (info) { + + var usage = ""; + + usage += ""; + usage += "
HTTP:   " + method + " /api2/json" + data.path + "
 
CLI:pvesh " + clicmdhash[method] + " " + data.path + "
"; + + var sections = [ + { + title: 'Description', + html: Ext.htmlEncode(info.description), + bodyPadding: 10 + }, + { + title: 'Usage', + html: usage, + bodyPadding: 10 + } + ]; + + if (info.parameters && info.parameters.properties) { + + var pstore = Ext.create('Ext.data.Store', { + model: 'pve-param-schema', + proxy: { + type: 'memory' + }, + groupField: 'optional', + sorters: [ + { + property: 'name', + direction: 'ASC' + } + ] + }); + + Ext.Object.each(info.parameters.properties, function(name, pdef) { + pdef.name = name; + pstore.add(pdef); + }); + + pstore.sort(); + + var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ + enableGroupingMenu: false, + groupHeaderTpl: 'OptionalRequired' + }); + + sections.push({ + xtype: 'gridpanel', + title: 'Parameters', + features: [groupingFeature], + store: pstore, + viewConfig: { + trackOver: false, + stripeRows: true + }, + columns: [ + { + header: 'Name', + dataIndex: 'name' + }, + { + header: 'Type', + dataIndex: 'type', + renderer: render_type, + }, + { + header: 'Format', + dataIndex: 'type', + renderer: render_format, + flex: 1 + }, + { + header: 'Description', + dataIndex: 'description', + renderer: render_text, + flex: 2 + } + ] + }); + + } + + if (info.returns) { + + var rtype = info.returns.type; + if (!rtype && info.returns.items) + rtype = 'array'; + if (!rtype) + rtype = 'object'; + + sections.push({ + title: 'Returns: ' + rtype + }); + } + + var permhtml = ''; + if (!info.permissions) { + permhtml = "Root only."; + } else { + if (info.permissions.description) { + permhtml += "
" + + Ext.htmlEncode(info.permissions.description) + "
"; + } + + if (info.permissions.user) { + if (!info.permissions.description) { + if (info.permissions.user === 'world') { + permhtml += "Accessible without any authententification."; + } else if (info.permissions.user === 'all') { + permhtml += "Accessible by all authententicated users."; + } else { + permhtml += 'Onyl accessible by user "' + + info.permissions.user + '"'; + } + } + } else if (info.permissions.check) { + permhtml += "
Check: " + 
+			    Ext.htmlEncode(Ext.JSON.encode(info.permissions.check))  + "
"; + } else { + permhtml += "Unknown systax!"; + } + } + + sections.push({ + title: 'Required permissions', + bodyPadding: 10, + html: permhtml + }); + + + items.push({ + title: method, + autoScroll: true, + defaults: { + border: false + }, + items: sections + }); + } + }); + + var ct = Ext.getCmp('docview'); + ct.setTitle("Path: " + data.path); + ct.removeAll(true); + ct.add(items); + }; + + var tree = Ext.create('Ext.tree.Panel', { + title: 'Resource Tree', + store: store, + width: 200, + region: 'west', + split: true, + margins: '5 0 5 5', + rootVisible: false, + listeners: { + selectionchange: function(v, selections) { + if (!selections[0]) + return; + var rec = selections[0]; + render_docu(rec.data); + } + } + }); + + Ext.create('Ext.container.Viewport', { + layout: 'border', + renderTo: Ext.getBody(), + items: [ + tree, + { + xtype: 'tabpanel', + title: 'Documentation', + id: 'docview', + region: 'center', + margins: '5 5 5 0', + layout: 'fit', + items: [] + } + ] + }); + +}); diff --git a/doc-debian/pve-docs.conf b/doc-debian/pve-docs.conf index 2b9b8e1..c092b9d 100644 --- a/doc-debian/pve-docs.conf +++ b/doc-debian/pve-docs.conf @@ -5,3 +5,6 @@ Alias /pve-docs /usr/share/pve-docs/ + +Alias /pve2-api-doc /usr/share/pve-docs/pve2-api-doc/ + diff --git a/extractapi.pl b/extractapi.pl new file mode 100755 index 0000000..0488ea9 --- /dev/null +++ b/extractapi.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl -w + +use strict; +use PVE::RESTHandler; +use PVE::API2; +use JSON; + +sub cleanup_tree { + my ($h) = @_; + + my $class = ref($h); + return $h if !$class; + + if ($class eq 'ARRAY') { + my $res = []; + foreach my $el (@$h) { + push @$res, cleanup_tree($el); + } + return $res; + } elsif ($class eq 'HASH') { + my $res = {}; + foreach my $k (keys %$h) { + if (my $class = ref($h->{$k})) { + if ($class eq 'CODE') { + next if $k eq 'completion'; + } + $res->{$k} = cleanup_tree($h->{$k}); + } else { + $res->{$k} = $h->{$k}; + } + } + return $res; + } elsif ($class eq 'Regexp') { + return "$h"; # return string representation + } else { + die "unknown class '$class'\n"; + } +} + +my $tree = cleanup_tree(PVE::RESTHandler::api_dump('PVE::API2')); + +print "var pveapi = " . to_json($tree, {pretty => 1}) . ";\n\n"; + +exit(0); -- 2.39.2