]>
git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/api-viewer/APIViewer.js
1 // avoid errors when running without development tools
2 if (!Ext
.isDefined(Ext
.global
.console
)) {
9 Ext
.onReady(function() {
11 Ext
.define('pmx-param-schema', {
12 extend
: 'Ext.data.Model',
14 'name', 'type', 'typetext', 'description', 'verbose_description',
15 'enum', 'minimum', 'maximum', 'minLength', 'maxLength',
16 'pattern', 'title', 'requires', 'format', 'default',
17 'disallow', 'extends', 'links',
25 var store
= Ext
.define('pmx-updated-treestore', {
26 extend
: 'Ext.data.TreeStore',
27 model
: Ext
.define('pmx-api-doc', {
28 extend
: 'Ext.data.Model',
30 'path', 'info', 'text',
45 doFilter: function(node
) {
46 this.filterNodes(node
, this.getFilters().getFilterFn(), true);
49 filterNodes: function(node
, filterFn
, parentVisible
) {
51 bottomUpFiltering
= me
.filterer
=== 'bottomup',
52 match
= filterFn(node
) && parentVisible
|| (node
.isRoot() && !me
.getRootVisible()),
53 childNodes
= node
.childNodes
,
54 len
= childNodes
&& childNodes
.length
, i
, matchingChildren
;
57 for (i
= 0; i
< len
; ++i
) {
58 matchingChildren
= me
.filterNodes(childNodes
[i
], filterFn
, match
|| bottomUpFiltering
) || matchingChildren
;
60 if (bottomUpFiltering
) {
61 match
= matchingChildren
|| match
;
65 node
.set("visible", match
, me
._silentOptions
);
71 var render_description = function(value
, metaData
, record
) {
72 var pdef
= record
.data
;
74 value
= pdef
.verbose_description
|| value
;
76 // TODO: try to render asciidoc correctly
78 metaData
.style
= 'white-space:pre-wrap;'
80 return Ext
.htmlEncode(value
);
83 var render_type = function(value
, metaData
, record
) {
84 var pdef
= record
.data
;
86 return pdef
['enum'] ? 'enum' : (pdef
.type
|| 'string');
89 let render_simple_format = function(pdef
, type_fallback
) {
94 return pdef
['enum'].join(' | ');
102 if (pdef
.type
=== 'boolean')
103 return `<true|false>`;
105 if (type_fallback
&& pdef
.type
)
106 return `<${pdef.type}>`;
111 let render_format = function(value
, metaData
, record
) {
112 let pdef
= record
.data
;
114 metaData
.style
= 'white-space:normal;'
116 if (pdef
.type
=== 'array' && pdef
.items
) {
117 let format
= render_simple_format(pdef
.items
, true);
118 return `[${Ext.htmlEncode(format)}, ...]`;
121 return Ext
.htmlEncode(render_simple_format(pdef
) || '');
124 var real_path = function(path
) {
125 return path
.replace(/^.*\/_upgrade_(\/)?/, "/");
128 var permission_text = function(permission
) {
131 if (permission
.user
) {
132 if (!permission
.description
) {
133 if (permission
.user
=== 'world') {
134 permhtml
+= "Accessible without any authentication.";
135 } else if (permission
.user
=== 'all') {
136 permhtml
+= "Accessible by all authenticated users.";
138 permhtml
+= 'Onyl accessible by user "' +
139 permission
.user
+ '"';
142 } else if (permission
.check
) {
143 permhtml
+= "<pre>Check: " +
144 Ext
.htmlEncode(Ext
.JSON
.encode(permission
.check
)) + "</pre>";
145 } else if (permission
.userParam
) {
146 permhtml
+= `<div>Check if user matches parameter '${permission.userParam}'`;
147 } else if (permission
.or
) {
148 permhtml
+= "<div>Or<div style='padding-left: 10px;'>";
149 Ext
.Array
.each(permission
.or
, function(sub_permission
) {
150 permhtml
+= permission_text(sub_permission
);
152 permhtml
+= "</div></div>";
153 } else if (permission
.and
) {
154 permhtml
+= "<div>And<div style='padding-left: 10px;'>";
155 Ext
.Array
.each(permission
.and
, function(sub_permission
) {
156 permhtml
+= permission_text(sub_permission
);
158 permhtml
+= "</div></div>";
160 //console.log(permission);
161 permhtml
+= "Unknown syntax!";
167 var render_docu = function(data
) {
170 // console.dir(data);
181 Ext
.Array
.each(['GET', 'POST', 'PUT', 'DELETE'], function(method
) {
182 var info
= md
[method
];
187 usage
+= "<table><tr><td>HTTP: </td><td>"
188 + method
+ " " + real_path("/api2/json" + data
.path
) + "</td></tr>";
190 if (typeof cliusage
=== 'function') {
191 usage
+= cliusage(method
, real_path(data
.path
));
196 title
: 'Description',
197 html
: Ext
.htmlEncode(info
.description
),
207 if (info
.parameters
&& info
.parameters
.properties
) {
209 var pstore
= Ext
.create('Ext.data.Store', {
210 model
: 'pmx-param-schema',
214 groupField
: 'optional',
223 Ext
.Object
.each(info
.parameters
.properties
, function(name
, pdef
) {
230 var groupingFeature
= Ext
.create('Ext.grid.feature.Grouping',{
231 enableGroupingMenu
: false,
232 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Required</tpl>'
238 features
: [groupingFeature
],
253 renderer
: render_type
,
258 dataIndex
: 'default',
264 renderer
: render_format
,
268 header
: 'Description',
269 dataIndex
: 'description',
270 renderer
: render_description
,
280 var retinf
= info
.returns
;
281 var rtype
= retinf
.type
;
282 if (!rtype
&& retinf
.items
)
287 var rpstore
= Ext
.create('Ext.data.Store', {
288 model
: 'pmx-param-schema',
292 groupField
: 'optional',
302 if (rtype
=== 'array' && retinf
.items
.properties
) {
303 properties
= retinf
.items
.properties
;
306 if (rtype
=== 'object' && retinf
.properties
) {
307 properties
= retinf
.properties
;
310 Ext
.Object
.each(properties
, function(name
, pdef
) {
317 var groupingFeature
= Ext
.create('Ext.grid.feature.Grouping',{
318 enableGroupingMenu
: false,
319 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Obligatory</tpl>'
323 returnhtml
= '<pre>items: ' + Ext
.htmlEncode(JSON
.stringify(retinf
.items
, null, 4)) + '</pre>';
326 if (retinf
.properties
) {
327 returnhtml
= returnhtml
|| '';
328 returnhtml
+= '<pre>properties:' + Ext
.htmlEncode(JSON
.stringify(retinf
.properties
, null, 4)) + '</pre>';
331 var rawSection
= Ext
.create('Ext.panel.Panel', {
332 bodyPadding
: '0px 10px 10px 10px',
339 title
: 'Returns: ' + rtype
,
340 features
: [groupingFeature
],
355 renderer
: render_type
,
360 dataIndex
: 'default',
366 renderer
: render_format
,
370 header
: 'Description',
371 dataIndex
: 'description',
372 renderer
: render_description
,
380 handler: function(btn
) {
381 rawSection
.setVisible(!rawSection
.isVisible());
382 btn
.setText(rawSection
.isVisible() ? 'Hide RAW' : 'Show RAW');
387 sections
.push(rawSection
);
392 if (!data
.path
.match(/\/_upgrade_/)) {
395 if (!info
.permissions
) {
396 permhtml
= "Root only.";
398 if (info
.permissions
.description
) {
399 permhtml
+= "<div style='white-space:pre-wrap;padding-bottom:10px;'>" +
400 Ext
.htmlEncode(info
.permissions
.description
) + "</div>";
402 permhtml
+= permission_text(info
.permissions
);
405 if (info
.allowtoken
!== undefined && !info
.allowtoken
) {
406 permhtml
+= "<br />This API endpoint is not available for API tokens."
410 title
: 'Required permissions',
427 var ct
= Ext
.getCmp('docview');
428 ct
.setTitle("Path: " + real_path(data
.path
));
434 Ext
.define('Ext.form.SearchField', {
435 extend
: 'Ext.form.field.Text',
436 alias
: 'widget.searchfield',
438 emptyText
: 'Search...',
444 'change': function(){
446 var value
= this.getValue();
447 if (!Ext
.isEmpty(value
)) {
460 var tree
= Ext
.create('Ext.tree.Panel', {
461 title
: 'Resource Tree',
464 xtype
: 'searchfield',
470 tooltip
: 'Expand all',
471 tooltipType
: 'title',
472 callback
: (tree
) => tree
.expandAll(),
476 tooltip
: 'Collapse all',
477 tooltipType
: 'title',
478 callback
: (tree
) => tree
.collapseAll(),
488 selectionchange: function(v
, selections
) {
491 var rec
= selections
[0];
492 render_docu(rec
.data
);
493 location
.hash
= '#' + rec
.data
.path
;
498 Ext
.create('Ext.container.Viewport', {
500 renderTo
: Ext
.getBody(),
505 title
: 'Documentation',
515 var deepLink = function() {
516 var path
= window
.location
.hash
.substring(1).replace(/\/\s*$/, '')
517 var endpoint
= store
.findNode('path', path
);
520 tree
.getSelectionModel().select(endpoint
);
521 tree
.expandPath(endpoint
.getPath());
522 render_docu(endpoint
.data
);
525 window
.onhashchange
= deepLink
;