From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id A07857371B for ; Fri, 28 May 2021 14:15:44 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 9DF629927 for ; Fri, 28 May 2021 14:15:44 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id C079D991C for ; Fri, 28 May 2021 14:15:40 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 7CC6C42F58 for ; Fri, 28 May 2021 14:15:40 +0200 (CEST) From: Dominik Csapak To: pmg-devel@lists.proxmox.com Date: Fri, 28 May 2021 14:15:39 +0200 Message-Id: <20210528121539.27591-1-d.csapak@proxmox.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.042 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [5-opts.pl, metadata.style, extractapi.pl, pdef.name] Subject: [pmg-devel] [PATCH pmg-docs] build api-viewer from proxmox-widget-toolkit-dev X-BeenThere: pmg-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Mail Gateway development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 28 May 2021 12:15:44 -0000 build-depends on the new proxmox-widget-toolkit-dev package Signed-off-by: Dominik Csapak --- Makefile | 10 +- api-viewer/PMGAPI.js | 490 +------------------------------------------ debian/control | 1 + extractapi.pl | 2 +- 4 files changed, 19 insertions(+), 484 deletions(-) diff --git a/Makefile b/Makefile index 8b125df..e2b7d6f 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,11 @@ GEN_SCRIPTS= \ gen-pmg.conf.5-opts.pl \ gen-user.conf.5-opts.pl +API_VIEWER_FILES= \ + api-viewer/apidata.js \ + api-viewer/PMGAPI.js \ + /usr/share/javascript/proxmox-widget-toolkit-dev/APIVIEWER.js + API_VIEWER_SOURCES= \ api-viewer/index.html \ api-viewer/apidoc.js @@ -139,8 +144,9 @@ pmg-admin-guide.epub: ${PMG_ADMIN_GUIDE_ADOCDEPENDS} api-viewer/apidata.js: extractapi.pl ./extractapi.pl >$@ -api-viewer/apidoc.js: api-viewer/apidata.js api-viewer/PMGAPI.js - cat api-viewer/apidata.js api-viewer/PMGAPI.js >$@ +api-viewer/apidoc.js: ${API_VIEWER_FILES} + cat ${API_VIEWER_FILES} >$@.tmp + mv $@.tmp $@ .PHONY: dinstall dinstall: ${GEN_DEB} ${DOC_DEB} diff --git a/api-viewer/PMGAPI.js b/api-viewer/PMGAPI.js index 76fd77e..838b0de 100644 --- a/api-viewer/PMGAPI.js +++ b/api-viewer/PMGAPI.js @@ -1,482 +1,10 @@ -// avoid errors when running without development tools -if (!Ext.isDefined(Ext.global.console)) { - var console = { - dir: function() {}, - log: function() {} - }; +var clicmdhash = { + GET: 'get', + POST: 'create', + PUT: 'set', + DELETE: 'delete' +}; + +function cliusage(method, path) { + return ` CLI:pmgsh ${clicmdhash[method]} ${path}`; } - -Ext.onReady(function() { - - Ext.define('pmg-param-schema', { - extend: 'Ext.data.Model', - fields: [ - 'name', 'type', 'typetext', 'description', 'verbose_description', - 'enum', 'minimum', 'maximum', 'minLength', 'maxLength', - 'pattern', 'title', 'requires', 'format', 'default', - 'disallow', 'extends', 'links', - { - name: 'optional', - type: 'boolean' - } - ] - }); - - var store = Ext.define('pmg-updated-treestore', { - extend: 'Ext.data.TreeStore', - model: Ext.define('pmg-api-doc', { - extend: 'Ext.data.Model', - fields: [ - 'path', 'info', 'text', - ] - }), - proxy: { - type: 'memory', - data: pmgapi - }, - sorters: [{ - property: 'leaf', - direction: 'ASC' - }, { - property: 'text', - direction: 'ASC' - }], - filterer: 'bottomup', - doFilter: function(node) { - this.filterNodes(node, this.getFilters().getFilterFn(), true); - }, - - filterNodes: function(node, filterFn, parentVisible) { - var me = this, - bottomUpFiltering = me.filterer === 'bottomup', - match = filterFn(node) && parentVisible || (node.isRoot() && !me.getRootVisible()), - childNodes = node.childNodes, - len = childNodes && childNodes.length, i, matchingChildren; - - if (len) { - for (i = 0; i < len; ++i) { - matchingChildren = me.filterNodes(childNodes[i], filterFn, match || bottomUpFiltering) || matchingChildren; - } - if (bottomUpFiltering) { - match = matchingChildren || match; - } - } - - node.set("visible", match, me._silentOptions); - return match; - }, - - }).create(); - - var render_description = function(value, metaData, record) { - var pdef = record.data; - - value = pdef.verbose_description || value; - - // TODO: try to render asciidoc correctly - - 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 Ext.htmlEncode(pdef.typetext); - - if (pdef['enum']) - return pdef['enum'].join(' | '); - - if (pdef.format) - return pdef.format; - - if (pdef.pattern) - return Ext.htmlEncode(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:pmgsh " + 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: 'pmg-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', - flex: 1 - }, - { - header: 'Type', - dataIndex: 'type', - renderer: render_type, - flex: 1 - }, - { - header: 'Default', - dataIndex: 'default', - flex: 1 - }, - { - header: 'Format', - dataIndex: 'type', - renderer: render_format, - flex: 2 - }, - { - header: 'Description', - dataIndex: 'description', - renderer: render_description, - flex: 6 - } - ] - }); - - } - - if (info.returns) { - - var retinf = info.returns; - var rtype = retinf.type; - if (!rtype && retinf.items) - rtype = 'array'; - if (!rtype) - rtype = 'object'; - - var rpstore = Ext.create('Ext.data.Store', { - model: 'pmg-param-schema', - proxy: { - type: 'memory' - }, - groupField: 'optional', - sorters: [ - { - property: 'name', - direction: 'ASC' - } - ] - }); - - var properties; - if (rtype === 'array' && retinf.items.properties) { - properties = retinf.items.properties; - } - - if (rtype === 'object' && retinf.properties) { - properties = retinf.properties; - } - - Ext.Object.each(properties, function(name, pdef) { - pdef.name = name; - rpstore.add(pdef); - }); - - rpstore.sort(); - - var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{ - enableGroupingMenu: false, - groupHeaderTpl: 'OptionalObligatory' - }); - var returnhtml; - if (retinf.items) { - returnhtml = '
items: ' + Ext.htmlEncode(JSON.stringify(retinf.items, null, 4)) + '
'; - } - - if (retinf.properties) { - returnhtml = returnhtml || ''; - returnhtml += '
properties:' + Ext.htmlEncode(JSON.stringify(retinf.properties, null, 4)) + '
'; - } - - var rawSection = Ext.create('Ext.panel.Panel', { - bodyPadding: '0px 10px 10px 10px', - html: returnhtml, - hidden: true - }); - - sections.push({ - xtype: 'gridpanel', - title: 'Returns: ' + rtype, - features: [groupingFeature], - store: rpstore, - viewConfig: { - trackOver: false, - stripeRows: true - }, - columns: [ - { - header: 'Name', - dataIndex: 'name', - flex: 1 - }, - { - header: 'Type', - dataIndex: 'type', - renderer: render_type, - flex: 1 - }, - { - header: 'Default', - dataIndex: 'default', - flex: 1 - }, - { - header: 'Format', - dataIndex: 'type', - renderer: render_format, - flex: 2 - }, - { - header: 'Description', - dataIndex: 'description', - renderer: render_description, - flex: 6 - } - ], - bbar: [ - { - xtype: 'button', - text: 'Show RAW', - handler: function(btn) { - rawSection.setVisible(!rawSection.isVisible()); - btn.setText(rawSection.isVisible() ? 'Hide RAW' : 'Show RAW'); - }} - ] - }); - - sections.push(rawSection); - - - } - - 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 authentication."; - } else if (info.permissions.user === 'all') { - permhtml += "Accessible by all authenticated 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!"; - } - } - if (!info.allowtoken) { - // PMG doesn't fully supports API token, and probably won't ever!? - permhtml += "
This API endpoint is not available for API tokens." - } - - 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); - ct.setActiveTab(0); - }; - - Ext.define('Ext.form.SearchField', { - extend: 'Ext.form.field.Text', - alias: 'widget.searchfield', - - emptyText: 'Search...', - - flex: 1, - - inputType: 'search', - listeners: { - 'change': function(){ - - var value = this.getValue(); - if (!Ext.isEmpty(value)) { - store.filter({ - property: 'path', - value: value, - anyMatch: true - }); - } else { - store.clearFilter(); - } - } - } - }); - - var tree = Ext.create('Ext.tree.Panel', { - title: 'Resource Tree', - tbar: [ - { - xtype: 'searchfield', - } - ], - tools: [ - { - type: 'expand', - tooltip: 'Expand all', - tooltipType: 'title', - callback: (tree) => tree.expandAll(), - }, - { - type: 'collapse', - tooltip: 'Collapse all', - tooltipType: 'title', - callback: (tree) => tree.collapseAll(), - }, - ], - 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); - location.hash = '#' + rec.data.path; - } - } - }); - - 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: [] - } - ] - }); - - var deepLink = function() { - var path = window.location.hash.substring(1).replace(/\/\s*$/, '') - var endpoint = store.findNode('path', path); - - if (endpoint) { - tree.getSelectionModel().select(endpoint); - tree.expandPath(endpoint.getPath()); - render_docu(endpoint.data); - } - } - window.onhashchange = deepLink; - - deepLink(); - -}); diff --git a/debian/control b/debian/control index 8d2ccde..353f688 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,7 @@ Build-Depends: asciidoc-dblatex, librsvg2-bin, lintian, source-highlight, + proxmox-widget-toolkit-dev, Standards-Version: 3.8.4 Package: pmg-doc-generator diff --git a/extractapi.pl b/extractapi.pl index 283d8a0..85ab54f 100755 --- a/extractapi.pl +++ b/extractapi.pl @@ -42,6 +42,6 @@ sub cleanup_tree { my $tree = cleanup_tree(PVE::RESTHandler::api_dump('PMG::API2')); -print "var pmgapi = " . to_json($tree, {pretty => 1, canonical => 1}) . ";\n\n"; +print "var pmxapi = " . to_json($tree, {pretty => 1, canonical => 1}) . ";\n\n"; exit(0); -- 2.20.1