add api doc viewer (moved from package pve2-api-doc)
authorDietmar Maurer <dietmar@proxmox.com>
Fri, 29 Apr 2016 10:22:23 +0000 (12:22 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Fri, 29 Apr 2016 10:22:23 +0000 (12:22 +0200)
Makefile
PVEAPI.js [new file with mode: 0644]
doc-debian/pve-docs.conf
extractapi.pl [new file with mode: 0755]

index 840d1a2e7c647f000f62b3c67d35bf4a93cfb548..60c1d93b7a0e3825d731411e6d0e54fb5f5111d4 100644 (file)
--- 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) $@ &
 
        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}
 .PHONY: dinstall
 dinstall: ${GEN_DEB}
        dpkg -i ${GEN_DEB}
@@ -179,7 +185,7 @@ DOC_DEB_FILES=                                      \
        pve-admin-guide.epub    \
        index.html
 
        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
        $(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 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}
 
        cd build; dpkg-buildpackage -rfakeroot -b -us -uc
        lintian ${DOC_DEB}
 
@@ -223,5 +233,5 @@ update: clean
        make all
 
 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 (file)
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 += "<table><tr><td>HTTP:&nbsp;&nbsp;&nbsp;</td><td>" + method + " /api2/json" + data.path + "</td></tr><tr><td>&nbsp</td></tr>";
+               usage += "<tr><td>CLI:</td><td>pvesh " + clicmdhash[method] + " " + data.path + "</td></tr></table>";
+
+               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: '<tpl if="name">Optional</tpl><tpl if="!name">Required</tpl>'
+                   });
+
+                   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 += "<div style='white-space:pre-wrap;padding-bottom:10px;'>" +
+                           Ext.htmlEncode(info.permissions.description) + "</div>";
+                   }
+
+                   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 += "<pre>Check: " + 
+                           Ext.htmlEncode(Ext.JSON.encode(info.permissions.check))  + "</pre>";
+                   } 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: []
+           }
+       ]
+    });
+
+});
index 2b9b8e1d8cfca2985b0e65a4bdf3931b6ad2bf83..c092b9dc5941dd5c81081ea0202eed7f3ea9912c 100644 (file)
@@ -5,3 +5,6 @@
 </Directory>
 
 Alias /pve-docs /usr/share/pve-docs/
 </Directory>
 
 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 (executable)
index 0000000..0488ea9
--- /dev/null
@@ -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);