mirror of
				git://git.proxmox.com/git/pve-docs.git
				synced 2025-10-30 00:23:44 +03:00 
			
		
		
		
	build api-viewer from widget-toolkit-dev
build-depends naturally on the new proxmox-widget-toolkit-dev package Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
		
				
					committed by
					
						 Thomas Lamprecht
						Thomas Lamprecht
					
				
			
			
				
	
			
			
			
						parent
						
							696ebb3c24
						
					
				
				
					commit
					37d44f8e21
				
			| @@ -1,481 +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 `<tr><td> </td></td><tr><td>CLI:</td><td>pvesh ${clicmdhash[method]} ${path}</td></tr></table>`; | ||||
| } | ||||
|  | ||||
| Ext.onReady(function() { | ||||
|  | ||||
|     Ext.define('pve-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('pve-updated-treestore', { | ||||
| 	extend: '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' | ||||
| 	}], | ||||
| 	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 += "<table><tr><td>HTTP:   </td><td>" + method + " /api2/json" + data.path + "</td></tr><tr><td> </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="groupValue">Optional</tpl><tpl if="!groupValue">Required</tpl>' | ||||
| 		    }); | ||||
|  | ||||
| 		    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: 'pve-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: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Obligatory</tpl>' | ||||
| 		    }); | ||||
| 		    var returnhtml; | ||||
| 		    if (retinf.items) { | ||||
| 			returnhtml = '<pre>items: ' + Ext.htmlEncode(JSON.stringify(retinf.items, null, 4)) + '</pre>'; | ||||
| 		    } | ||||
|  | ||||
| 		    if (retinf.properties) { | ||||
| 			returnhtml = returnhtml || ''; | ||||
| 			returnhtml += '<pre>properties:' + Ext.htmlEncode(JSON.stringify(retinf.properties, null, 4)) + '</pre>'; | ||||
| 		    } | ||||
|  | ||||
| 		    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 += "<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 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 += "<pre>Check: " + | ||||
| 			    Ext.htmlEncode(Ext.JSON.encode(info.permissions.check))  + "</pre>"; | ||||
| 		    } else { | ||||
| 			permhtml += "Unknown systax!"; | ||||
| 		    } | ||||
| 		} | ||||
| 		if (!info.allowtoken) { | ||||
| 		    permhtml += "<br />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(); | ||||
|  | ||||
| }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user